Path: blob/master/drivers/media/video/gspca/spca561.c
17705 views
/*1* Sunplus spca561 subdriver2*3* Copyright (C) 2004 Michel Xhaard [email protected]4*5* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License as published by9* the Free Software Foundation; either version 2 of the License, or10* any later version.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA20*/2122#define MODULE_NAME "spca561"2324#include <linux/input.h>25#include "gspca.h"2627MODULE_AUTHOR("Michel Xhaard <[email protected]>");28MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver");29MODULE_LICENSE("GPL");3031/* specific webcam descriptor */32struct sd {33struct gspca_dev gspca_dev; /* !! must be the first item */3435__u16 exposure; /* rev12a only */36#define EXPOSURE_MIN 137#define EXPOSURE_DEF 700 /* == 10 fps */38#define EXPOSURE_MAX (2047 + 325) /* see setexposure */3940__u8 contrast; /* rev72a only */41#define CONTRAST_MIN 0x0042#define CONTRAST_DEF 0x2043#define CONTRAST_MAX 0x3f4445__u8 brightness; /* rev72a only */46#define BRIGHTNESS_MIN 047#define BRIGHTNESS_DEF 0x2048#define BRIGHTNESS_MAX 0x3f4950__u8 white;51#define HUE_MIN 152#define HUE_DEF 0x4053#define HUE_MAX 0x7f5455__u8 autogain;56#define AUTOGAIN_MIN 057#define AUTOGAIN_DEF 158#define AUTOGAIN_MAX 15960__u8 gain; /* rev12a only */61#define GAIN_MIN 062#define GAIN_DEF 6363#define GAIN_MAX 2556465#define EXPO12A_DEF 366__u8 expo12a; /* expo/gain? for rev 12a */6768__u8 chip_revision;69#define Rev012A 070#define Rev072A 17172signed char ag_cnt;73#define AG_CNT_START 1374};7576static const struct v4l2_pix_format sif_012a_mode[] = {77{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,78.bytesperline = 160,79.sizeimage = 160 * 120,80.colorspace = V4L2_COLORSPACE_SRGB,81.priv = 3},82{176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,83.bytesperline = 176,84.sizeimage = 176 * 144,85.colorspace = V4L2_COLORSPACE_SRGB,86.priv = 2},87{320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,88.bytesperline = 320,89.sizeimage = 320 * 240 * 4 / 8,90.colorspace = V4L2_COLORSPACE_SRGB,91.priv = 1},92{352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,93.bytesperline = 352,94.sizeimage = 352 * 288 * 4 / 8,95.colorspace = V4L2_COLORSPACE_SRGB,96.priv = 0},97};9899static const struct v4l2_pix_format sif_072a_mode[] = {100{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,101.bytesperline = 160,102.sizeimage = 160 * 120,103.colorspace = V4L2_COLORSPACE_SRGB,104.priv = 3},105{176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,106.bytesperline = 176,107.sizeimage = 176 * 144,108.colorspace = V4L2_COLORSPACE_SRGB,109.priv = 2},110{320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,111.bytesperline = 320,112.sizeimage = 320 * 240,113.colorspace = V4L2_COLORSPACE_SRGB,114.priv = 1},115{352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,116.bytesperline = 352,117.sizeimage = 352 * 288,118.colorspace = V4L2_COLORSPACE_SRGB,119.priv = 0},120};121122/*123* Initialization data124* I'm not very sure how to split initialization from open data125* chunks. For now, we'll consider everything as initialization126*/127/* Frame packet header offsets for the spca561 */128#define SPCA561_OFFSET_SNAP 1129#define SPCA561_OFFSET_TYPE 2130#define SPCA561_OFFSET_COMPRESS 3131#define SPCA561_OFFSET_FRAMSEQ 4132#define SPCA561_OFFSET_GPIO 5133#define SPCA561_OFFSET_USBBUFF 6134#define SPCA561_OFFSET_WIN2GRAVE 7135#define SPCA561_OFFSET_WIN2RAVE 8136#define SPCA561_OFFSET_WIN2BAVE 9137#define SPCA561_OFFSET_WIN2GBAVE 10138#define SPCA561_OFFSET_WIN1GRAVE 11139#define SPCA561_OFFSET_WIN1RAVE 12140#define SPCA561_OFFSET_WIN1BAVE 13141#define SPCA561_OFFSET_WIN1GBAVE 14142#define SPCA561_OFFSET_FREQ 15143#define SPCA561_OFFSET_VSYNC 16144#define SPCA561_INDEX_I2C_BASE 0x8800145#define SPCA561_SNAPBIT 0x20146#define SPCA561_SNAPCTRL 0x40147148static const u16 rev72a_reset[][2] = {149{0x0000, 0x8114}, /* Software GPIO output data */150{0x0001, 0x8114}, /* Software GPIO output data */151{0x0000, 0x8112}, /* Some kind of reset */152{}153};154static const __u16 rev72a_init_data1[][2] = {155{0x0003, 0x8701}, /* PCLK clock delay adjustment */156{0x0001, 0x8703}, /* HSYNC from cmos inverted */157{0x0011, 0x8118}, /* Enable and conf sensor */158{0x0001, 0x8118}, /* Conf sensor */159{0x0092, 0x8804}, /* I know nothing about these */160{0x0010, 0x8802}, /* 0x88xx registers, so I won't */161{}162};163static const u16 rev72a_init_sensor1[][2] = {164{0x0001, 0x000d},165{0x0002, 0x0018},166{0x0004, 0x0165},167{0x0005, 0x0021},168{0x0007, 0x00aa},169{0x0020, 0x1504},170{0x0039, 0x0002},171{0x0035, 0x0010},172{0x0009, 0x1049},173{0x0028, 0x000b},174{0x003b, 0x000f},175{0x003c, 0x0000},176{}177};178static const __u16 rev72a_init_data2[][2] = {179{0x0018, 0x8601}, /* Pixel/line selection for color separation */180{0x0000, 0x8602}, /* Optical black level for user setting */181{0x0060, 0x8604}, /* Optical black horizontal offset */182{0x0002, 0x8605}, /* Optical black vertical offset */183{0x0000, 0x8603}, /* Non-automatic optical black level */184{0x0002, 0x865b}, /* Horizontal offset for valid pixels */185{0x0000, 0x865f}, /* Vertical valid pixels window (x2) */186{0x00b0, 0x865d}, /* Horizontal valid pixels window (x2) */187{0x0090, 0x865e}, /* Vertical valid lines window (x2) */188{0x00e0, 0x8406}, /* Memory buffer threshold */189{0x0000, 0x8660}, /* Compensation memory stuff */190{0x0002, 0x8201}, /* Output address for r/w serial EEPROM */191{0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */192{0x0001, 0x8200}, /* OprMode to be executed by hardware */193/* from ms-win */194{0x0000, 0x8611}, /* R offset for white balance */195{0x00fd, 0x8612}, /* Gr offset for white balance */196{0x0003, 0x8613}, /* B offset for white balance */197{0x0000, 0x8614}, /* Gb offset for white balance */198/* from ms-win */199{0x0035, 0x8651}, /* R gain for white balance */200{0x0040, 0x8652}, /* Gr gain for white balance */201{0x005f, 0x8653}, /* B gain for white balance */202{0x0040, 0x8654}, /* Gb gain for white balance */203{0x0002, 0x8502}, /* Maximum average bit rate stuff */204{0x0011, 0x8802},205206{0x0087, 0x8700}, /* Set master clock (96Mhz????) */207{0x0081, 0x8702}, /* Master clock output enable */208209{0x0000, 0x8500}, /* Set image type (352x288 no compression) */210/* Originally was 0x0010 (352x288 compression) */211212{0x0002, 0x865b}, /* Horizontal offset for valid pixels */213{0x0003, 0x865c}, /* Vertical offset for valid lines */214{}215};216static const u16 rev72a_init_sensor2[][2] = {217{0x0003, 0x0121},218{0x0004, 0x0165},219{0x0005, 0x002f}, /* blanking control column */220{0x0006, 0x0000}, /* blanking mode row*/221{0x000a, 0x0002},222{0x0009, 0x1061}, /* setexposure times && pixel clock223* 0001 0 | 000 0110 0001 */224{0x0035, 0x0014},225{}226};227228/******************** QC Express etch2 stuff ********************/229static const __u16 Pb100_1map8300[][2] = {230/* reg, value */231{0x8320, 0x3304},232233{0x8303, 0x0125}, /* image area */234{0x8304, 0x0169},235{0x8328, 0x000b},236{0x833c, 0x0001}, /*fixme: win:07*/237238{0x832f, 0x1904}, /*fixme: was 0419*/239{0x8307, 0x00aa},240{0x8301, 0x0003},241{0x8302, 0x000e},242{}243};244static const __u16 Pb100_2map8300[][2] = {245/* reg, value */246{0x8339, 0x0000},247{0x8307, 0x00aa},248{}249};250251static const __u16 spca561_161rev12A_data1[][2] = {252{0x29, 0x8118}, /* Control register (various enable bits) */253{0x08, 0x8114}, /* GPIO: Led off */254{0x0e, 0x8112}, /* 0x0e stream off 0x3e stream on */255{0x00, 0x8102}, /* white balance - new */256{0x92, 0x8804},257{0x04, 0x8802}, /* windows uses 08 */258{}259};260static const __u16 spca561_161rev12A_data2[][2] = {261{0x21, 0x8118},262{0x10, 0x8500},263{0x07, 0x8601},264{0x07, 0x8602},265{0x04, 0x8501},266267{0x07, 0x8201}, /* windows uses 02 */268{0x08, 0x8200},269{0x01, 0x8200},270271{0x90, 0x8604},272{0x00, 0x8605},273{0xb0, 0x8603},274275/* sensor gains */276{0x07, 0x8601}, /* white balance - new */277{0x07, 0x8602}, /* white balance - new */278{0x00, 0x8610}, /* *red */279{0x00, 0x8611}, /* 3f *green */280{0x00, 0x8612}, /* green *blue */281{0x00, 0x8613}, /* blue *green */282{0x43, 0x8614}, /* green *red - white balance - was 0x35 */283{0x40, 0x8615}, /* 40 *green - white balance - was 0x35 */284{0x71, 0x8616}, /* 7a *blue - white balance - was 0x35 */285{0x40, 0x8617}, /* 40 *green - white balance - was 0x35 */286287{0x0c, 0x8620}, /* 0c */288{0xc8, 0x8631}, /* c8 */289{0xc8, 0x8634}, /* c8 */290{0x23, 0x8635}, /* 23 */291{0x1f, 0x8636}, /* 1f */292{0xdd, 0x8637}, /* dd */293{0xe1, 0x8638}, /* e1 */294{0x1d, 0x8639}, /* 1d */295{0x21, 0x863a}, /* 21 */296{0xe3, 0x863b}, /* e3 */297{0xdf, 0x863c}, /* df */298{0xf0, 0x8505},299{0x32, 0x850a},300/* {0x99, 0x8700}, * - white balance - new (removed) */301/* HDG we used to do this in stop0, making the init state and the state302after a start / stop different, so do this here instead. */303{0x29, 0x8118},304{}305};306307static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)308{309int ret;310311ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),3120, /* request */313USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,314value, index, NULL, 0, 500);315PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);316if (ret < 0)317err("reg write: error %d", ret);318}319320static void write_vector(struct gspca_dev *gspca_dev,321const __u16 data[][2])322{323struct usb_device *dev = gspca_dev->dev;324int i;325326i = 0;327while (data[i][1] != 0) {328reg_w_val(dev, data[i][1], data[i][0]);329i++;330}331}332333/* read 'len' bytes to gspca_dev->usb_buf */334static void reg_r(struct gspca_dev *gspca_dev,335__u16 index, __u16 length)336{337usb_control_msg(gspca_dev->dev,338usb_rcvctrlpipe(gspca_dev->dev, 0),3390, /* request */340USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,3410, /* value */342index, gspca_dev->usb_buf, length, 500);343}344345/* write 'len' bytes from gspca_dev->usb_buf */346static void reg_w_buf(struct gspca_dev *gspca_dev,347__u16 index, __u16 len)348{349usb_control_msg(gspca_dev->dev,350usb_sndctrlpipe(gspca_dev->dev, 0),3510, /* request */352USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,3530, /* value */354index, gspca_dev->usb_buf, len, 500);355}356357static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg)358{359int retry = 60;360361reg_w_val(gspca_dev->dev, 0x8801, reg);362reg_w_val(gspca_dev->dev, 0x8805, value);363reg_w_val(gspca_dev->dev, 0x8800, value >> 8);364do {365reg_r(gspca_dev, 0x8803, 1);366if (!gspca_dev->usb_buf[0])367return;368msleep(10);369} while (--retry);370}371372static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)373{374int retry = 60;375__u8 value;376377reg_w_val(gspca_dev->dev, 0x8804, 0x92);378reg_w_val(gspca_dev->dev, 0x8801, reg);379reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01);380do {381reg_r(gspca_dev, 0x8803, 1);382if (!gspca_dev->usb_buf[0]) {383reg_r(gspca_dev, 0x8800, 1);384value = gspca_dev->usb_buf[0];385reg_r(gspca_dev, 0x8805, 1);386return ((int) value << 8) | gspca_dev->usb_buf[0];387}388msleep(10);389} while (--retry);390return -1;391}392393static void sensor_mapwrite(struct gspca_dev *gspca_dev,394const __u16 (*sensormap)[2])395{396while ((*sensormap)[0]) {397gspca_dev->usb_buf[0] = (*sensormap)[1];398gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8;399reg_w_buf(gspca_dev, (*sensormap)[0], 2);400sensormap++;401}402}403404static void write_sensor_72a(struct gspca_dev *gspca_dev,405const __u16 (*sensor)[2])406{407while ((*sensor)[0]) {408i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]);409sensor++;410}411}412413static void init_161rev12A(struct gspca_dev *gspca_dev)414{415write_vector(gspca_dev, spca561_161rev12A_data1);416sensor_mapwrite(gspca_dev, Pb100_1map8300);417/*fixme: should be in sd_start*/418write_vector(gspca_dev, spca561_161rev12A_data2);419sensor_mapwrite(gspca_dev, Pb100_2map8300);420}421422/* this function is called at probe time */423static int sd_config(struct gspca_dev *gspca_dev,424const struct usb_device_id *id)425{426struct sd *sd = (struct sd *) gspca_dev;427struct cam *cam;428__u16 vendor, product;429__u8 data1, data2;430431/* Read frm global register the USB product and vendor IDs, just to432* prove that we can communicate with the device. This works, which433* confirms at we are communicating properly and that the device434* is a 561. */435reg_r(gspca_dev, 0x8104, 1);436data1 = gspca_dev->usb_buf[0];437reg_r(gspca_dev, 0x8105, 1);438data2 = gspca_dev->usb_buf[0];439vendor = (data2 << 8) | data1;440reg_r(gspca_dev, 0x8106, 1);441data1 = gspca_dev->usb_buf[0];442reg_r(gspca_dev, 0x8107, 1);443data2 = gspca_dev->usb_buf[0];444product = (data2 << 8) | data1;445if (vendor != id->idVendor || product != id->idProduct) {446PDEBUG(D_PROBE, "Bad vendor / product from device");447return -EINVAL;448}449450cam = &gspca_dev->cam;451gspca_dev->nbalt = 7 + 1; /* choose alternate 7 first */452453sd->chip_revision = id->driver_info;454if (sd->chip_revision == Rev012A) {455cam->cam_mode = sif_012a_mode;456cam->nmodes = ARRAY_SIZE(sif_012a_mode);457} else {458cam->cam_mode = sif_072a_mode;459cam->nmodes = ARRAY_SIZE(sif_072a_mode);460}461sd->brightness = BRIGHTNESS_DEF;462sd->contrast = CONTRAST_DEF;463sd->white = HUE_DEF;464sd->exposure = EXPOSURE_DEF;465sd->autogain = AUTOGAIN_DEF;466sd->gain = GAIN_DEF;467sd->expo12a = EXPO12A_DEF;468return 0;469}470471/* this function is called at probe and resume time */472static int sd_init_12a(struct gspca_dev *gspca_dev)473{474PDEBUG(D_STREAM, "Chip revision: 012a");475init_161rev12A(gspca_dev);476return 0;477}478static int sd_init_72a(struct gspca_dev *gspca_dev)479{480PDEBUG(D_STREAM, "Chip revision: 072a");481write_vector(gspca_dev, rev72a_reset);482msleep(200);483write_vector(gspca_dev, rev72a_init_data1);484write_sensor_72a(gspca_dev, rev72a_init_sensor1);485write_vector(gspca_dev, rev72a_init_data2);486write_sensor_72a(gspca_dev, rev72a_init_sensor2);487reg_w_val(gspca_dev->dev, 0x8112, 0x30);488return 0;489}490491/* rev 72a only */492static void setbrightness(struct gspca_dev *gspca_dev)493{494struct sd *sd = (struct sd *) gspca_dev;495struct usb_device *dev = gspca_dev->dev;496__u8 value;497498value = sd->brightness;499500/* offsets for white balance */501reg_w_val(dev, 0x8611, value); /* R */502reg_w_val(dev, 0x8612, value); /* Gr */503reg_w_val(dev, 0x8613, value); /* B */504reg_w_val(dev, 0x8614, value); /* Gb */505}506507static void setwhite(struct gspca_dev *gspca_dev)508{509struct sd *sd = (struct sd *) gspca_dev;510__u16 white;511__u8 blue, red;512__u16 reg;513514/* try to emulate MS-win as possible */515white = sd->white;516red = 0x20 + white * 3 / 8;517blue = 0x90 - white * 5 / 8;518if (sd->chip_revision == Rev012A) {519reg = 0x8614;520} else {521reg = 0x8651;522red += sd->contrast - 0x20;523blue += sd->contrast - 0x20;524}525reg_w_val(gspca_dev->dev, reg, red);526reg_w_val(gspca_dev->dev, reg + 2, blue);527}528529static void setcontrast(struct gspca_dev *gspca_dev)530{531struct sd *sd = (struct sd *) gspca_dev;532struct usb_device *dev = gspca_dev->dev;533__u8 value;534535if (sd->chip_revision != Rev072A)536return;537value = sd->contrast + 0x20;538539/* gains for white balance */540setwhite(gspca_dev);541/* reg_w_val(dev, 0x8651, value); * R - done by setwhite */542reg_w_val(dev, 0x8652, value); /* Gr */543/* reg_w_val(dev, 0x8653, value); * B - done by setwhite */544reg_w_val(dev, 0x8654, value); /* Gb */545}546547/* rev 12a only */548static void setexposure(struct gspca_dev *gspca_dev)549{550struct sd *sd = (struct sd *) gspca_dev;551int i, expo = 0;552553/* Register 0x8309 controls exposure for the spca561,554the basic exposure setting goes from 1-2047, where 1 is completely555dark and 2047 is very bright. It not only influences exposure but556also the framerate (to allow for longer exposure) from 1 - 300 it557only raises the exposure time then from 300 - 600 it halves the558framerate to be able to further raise the exposure time and for every559300 more it halves the framerate again. This allows for a maximum560exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps).561Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12562configure a divider for the base framerate which us used at the563exposure setting of 1-300. These bits configure the base framerate564according to the following formula: fps = 60 / (value + 2) */565566/* We choose to use the high bits setting the fixed framerate divisor567asap, as setting high basic exposure setting without the fixed568divider in combination with high gains makes the cam stop */569int table[] = { 0, 450, 550, 625, EXPOSURE_MAX };570571for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {572if (sd->exposure <= table[i + 1]) {573expo = sd->exposure - table[i];574if (i)575expo += 300;576expo |= i << 11;577break;578}579}580581gspca_dev->usb_buf[0] = expo;582gspca_dev->usb_buf[1] = expo >> 8;583reg_w_buf(gspca_dev, 0x8309, 2);584}585586/* rev 12a only */587static void setgain(struct gspca_dev *gspca_dev)588{589struct sd *sd = (struct sd *) gspca_dev;590591/* gain reg low 6 bits 0-63 gain, bit 6 and 7, both double the592sensitivity when set, so 31 + one of them set == 63, and 15593with both of them set == 63 */594if (sd->gain < 64)595gspca_dev->usb_buf[0] = sd->gain;596else if (sd->gain < 128)597gspca_dev->usb_buf[0] = (sd->gain / 2) | 0x40;598else599gspca_dev->usb_buf[0] = (sd->gain / 4) | 0xc0;600601gspca_dev->usb_buf[1] = 0;602reg_w_buf(gspca_dev, 0x8335, 2);603}604605static void setautogain(struct gspca_dev *gspca_dev)606{607struct sd *sd = (struct sd *) gspca_dev;608609if (sd->autogain)610sd->ag_cnt = AG_CNT_START;611else612sd->ag_cnt = -1;613}614615static int sd_start_12a(struct gspca_dev *gspca_dev)616{617struct usb_device *dev = gspca_dev->dev;618int mode;619static const __u8 Reg8391[8] =620{0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00};621622mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;623if (mode <= 1) {624/* Use compression on 320x240 and above */625reg_w_val(dev, 0x8500, 0x10 | mode);626} else {627/* I couldn't get the compression to work below 320x240628* Fortunately at these resolutions the bandwidth629* is sufficient to push raw frames at ~20fps */630reg_w_val(dev, 0x8500, mode);631} /* -- [email protected] */632633gspca_dev->usb_buf[0] = 0xaa;634gspca_dev->usb_buf[1] = 0x00;635reg_w_buf(gspca_dev, 0x8307, 2);636/* clock - lower 0x8X values lead to fps > 30 */637reg_w_val(gspca_dev->dev, 0x8700, 0x8a);638/* 0x8f 0x85 0x27 clock */639reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20);640reg_w_val(gspca_dev->dev, 0x850b, 0x03);641memcpy(gspca_dev->usb_buf, Reg8391, 8);642reg_w_buf(gspca_dev, 0x8391, 8);643reg_w_buf(gspca_dev, 0x8390, 8);644setwhite(gspca_dev);645setgain(gspca_dev);646setexposure(gspca_dev);647648/* Led ON (bit 3 -> 0 */649reg_w_val(gspca_dev->dev, 0x8114, 0x00);650return 0;651}652static int sd_start_72a(struct gspca_dev *gspca_dev)653{654struct usb_device *dev = gspca_dev->dev;655int Clck;656int mode;657658write_vector(gspca_dev, rev72a_reset);659msleep(200);660write_vector(gspca_dev, rev72a_init_data1);661write_sensor_72a(gspca_dev, rev72a_init_sensor1);662663mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;664switch (mode) {665default:666case 0:667Clck = 0x27; /* ms-win 0x87 */668break;669case 1:670Clck = 0x25;671break;672case 2:673Clck = 0x22;674break;675case 3:676Clck = 0x21;677break;678}679reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */680reg_w_val(dev, 0x8702, 0x81);681reg_w_val(dev, 0x8500, mode); /* mode */682write_sensor_72a(gspca_dev, rev72a_init_sensor2);683setcontrast(gspca_dev);684/* setbrightness(gspca_dev); * fixme: bad values */685setautogain(gspca_dev);686reg_w_val(dev, 0x8112, 0x10 | 0x20);687return 0;688}689690static void sd_stopN(struct gspca_dev *gspca_dev)691{692struct sd *sd = (struct sd *) gspca_dev;693694if (sd->chip_revision == Rev012A) {695reg_w_val(gspca_dev->dev, 0x8112, 0x0e);696/* Led Off (bit 3 -> 1 */697reg_w_val(gspca_dev->dev, 0x8114, 0x08);698} else {699reg_w_val(gspca_dev->dev, 0x8112, 0x20);700/* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */701}702}703704static void do_autogain(struct gspca_dev *gspca_dev)705{706struct sd *sd = (struct sd *) gspca_dev;707int expotimes;708int pixelclk;709int gainG;710__u8 R, Gr, Gb, B;711int y;712__u8 luma_mean = 110;713__u8 luma_delta = 20;714__u8 spring = 4;715716if (sd->ag_cnt < 0)717return;718if (--sd->ag_cnt >= 0)719return;720sd->ag_cnt = AG_CNT_START;721722switch (sd->chip_revision) {723case Rev072A:724reg_r(gspca_dev, 0x8621, 1);725Gr = gspca_dev->usb_buf[0];726reg_r(gspca_dev, 0x8622, 1);727R = gspca_dev->usb_buf[0];728reg_r(gspca_dev, 0x8623, 1);729B = gspca_dev->usb_buf[0];730reg_r(gspca_dev, 0x8624, 1);731Gb = gspca_dev->usb_buf[0];732y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8;733/* u= (128*B-(43*(Gr+Gb+R))) >> 8; */734/* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */735/* PDEBUG(D_CONF,"reading Y %d U %d V %d ",y,u,v); */736737if (y < luma_mean - luma_delta ||738y > luma_mean + luma_delta) {739expotimes = i2c_read(gspca_dev, 0x09, 0x10);740pixelclk = 0x0800;741expotimes = expotimes & 0x07ff;742/* PDEBUG(D_PACK,743"Exposition Times 0x%03X Clock 0x%04X ",744expotimes,pixelclk); */745gainG = i2c_read(gspca_dev, 0x35, 0x10);746/* PDEBUG(D_PACK,747"reading Gain register %d", gainG); */748749expotimes += (luma_mean - y) >> spring;750gainG += (luma_mean - y) / 50;751/* PDEBUG(D_PACK,752"compute expotimes %d gain %d",753expotimes,gainG); */754755if (gainG > 0x3f)756gainG = 0x3f;757else if (gainG < 3)758gainG = 3;759i2c_write(gspca_dev, gainG, 0x35);760761if (expotimes > 0x0256)762expotimes = 0x0256;763else if (expotimes < 3)764expotimes = 3;765i2c_write(gspca_dev, expotimes | pixelclk, 0x09);766}767break;768}769}770771static void sd_pkt_scan(struct gspca_dev *gspca_dev,772u8 *data, /* isoc packet */773int len) /* iso packet length */774{775struct sd *sd = (struct sd *) gspca_dev;776777len--;778switch (*data++) { /* sequence number */779case 0: /* start of frame */780gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);781782/* This should never happen */783if (len < 2) {784PDEBUG(D_ERR, "Short SOF packet, ignoring");785gspca_dev->last_packet_type = DISCARD_PACKET;786return;787}788789#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)790if (data[0] & 0x20) {791input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);792input_sync(gspca_dev->input_dev);793input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);794input_sync(gspca_dev->input_dev);795}796#endif797798if (data[1] & 0x10) {799/* compressed bayer */800gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);801} else {802/* raw bayer (with a header, which we skip) */803if (sd->chip_revision == Rev012A) {804data += 20;805len -= 20;806} else {807data += 16;808len -= 16;809}810gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);811}812return;813case 0xff: /* drop (empty mpackets) */814return;815}816gspca_frame_add(gspca_dev, INTER_PACKET, data, len);817}818819/* rev 72a only */820static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)821{822struct sd *sd = (struct sd *) gspca_dev;823824sd->brightness = val;825if (gspca_dev->streaming)826setbrightness(gspca_dev);827return 0;828}829830static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)831{832struct sd *sd = (struct sd *) gspca_dev;833834*val = sd->brightness;835return 0;836}837838/* rev 72a only */839static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)840{841struct sd *sd = (struct sd *) gspca_dev;842843sd->contrast = val;844if (gspca_dev->streaming)845setcontrast(gspca_dev);846return 0;847}848849static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)850{851struct sd *sd = (struct sd *) gspca_dev;852853*val = sd->contrast;854return 0;855}856857static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)858{859struct sd *sd = (struct sd *) gspca_dev;860861sd->autogain = val;862if (gspca_dev->streaming)863setautogain(gspca_dev);864return 0;865}866867static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)868{869struct sd *sd = (struct sd *) gspca_dev;870871*val = sd->autogain;872return 0;873}874875static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val)876{877struct sd *sd = (struct sd *) gspca_dev;878879sd->white = val;880if (gspca_dev->streaming)881setwhite(gspca_dev);882return 0;883}884885static int sd_getwhite(struct gspca_dev *gspca_dev, __s32 *val)886{887struct sd *sd = (struct sd *) gspca_dev;888889*val = sd->white;890return 0;891}892893/* rev12a only */894static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)895{896struct sd *sd = (struct sd *) gspca_dev;897898sd->exposure = val;899if (gspca_dev->streaming)900setexposure(gspca_dev);901return 0;902}903904static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)905{906struct sd *sd = (struct sd *) gspca_dev;907908*val = sd->exposure;909return 0;910}911912/* rev12a only */913static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)914{915struct sd *sd = (struct sd *) gspca_dev;916917sd->gain = val;918if (gspca_dev->streaming)919setgain(gspca_dev);920return 0;921}922923static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)924{925struct sd *sd = (struct sd *) gspca_dev;926927*val = sd->gain;928return 0;929}930931/* control tables */932static const struct ctrl sd_ctrls_12a[] = {933{934{935.id = V4L2_CID_HUE,936.type = V4L2_CTRL_TYPE_INTEGER,937.name = "Hue",938.minimum = HUE_MIN,939.maximum = HUE_MAX,940.step = 1,941.default_value = HUE_DEF,942},943.set = sd_setwhite,944.get = sd_getwhite,945},946{947{948.id = V4L2_CID_EXPOSURE,949.type = V4L2_CTRL_TYPE_INTEGER,950.name = "Exposure",951.minimum = EXPOSURE_MIN,952.maximum = EXPOSURE_MAX,953.step = 1,954.default_value = EXPOSURE_DEF,955},956.set = sd_setexposure,957.get = sd_getexposure,958},959{960{961.id = V4L2_CID_GAIN,962.type = V4L2_CTRL_TYPE_INTEGER,963.name = "Gain",964.minimum = GAIN_MIN,965.maximum = GAIN_MAX,966.step = 1,967.default_value = GAIN_DEF,968},969.set = sd_setgain,970.get = sd_getgain,971},972};973974static const struct ctrl sd_ctrls_72a[] = {975{976{977.id = V4L2_CID_HUE,978.type = V4L2_CTRL_TYPE_INTEGER,979.name = "Hue",980.minimum = HUE_MIN,981.maximum = HUE_MAX,982.step = 1,983.default_value = HUE_DEF,984},985.set = sd_setwhite,986.get = sd_getwhite,987},988{989{990.id = V4L2_CID_BRIGHTNESS,991.type = V4L2_CTRL_TYPE_INTEGER,992.name = "Brightness",993.minimum = BRIGHTNESS_MIN,994.maximum = BRIGHTNESS_MAX,995.step = 1,996.default_value = BRIGHTNESS_DEF,997},998.set = sd_setbrightness,999.get = sd_getbrightness,1000},1001{1002{1003.id = V4L2_CID_CONTRAST,1004.type = V4L2_CTRL_TYPE_INTEGER,1005.name = "Contrast",1006.minimum = CONTRAST_MIN,1007.maximum = CONTRAST_MAX,1008.step = 1,1009.default_value = CONTRAST_DEF,1010},1011.set = sd_setcontrast,1012.get = sd_getcontrast,1013},1014{1015{1016.id = V4L2_CID_AUTOGAIN,1017.type = V4L2_CTRL_TYPE_BOOLEAN,1018.name = "Auto Gain",1019.minimum = AUTOGAIN_MIN,1020.maximum = AUTOGAIN_MAX,1021.step = 1,1022.default_value = AUTOGAIN_DEF,1023},1024.set = sd_setautogain,1025.get = sd_getautogain,1026},1027};10281029/* sub-driver description */1030static const struct sd_desc sd_desc_12a = {1031.name = MODULE_NAME,1032.ctrls = sd_ctrls_12a,1033.nctrls = ARRAY_SIZE(sd_ctrls_12a),1034.config = sd_config,1035.init = sd_init_12a,1036.start = sd_start_12a,1037.stopN = sd_stopN,1038.pkt_scan = sd_pkt_scan,1039#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)1040.other_input = 1,1041#endif1042};1043static const struct sd_desc sd_desc_72a = {1044.name = MODULE_NAME,1045.ctrls = sd_ctrls_72a,1046.nctrls = ARRAY_SIZE(sd_ctrls_72a),1047.config = sd_config,1048.init = sd_init_72a,1049.start = sd_start_72a,1050.stopN = sd_stopN,1051.pkt_scan = sd_pkt_scan,1052.dq_callback = do_autogain,1053#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)1054.other_input = 1,1055#endif1056};1057static const struct sd_desc *sd_desc[2] = {1058&sd_desc_12a,1059&sd_desc_72a1060};10611062/* -- module initialisation -- */1063static const struct usb_device_id device_table[] = {1064{USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A},1065{USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A},1066{USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A},1067{USB_DEVICE(0x0461, 0x0815), .driver_info = Rev072A},1068{USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A},1069{USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A},1070{USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A},1071{USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A},1072{USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A},1073{USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A},1074{USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A},1075{USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A},1076{USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A},1077{USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A},1078{USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A},1079{USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A},1080{}1081};10821083MODULE_DEVICE_TABLE(usb, device_table);10841085/* -- device connect -- */1086static int sd_probe(struct usb_interface *intf,1087const struct usb_device_id *id)1088{1089return gspca_dev_probe(intf, id,1090sd_desc[id->driver_info],1091sizeof(struct sd),1092THIS_MODULE);1093}10941095static struct usb_driver sd_driver = {1096.name = MODULE_NAME,1097.id_table = device_table,1098.probe = sd_probe,1099.disconnect = gspca_disconnect,1100#ifdef CONFIG_PM1101.suspend = gspca_suspend,1102.resume = gspca_resume,1103#endif1104};11051106/* -- module insert / remove -- */1107static int __init sd_mod_init(void)1108{1109return usb_register(&sd_driver);1110}1111static void __exit sd_mod_exit(void)1112{1113usb_deregister(&sd_driver);1114}11151116module_init(sd_mod_init);1117module_exit(sd_mod_exit);111811191120