Path: blob/master/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.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#include "stv06xx_vv6410.h"3031static struct v4l2_pix_format vv6410_mode[] = {32{33356,34292,35V4L2_PIX_FMT_SGRBG8,36V4L2_FIELD_NONE,37.sizeimage = 356 * 292,38.bytesperline = 356,39.colorspace = V4L2_COLORSPACE_SRGB,40.priv = 041}42};4344static const struct ctrl vv6410_ctrl[] = {45#define HFLIP_IDX 046{47{48.id = V4L2_CID_HFLIP,49.type = V4L2_CTRL_TYPE_BOOLEAN,50.name = "horizontal flip",51.minimum = 0,52.maximum = 1,53.step = 1,54.default_value = 055},56.set = vv6410_set_hflip,57.get = vv6410_get_hflip58},59#define VFLIP_IDX 160{61{62.id = V4L2_CID_VFLIP,63.type = V4L2_CTRL_TYPE_BOOLEAN,64.name = "vertical flip",65.minimum = 0,66.maximum = 1,67.step = 1,68.default_value = 069},70.set = vv6410_set_vflip,71.get = vv6410_get_vflip72},73#define GAIN_IDX 274{75{76.id = V4L2_CID_GAIN,77.type = V4L2_CTRL_TYPE_INTEGER,78.name = "analog gain",79.minimum = 0,80.maximum = 15,81.step = 1,82.default_value = 1083},84.set = vv6410_set_analog_gain,85.get = vv6410_get_analog_gain86},87#define EXPOSURE_IDX 388{89{90.id = V4L2_CID_EXPOSURE,91.type = V4L2_CTRL_TYPE_INTEGER,92.name = "exposure",93.minimum = 0,94.maximum = 32768,95.step = 1,96.default_value = 2000097},98.set = vv6410_set_exposure,99.get = vv6410_get_exposure100}101};102103static int vv6410_probe(struct sd *sd)104{105u16 data;106int err, i;107s32 *sensor_settings;108109err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);110if (err < 0)111return -ENODEV;112113if (data == 0x19) {114info("vv6410 sensor detected");115116sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32),117GFP_KERNEL);118if (!sensor_settings)119return -ENOMEM;120121sd->gspca_dev.cam.cam_mode = vv6410_mode;122sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode);123sd->desc.ctrls = vv6410_ctrl;124sd->desc.nctrls = ARRAY_SIZE(vv6410_ctrl);125126for (i = 0; i < sd->desc.nctrls; i++)127sensor_settings[i] = vv6410_ctrl[i].qctrl.default_value;128sd->sensor_priv = sensor_settings;129return 0;130}131return -ENODEV;132}133134static int vv6410_init(struct sd *sd)135{136int err = 0, i;137s32 *sensor_settings = sd->sensor_priv;138139for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) {140/* if NULL then len contains single value */141if (stv_bridge_init[i].data == NULL) {142err = stv06xx_write_bridge(sd,143stv_bridge_init[i].start,144stv_bridge_init[i].len);145} else {146int j;147for (j = 0; j < stv_bridge_init[i].len; j++)148err = stv06xx_write_bridge(sd,149stv_bridge_init[i].start + j,150stv_bridge_init[i].data[j]);151}152}153154if (err < 0)155return err;156157err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init,158ARRAY_SIZE(vv6410_sensor_init));159if (err < 0)160return err;161162err = vv6410_set_exposure(&sd->gspca_dev,163sensor_settings[EXPOSURE_IDX]);164if (err < 0)165return err;166167err = vv6410_set_analog_gain(&sd->gspca_dev,168sensor_settings[GAIN_IDX]);169170return (err < 0) ? err : 0;171}172173static void vv6410_disconnect(struct sd *sd)174{175sd->sensor = NULL;176kfree(sd->sensor_priv);177}178179static int vv6410_start(struct sd *sd)180{181int err;182struct cam *cam = &sd->gspca_dev.cam;183u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;184185if (priv & VV6410_CROP_TO_QVGA) {186PDEBUG(D_CONF, "Cropping to QVGA");187stv06xx_write_sensor(sd, VV6410_XENDH, 320 - 1);188stv06xx_write_sensor(sd, VV6410_YENDH, 240 - 1);189} else {190stv06xx_write_sensor(sd, VV6410_XENDH, 360 - 1);191stv06xx_write_sensor(sd, VV6410_YENDH, 294 - 1);192}193194if (priv & VV6410_SUBSAMPLE) {195PDEBUG(D_CONF, "Enabling subsampling");196stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);197stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);198199stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);200} else {201stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);202stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);203204stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);205}206207/* Turn on LED */208err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON);209if (err < 0)210return err;211212err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0);213if (err < 0)214return err;215216PDEBUG(D_STREAM, "Starting stream");217218return 0;219}220221static int vv6410_stop(struct sd *sd)222{223int err;224225/* Turn off LED */226err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF);227if (err < 0)228return err;229230err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE);231if (err < 0)232return err;233234PDEBUG(D_STREAM, "Halting stream");235236return (err < 0) ? err : 0;237}238239static int vv6410_dump(struct sd *sd)240{241u8 i;242int err = 0;243244info("Dumping all vv6410 sensor registers");245for (i = 0; i < 0xff && !err; i++) {246u16 data;247err = stv06xx_read_sensor(sd, i, &data);248info("Register 0x%x contained 0x%x", i, data);249}250return (err < 0) ? err : 0;251}252253static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)254{255struct sd *sd = (struct sd *) gspca_dev;256s32 *sensor_settings = sd->sensor_priv;257258*val = sensor_settings[HFLIP_IDX];259PDEBUG(D_V4L2, "Read horizontal flip %d", *val);260261return 0;262}263264static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)265{266int err;267u16 i2c_data;268struct sd *sd = (struct sd *) gspca_dev;269s32 *sensor_settings = sd->sensor_priv;270271sensor_settings[HFLIP_IDX] = val;272err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);273if (err < 0)274return err;275276if (val)277i2c_data |= VV6410_HFLIP;278else279i2c_data &= ~VV6410_HFLIP;280281PDEBUG(D_V4L2, "Set horizontal flip to %d", val);282err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);283284return (err < 0) ? err : 0;285}286287static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)288{289struct sd *sd = (struct sd *) gspca_dev;290s32 *sensor_settings = sd->sensor_priv;291292*val = sensor_settings[VFLIP_IDX];293PDEBUG(D_V4L2, "Read vertical flip %d", *val);294295return 0;296}297298static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)299{300int err;301u16 i2c_data;302struct sd *sd = (struct sd *) gspca_dev;303s32 *sensor_settings = sd->sensor_priv;304305sensor_settings[VFLIP_IDX] = val;306err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);307if (err < 0)308return err;309310if (val)311i2c_data |= VV6410_VFLIP;312else313i2c_data &= ~VV6410_VFLIP;314315PDEBUG(D_V4L2, "Set vertical flip to %d", val);316err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);317318return (err < 0) ? err : 0;319}320321static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val)322{323struct sd *sd = (struct sd *) gspca_dev;324s32 *sensor_settings = sd->sensor_priv;325326*val = sensor_settings[GAIN_IDX];327328PDEBUG(D_V4L2, "Read analog gain %d", *val);329330return 0;331}332333static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)334{335int err;336struct sd *sd = (struct sd *) gspca_dev;337s32 *sensor_settings = sd->sensor_priv;338339sensor_settings[GAIN_IDX] = val;340PDEBUG(D_V4L2, "Set analog gain to %d", val);341err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));342343return (err < 0) ? err : 0;344}345346static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)347{348struct sd *sd = (struct sd *) gspca_dev;349s32 *sensor_settings = sd->sensor_priv;350351*val = sensor_settings[EXPOSURE_IDX];352353PDEBUG(D_V4L2, "Read exposure %d", *val);354355return 0;356}357358static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val)359{360int err;361struct sd *sd = (struct sd *) gspca_dev;362s32 *sensor_settings = sd->sensor_priv;363unsigned int fine, coarse;364365sensor_settings[EXPOSURE_IDX] = val;366367val = (val * val >> 14) + val / 4;368369fine = val % VV6410_CIF_LINELENGTH;370coarse = min(512, val / VV6410_CIF_LINELENGTH);371372PDEBUG(D_V4L2, "Set coarse exposure to %d, fine expsure to %d",373coarse, fine);374375err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8);376if (err < 0)377goto out;378379err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff);380if (err < 0)381goto out;382383err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8);384if (err < 0)385goto out;386387err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff);388389out:390return err;391}392393394