162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher 462306a36Sopenharmony_ci * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland 562306a36Sopenharmony_ci * Copyright (c) 2002, 2003 Tuukka Toivonen 662306a36Sopenharmony_ci * Copyright (c) 2008 Erik Andrén 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * P/N 861037: Sensor HDCS1000 ASIC STV0600 962306a36Sopenharmony_ci * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 1062306a36Sopenharmony_ci * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express 1162306a36Sopenharmony_ci * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam 1262306a36Sopenharmony_ci * P/N 861075-0040: Sensor HDCS1000 ASIC 1362306a36Sopenharmony_ci * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB 1462306a36Sopenharmony_ci * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "stv06xx_vv6410.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct v4l2_pix_format vv6410_mode[] = { 2262306a36Sopenharmony_ci { 2362306a36Sopenharmony_ci 356, 2462306a36Sopenharmony_ci 292, 2562306a36Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 2662306a36Sopenharmony_ci V4L2_FIELD_NONE, 2762306a36Sopenharmony_ci .sizeimage = 356 * 292, 2862306a36Sopenharmony_ci .bytesperline = 356, 2962306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 3062306a36Sopenharmony_ci .priv = 0 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct gspca_dev *gspca_dev = 3762306a36Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 3862306a36Sopenharmony_ci int err = -EINVAL; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci switch (ctrl->id) { 4162306a36Sopenharmony_ci case V4L2_CID_HFLIP: 4262306a36Sopenharmony_ci if (!gspca_dev->streaming) 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci err = vv6410_set_hflip(gspca_dev, ctrl->val); 4562306a36Sopenharmony_ci break; 4662306a36Sopenharmony_ci case V4L2_CID_VFLIP: 4762306a36Sopenharmony_ci if (!gspca_dev->streaming) 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci err = vv6410_set_vflip(gspca_dev, ctrl->val); 5062306a36Sopenharmony_ci break; 5162306a36Sopenharmony_ci case V4L2_CID_GAIN: 5262306a36Sopenharmony_ci err = vv6410_set_analog_gain(gspca_dev, ctrl->val); 5362306a36Sopenharmony_ci break; 5462306a36Sopenharmony_ci case V4L2_CID_EXPOSURE: 5562306a36Sopenharmony_ci err = vv6410_set_exposure(gspca_dev, ctrl->val); 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci return err; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops vv6410_ctrl_ops = { 6262306a36Sopenharmony_ci .s_ctrl = vv6410_s_ctrl, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int vv6410_probe(struct sd *sd) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci u16 data; 6862306a36Sopenharmony_ci int err; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data); 7162306a36Sopenharmony_ci if (err < 0) 7262306a36Sopenharmony_ci return -ENODEV; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (data != 0x19) 7562306a36Sopenharmony_ci return -ENODEV; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci pr_info("vv6410 sensor detected\n"); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci sd->gspca_dev.cam.cam_mode = vv6410_mode; 8062306a36Sopenharmony_ci sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int vv6410_init_controls(struct sd *sd) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 2); 8962306a36Sopenharmony_ci /* Disable the hardware VFLIP and HFLIP as we currently lack a 9062306a36Sopenharmony_ci mechanism to adjust the image offset in such a way that 9162306a36Sopenharmony_ci we don't need to renegotiate the announced format */ 9262306a36Sopenharmony_ci /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ 9362306a36Sopenharmony_ci /* V4L2_CID_HFLIP, 0, 1, 1, 0); */ 9462306a36Sopenharmony_ci /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ 9562306a36Sopenharmony_ci /* V4L2_CID_VFLIP, 0, 1, 1, 0); */ 9662306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, 9762306a36Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); 9862306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, 9962306a36Sopenharmony_ci V4L2_CID_GAIN, 0, 15, 1, 10); 10062306a36Sopenharmony_ci return hdl->error; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int vv6410_init(struct sd *sd) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int err = 0, i; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) 10862306a36Sopenharmony_ci stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init, 11162306a36Sopenharmony_ci ARRAY_SIZE(vv6410_sensor_init)); 11262306a36Sopenharmony_ci return (err < 0) ? err : 0; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int vv6410_start(struct sd *sd) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int err; 11862306a36Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 11962306a36Sopenharmony_ci struct cam *cam = &sd->gspca_dev.cam; 12062306a36Sopenharmony_ci u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (priv & VV6410_SUBSAMPLE) { 12362306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Enabling subsampling\n"); 12462306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); 12562306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); 12862306a36Sopenharmony_ci } else { 12962306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); 13062306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); 13162306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Turn on LED */ 13662306a36Sopenharmony_ci err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON); 13762306a36Sopenharmony_ci if (err < 0) 13862306a36Sopenharmony_ci return err; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0); 14162306a36Sopenharmony_ci if (err < 0) 14262306a36Sopenharmony_ci return err; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Starting stream\n"); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int vv6410_stop(struct sd *sd) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 15262306a36Sopenharmony_ci int err; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* Turn off LED */ 15562306a36Sopenharmony_ci err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF); 15662306a36Sopenharmony_ci if (err < 0) 15762306a36Sopenharmony_ci return err; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE); 16062306a36Sopenharmony_ci if (err < 0) 16162306a36Sopenharmony_ci return err; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n"); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int vv6410_dump(struct sd *sd) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci u8 i; 17162306a36Sopenharmony_ci int err = 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pr_info("Dumping all vv6410 sensor registers\n"); 17462306a36Sopenharmony_ci for (i = 0; i < 0xff && !err; i++) { 17562306a36Sopenharmony_ci u16 data; 17662306a36Sopenharmony_ci err = stv06xx_read_sensor(sd, i, &data); 17762306a36Sopenharmony_ci pr_info("Register 0x%x contained 0x%x\n", i, data); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci return (err < 0) ? err : 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci int err; 18562306a36Sopenharmony_ci u16 i2c_data; 18662306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); 18962306a36Sopenharmony_ci if (err < 0) 19062306a36Sopenharmony_ci return err; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (val) 19362306a36Sopenharmony_ci i2c_data |= VV6410_HFLIP; 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci i2c_data &= ~VV6410_HFLIP; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set horizontal flip to %d\n", val); 19862306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return (err < 0) ? err : 0; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci int err; 20662306a36Sopenharmony_ci u16 i2c_data; 20762306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); 21062306a36Sopenharmony_ci if (err < 0) 21162306a36Sopenharmony_ci return err; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (val) 21462306a36Sopenharmony_ci i2c_data |= VV6410_VFLIP; 21562306a36Sopenharmony_ci else 21662306a36Sopenharmony_ci i2c_data &= ~VV6410_VFLIP; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set vertical flip to %d\n", val); 21962306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return (err < 0) ? err : 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci int err; 22762306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set analog gain to %d\n", val); 23062306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return (err < 0) ? err : 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci int err; 23862306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 23962306a36Sopenharmony_ci unsigned int fine, coarse; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci val = (val * val >> 14) + val / 4; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci fine = val % VV6410_CIF_LINELENGTH; 24462306a36Sopenharmony_ci coarse = min(512, val / VV6410_CIF_LINELENGTH); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set coarse exposure to %d, fine exposure to %d\n", 24762306a36Sopenharmony_ci coarse, fine); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8); 25062306a36Sopenharmony_ci if (err < 0) 25162306a36Sopenharmony_ci goto out; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff); 25462306a36Sopenharmony_ci if (err < 0) 25562306a36Sopenharmony_ci goto out; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8); 25862306a36Sopenharmony_ci if (err < 0) 25962306a36Sopenharmony_ci goto out; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ciout: 26462306a36Sopenharmony_ci return err; 26562306a36Sopenharmony_ci} 266