Path: blob/master/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
17988 views
/*1* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher2* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland3* Copyright (c) 2002, 2003 Tuukka Toivonen4* Copyright (c) 2008 Erik Andrén5*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* (at your option) 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*20* P/N 861037: Sensor HDCS1000 ASIC STV060021* P/N 861050-0010: Sensor HDCS1000 ASIC STV060022* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express23* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam24* P/N 861075-0040: Sensor HDCS1000 ASIC25* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB26* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web27*/2829/*30* The spec file for the PB-0100 suggests the following for best quality31* images after the sensor has been reset :32*33* PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC34to produce good black level35* PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes36through R5337* PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for38auto-exposure39* PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain40* PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value41* PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on42auto-exposure routine43* PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate44*/4546#include "stv06xx_pb0100.h"4748static const struct ctrl pb0100_ctrl[] = {49#define GAIN_IDX 050{51{52.id = V4L2_CID_GAIN,53.type = V4L2_CTRL_TYPE_INTEGER,54.name = "Gain",55.minimum = 0,56.maximum = 255,57.step = 1,58.default_value = 12859},60.set = pb0100_set_gain,61.get = pb0100_get_gain62},63#define RED_BALANCE_IDX 164{65{66.id = V4L2_CID_RED_BALANCE,67.type = V4L2_CTRL_TYPE_INTEGER,68.name = "Red Balance",69.minimum = -255,70.maximum = 255,71.step = 1,72.default_value = 073},74.set = pb0100_set_red_balance,75.get = pb0100_get_red_balance76},77#define BLUE_BALANCE_IDX 278{79{80.id = V4L2_CID_BLUE_BALANCE,81.type = V4L2_CTRL_TYPE_INTEGER,82.name = "Blue Balance",83.minimum = -255,84.maximum = 255,85.step = 1,86.default_value = 087},88.set = pb0100_set_blue_balance,89.get = pb0100_get_blue_balance90},91#define EXPOSURE_IDX 392{93{94.id = V4L2_CID_EXPOSURE,95.type = V4L2_CTRL_TYPE_INTEGER,96.name = "Exposure",97.minimum = 0,98.maximum = 511,99.step = 1,100.default_value = 12101},102.set = pb0100_set_exposure,103.get = pb0100_get_exposure104},105#define AUTOGAIN_IDX 4106{107{108.id = V4L2_CID_AUTOGAIN,109.type = V4L2_CTRL_TYPE_BOOLEAN,110.name = "Automatic Gain and Exposure",111.minimum = 0,112.maximum = 1,113.step = 1,114.default_value = 1115},116.set = pb0100_set_autogain,117.get = pb0100_get_autogain118},119#define AUTOGAIN_TARGET_IDX 5120{121{122.id = V4L2_CTRL_CLASS_USER + 0x1000,123.type = V4L2_CTRL_TYPE_INTEGER,124.name = "Automatic Gain Target",125.minimum = 0,126.maximum = 255,127.step = 1,128.default_value = 128129},130.set = pb0100_set_autogain_target,131.get = pb0100_get_autogain_target132},133#define NATURAL_IDX 6134{135{136.id = V4L2_CTRL_CLASS_USER + 0x1001,137.type = V4L2_CTRL_TYPE_BOOLEAN,138.name = "Natural Light Source",139.minimum = 0,140.maximum = 1,141.step = 1,142.default_value = 1143},144.set = pb0100_set_natural,145.get = pb0100_get_natural146}147};148149static struct v4l2_pix_format pb0100_mode[] = {150/* low res / subsample modes disabled as they are only half res horizontal,151halving the vertical resolution does not seem to work */152{153320,154240,155V4L2_PIX_FMT_SGRBG8,156V4L2_FIELD_NONE,157.sizeimage = 320 * 240,158.bytesperline = 320,159.colorspace = V4L2_COLORSPACE_SRGB,160.priv = PB0100_CROP_TO_VGA161},162{163352,164288,165V4L2_PIX_FMT_SGRBG8,166V4L2_FIELD_NONE,167.sizeimage = 352 * 288,168.bytesperline = 352,169.colorspace = V4L2_COLORSPACE_SRGB,170.priv = 0171}172};173174static int pb0100_probe(struct sd *sd)175{176u16 sensor;177int i, err;178s32 *sensor_settings;179180err = stv06xx_read_sensor(sd, PB_IDENT, &sensor);181182if (err < 0)183return -ENODEV;184185if ((sensor >> 8) == 0x64) {186sensor_settings = kmalloc(187ARRAY_SIZE(pb0100_ctrl) * sizeof(s32),188GFP_KERNEL);189if (!sensor_settings)190return -ENOMEM;191192info("Photobit pb0100 sensor detected");193194sd->gspca_dev.cam.cam_mode = pb0100_mode;195sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode);196sd->desc.ctrls = pb0100_ctrl;197sd->desc.nctrls = ARRAY_SIZE(pb0100_ctrl);198for (i = 0; i < sd->desc.nctrls; i++)199sensor_settings[i] = pb0100_ctrl[i].qctrl.default_value;200sd->sensor_priv = sensor_settings;201202return 0;203}204205return -ENODEV;206}207208static int pb0100_start(struct sd *sd)209{210int err, packet_size, max_packet_size;211struct usb_host_interface *alt;212struct usb_interface *intf;213struct cam *cam = &sd->gspca_dev.cam;214s32 *sensor_settings = sd->sensor_priv;215u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv;216217intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);218alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);219if (!alt)220return -ENODEV;221packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);222223/* If we don't have enough bandwidth use a lower framerate */224max_packet_size = sd->sensor->max_packet_size[sd->gspca_dev.curr_mode];225if (packet_size < max_packet_size)226stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));227else228stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(5)|BIT(3)|BIT(1));229230/* Setup sensor window */231if (mode & PB0100_CROP_TO_VGA) {232stv06xx_write_sensor(sd, PB_RSTART, 30);233stv06xx_write_sensor(sd, PB_CSTART, 20);234stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1);235stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1);236} else {237stv06xx_write_sensor(sd, PB_RSTART, 8);238stv06xx_write_sensor(sd, PB_CSTART, 4);239stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1);240stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1);241}242243if (mode & PB0100_SUBSAMPLE) {244stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */245stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);246247stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);248} else {249stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);250stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);251/* larger -> slower */252stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);253}254255/* set_gain also sets red and blue balance */256pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);257pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]);258pb0100_set_autogain_target(&sd->gspca_dev,259sensor_settings[AUTOGAIN_TARGET_IDX]);260pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]);261262err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1));263PDEBUG(D_STREAM, "Started stream, status: %d", err);264265return (err < 0) ? err : 0;266}267268static int pb0100_stop(struct sd *sd)269{270int err;271272err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1);273274if (err < 0)275goto out;276277/* Set bit 1 to zero */278err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));279280PDEBUG(D_STREAM, "Halting stream");281out:282return (err < 0) ? err : 0;283}284285static void pb0100_disconnect(struct sd *sd)286{287sd->sensor = NULL;288kfree(sd->sensor_priv);289}290291/* FIXME: Sort the init commands out and put them into tables,292this is only for getting the camera to work */293/* FIXME: No error handling for now,294add this once the init has been converted to proper tables */295static int pb0100_init(struct sd *sd)296{297stv06xx_write_bridge(sd, STV_REG00, 1);298stv06xx_write_bridge(sd, STV_SCAN_RATE, 0);299300/* Reset sensor */301stv06xx_write_sensor(sd, PB_RESET, 1);302stv06xx_write_sensor(sd, PB_RESET, 0);303304/* Disable chip */305stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));306307/* Gain stuff...*/308stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6));309stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12);310311/* Set up auto-exposure */312/* ADC VREF_HI new setting for a transition313from the Expose1 to the Expose2 setting */314stv06xx_write_sensor(sd, PB_R28, 12);315/* gain max for autoexposure */316stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180);317/* gain min for autoexposure */318stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12);319/* Maximum frame integration time (programmed into R8)320allowed for auto-exposure routine */321stv06xx_write_sensor(sd, PB_R54, 3);322/* Minimum frame integration time (programmed into R8)323allowed for auto-exposure routine */324stv06xx_write_sensor(sd, PB_R55, 0);325stv06xx_write_sensor(sd, PB_UPDATEINT, 1);326/* R15 Expose0 (maximum that auto-exposure may use) */327stv06xx_write_sensor(sd, PB_R15, 800);328/* R17 Expose2 (minimum that auto-exposure may use) */329stv06xx_write_sensor(sd, PB_R17, 10);330331stv06xx_write_sensor(sd, PB_EXPGAIN, 0);332333/* 0x14 */334stv06xx_write_sensor(sd, PB_VOFFSET, 0);335/* 0x0D */336stv06xx_write_sensor(sd, PB_ADCGAINH, 11);337/* Set black level (important!) */338stv06xx_write_sensor(sd, PB_ADCGAINL, 0);339340/* ??? */341stv06xx_write_bridge(sd, STV_REG00, 0x11);342stv06xx_write_bridge(sd, STV_REG03, 0x45);343stv06xx_write_bridge(sd, STV_REG04, 0x07);344345/* Scan/timing for the sensor */346stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));347stv06xx_write_sensor(sd, PB_CFILLIN, 14);348stv06xx_write_sensor(sd, PB_VBL, 0);349stv06xx_write_sensor(sd, PB_FINTTIME, 0);350stv06xx_write_sensor(sd, PB_RINTTIME, 123);351352stv06xx_write_bridge(sd, STV_REG01, 0xc2);353stv06xx_write_bridge(sd, STV_REG02, 0xb0);354return 0;355}356357static int pb0100_dump(struct sd *sd)358{359return 0;360}361362static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val)363{364struct sd *sd = (struct sd *) gspca_dev;365s32 *sensor_settings = sd->sensor_priv;366367*val = sensor_settings[GAIN_IDX];368369return 0;370}371372static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val)373{374int err;375struct sd *sd = (struct sd *) gspca_dev;376s32 *sensor_settings = sd->sensor_priv;377378if (sensor_settings[AUTOGAIN_IDX])379return -EBUSY;380381sensor_settings[GAIN_IDX] = val;382err = stv06xx_write_sensor(sd, PB_G1GAIN, val);383if (!err)384err = stv06xx_write_sensor(sd, PB_G2GAIN, val);385PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err);386387if (!err)388err = pb0100_set_red_balance(gspca_dev,389sensor_settings[RED_BALANCE_IDX]);390if (!err)391err = pb0100_set_blue_balance(gspca_dev,392sensor_settings[BLUE_BALANCE_IDX]);393394return err;395}396397static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)398{399struct sd *sd = (struct sd *) gspca_dev;400s32 *sensor_settings = sd->sensor_priv;401402*val = sensor_settings[RED_BALANCE_IDX];403404return 0;405}406407static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)408{409int err;410struct sd *sd = (struct sd *) gspca_dev;411s32 *sensor_settings = sd->sensor_priv;412413if (sensor_settings[AUTOGAIN_IDX])414return -EBUSY;415416sensor_settings[RED_BALANCE_IDX] = val;417val += sensor_settings[GAIN_IDX];418if (val < 0)419val = 0;420else if (val > 255)421val = 255;422423err = stv06xx_write_sensor(sd, PB_RGAIN, val);424PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err);425426return err;427}428429static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)430{431struct sd *sd = (struct sd *) gspca_dev;432s32 *sensor_settings = sd->sensor_priv;433434*val = sensor_settings[BLUE_BALANCE_IDX];435436return 0;437}438439static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)440{441int err;442struct sd *sd = (struct sd *) gspca_dev;443s32 *sensor_settings = sd->sensor_priv;444445if (sensor_settings[AUTOGAIN_IDX])446return -EBUSY;447448sensor_settings[BLUE_BALANCE_IDX] = val;449val += sensor_settings[GAIN_IDX];450if (val < 0)451val = 0;452else if (val > 255)453val = 255;454455err = stv06xx_write_sensor(sd, PB_BGAIN, val);456PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err);457458return err;459}460461static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)462{463struct sd *sd = (struct sd *) gspca_dev;464s32 *sensor_settings = sd->sensor_priv;465466*val = sensor_settings[EXPOSURE_IDX];467468return 0;469}470471static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val)472{473int err;474struct sd *sd = (struct sd *) gspca_dev;475s32 *sensor_settings = sd->sensor_priv;476477if (sensor_settings[AUTOGAIN_IDX])478return -EBUSY;479480sensor_settings[EXPOSURE_IDX] = val;481err = stv06xx_write_sensor(sd, PB_RINTTIME, val);482PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err);483484return err;485}486487static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val)488{489struct sd *sd = (struct sd *) gspca_dev;490s32 *sensor_settings = sd->sensor_priv;491492*val = sensor_settings[AUTOGAIN_IDX];493494return 0;495}496497static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val)498{499int err;500struct sd *sd = (struct sd *) gspca_dev;501s32 *sensor_settings = sd->sensor_priv;502503sensor_settings[AUTOGAIN_IDX] = val;504if (sensor_settings[AUTOGAIN_IDX]) {505if (sensor_settings[NATURAL_IDX])506val = BIT(6)|BIT(4)|BIT(0);507else508val = BIT(4)|BIT(0);509} else510val = 0;511512err = stv06xx_write_sensor(sd, PB_EXPGAIN, val);513PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d",514sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX],515err);516517return err;518}519520static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val)521{522struct sd *sd = (struct sd *) gspca_dev;523s32 *sensor_settings = sd->sensor_priv;524525*val = sensor_settings[AUTOGAIN_TARGET_IDX];526527return 0;528}529530static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val)531{532int err, totalpixels, brightpixels, darkpixels;533struct sd *sd = (struct sd *) gspca_dev;534s32 *sensor_settings = sd->sensor_priv;535536sensor_settings[AUTOGAIN_TARGET_IDX] = val;537538/* Number of pixels counted by the sensor when subsampling the pixels.539* Slightly larger than the real value to avoid oscillation */540totalpixels = gspca_dev->width * gspca_dev->height;541totalpixels = totalpixels/(8*8) + totalpixels/(64*64);542543brightpixels = (totalpixels * val) >> 8;544darkpixels = totalpixels - brightpixels;545err = stv06xx_write_sensor(sd, PB_R21, brightpixels);546if (!err)547err = stv06xx_write_sensor(sd, PB_R22, darkpixels);548549PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err);550551return err;552}553554static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val)555{556struct sd *sd = (struct sd *) gspca_dev;557s32 *sensor_settings = sd->sensor_priv;558559*val = sensor_settings[NATURAL_IDX];560561return 0;562}563564static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val)565{566struct sd *sd = (struct sd *) gspca_dev;567s32 *sensor_settings = sd->sensor_priv;568569sensor_settings[NATURAL_IDX] = val;570571return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]);572}573574575