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/* 1862306a36Sopenharmony_ci * The spec file for the PB-0100 suggests the following for best quality 1962306a36Sopenharmony_ci * images after the sensor has been reset : 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC 2262306a36Sopenharmony_ci to produce good black level 2362306a36Sopenharmony_ci * PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes 2462306a36Sopenharmony_ci through R53 2562306a36Sopenharmony_ci * PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for 2662306a36Sopenharmony_ci auto-exposure 2762306a36Sopenharmony_ci * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain 2862306a36Sopenharmony_ci * PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value 2962306a36Sopenharmony_ci * PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on 3062306a36Sopenharmony_ci auto-exposure routine 3162306a36Sopenharmony_ci * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "stv06xx_pb0100.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct pb0100_ctrls { 3962306a36Sopenharmony_ci struct { /* one big happy control cluster... */ 4062306a36Sopenharmony_ci struct v4l2_ctrl *autogain; 4162306a36Sopenharmony_ci struct v4l2_ctrl *gain; 4262306a36Sopenharmony_ci struct v4l2_ctrl *exposure; 4362306a36Sopenharmony_ci struct v4l2_ctrl *red; 4462306a36Sopenharmony_ci struct v4l2_ctrl *blue; 4562306a36Sopenharmony_ci struct v4l2_ctrl *natural; 4662306a36Sopenharmony_ci }; 4762306a36Sopenharmony_ci struct v4l2_ctrl *target; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic struct v4l2_pix_format pb0100_mode[] = { 5162306a36Sopenharmony_ci/* low res / subsample modes disabled as they are only half res horizontal, 5262306a36Sopenharmony_ci halving the vertical resolution does not seem to work */ 5362306a36Sopenharmony_ci { 5462306a36Sopenharmony_ci 320, 5562306a36Sopenharmony_ci 240, 5662306a36Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 5762306a36Sopenharmony_ci V4L2_FIELD_NONE, 5862306a36Sopenharmony_ci .sizeimage = 320 * 240, 5962306a36Sopenharmony_ci .bytesperline = 320, 6062306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 6162306a36Sopenharmony_ci .priv = PB0100_CROP_TO_VGA 6262306a36Sopenharmony_ci }, 6362306a36Sopenharmony_ci { 6462306a36Sopenharmony_ci 352, 6562306a36Sopenharmony_ci 288, 6662306a36Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 6762306a36Sopenharmony_ci V4L2_FIELD_NONE, 6862306a36Sopenharmony_ci .sizeimage = 352 * 288, 6962306a36Sopenharmony_ci .bytesperline = 352, 7062306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 7162306a36Sopenharmony_ci .priv = 0 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int pb0100_s_ctrl(struct v4l2_ctrl *ctrl) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct gspca_dev *gspca_dev = 7862306a36Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 7962306a36Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 8062306a36Sopenharmony_ci struct pb0100_ctrls *ctrls = sd->sensor_priv; 8162306a36Sopenharmony_ci int err = -EINVAL; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci switch (ctrl->id) { 8462306a36Sopenharmony_ci case V4L2_CID_AUTOGAIN: 8562306a36Sopenharmony_ci err = pb0100_set_autogain(gspca_dev, ctrl->val); 8662306a36Sopenharmony_ci if (err) 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci if (ctrl->val) 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci err = pb0100_set_gain(gspca_dev, ctrls->gain->val); 9162306a36Sopenharmony_ci if (err) 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci err = pb0100_set_exposure(gspca_dev, ctrls->exposure->val); 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci case V4L2_CTRL_CLASS_USER + 0x1001: 9662306a36Sopenharmony_ci err = pb0100_set_autogain_target(gspca_dev, ctrl->val); 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci return err; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops pb0100_ctrl_ops = { 10362306a36Sopenharmony_ci .s_ctrl = pb0100_s_ctrl, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int pb0100_init_controls(struct sd *sd) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; 10962306a36Sopenharmony_ci struct pb0100_ctrls *ctrls; 11062306a36Sopenharmony_ci static const struct v4l2_ctrl_config autogain_target = { 11162306a36Sopenharmony_ci .ops = &pb0100_ctrl_ops, 11262306a36Sopenharmony_ci .id = V4L2_CTRL_CLASS_USER + 0x1000, 11362306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 11462306a36Sopenharmony_ci .name = "Automatic Gain Target", 11562306a36Sopenharmony_ci .max = 255, 11662306a36Sopenharmony_ci .step = 1, 11762306a36Sopenharmony_ci .def = 128, 11862306a36Sopenharmony_ci }; 11962306a36Sopenharmony_ci static const struct v4l2_ctrl_config natural_light = { 12062306a36Sopenharmony_ci .ops = &pb0100_ctrl_ops, 12162306a36Sopenharmony_ci .id = V4L2_CTRL_CLASS_USER + 0x1001, 12262306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 12362306a36Sopenharmony_ci .name = "Natural Light Source", 12462306a36Sopenharmony_ci .max = 1, 12562306a36Sopenharmony_ci .step = 1, 12662306a36Sopenharmony_ci .def = 1, 12762306a36Sopenharmony_ci }; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ctrls = kzalloc(sizeof(*ctrls), GFP_KERNEL); 13062306a36Sopenharmony_ci if (!ctrls) 13162306a36Sopenharmony_ci return -ENOMEM; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 6); 13462306a36Sopenharmony_ci ctrls->autogain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, 13562306a36Sopenharmony_ci V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 13662306a36Sopenharmony_ci ctrls->exposure = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, 13762306a36Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 511, 1, 12); 13862306a36Sopenharmony_ci ctrls->gain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, 13962306a36Sopenharmony_ci V4L2_CID_GAIN, 0, 255, 1, 128); 14062306a36Sopenharmony_ci ctrls->red = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, 14162306a36Sopenharmony_ci V4L2_CID_RED_BALANCE, -255, 255, 1, 0); 14262306a36Sopenharmony_ci ctrls->blue = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, 14362306a36Sopenharmony_ci V4L2_CID_BLUE_BALANCE, -255, 255, 1, 0); 14462306a36Sopenharmony_ci ctrls->natural = v4l2_ctrl_new_custom(hdl, &natural_light, NULL); 14562306a36Sopenharmony_ci ctrls->target = v4l2_ctrl_new_custom(hdl, &autogain_target, NULL); 14662306a36Sopenharmony_ci if (hdl->error) { 14762306a36Sopenharmony_ci kfree(ctrls); 14862306a36Sopenharmony_ci return hdl->error; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci sd->sensor_priv = ctrls; 15162306a36Sopenharmony_ci v4l2_ctrl_auto_cluster(5, &ctrls->autogain, 0, false); 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int pb0100_probe(struct sd *sd) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci u16 sensor; 15862306a36Sopenharmony_ci int err; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci err = stv06xx_read_sensor(sd, PB_IDENT, &sensor); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (err < 0) 16362306a36Sopenharmony_ci return -ENODEV; 16462306a36Sopenharmony_ci if ((sensor >> 8) != 0x64) 16562306a36Sopenharmony_ci return -ENODEV; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci pr_info("Photobit pb0100 sensor detected\n"); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci sd->gspca_dev.cam.cam_mode = pb0100_mode; 17062306a36Sopenharmony_ci sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int pb0100_start(struct sd *sd) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci int err, packet_size, max_packet_size; 17862306a36Sopenharmony_ci struct usb_host_interface *alt; 17962306a36Sopenharmony_ci struct usb_interface *intf; 18062306a36Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 18162306a36Sopenharmony_ci struct cam *cam = &sd->gspca_dev.cam; 18262306a36Sopenharmony_ci u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); 18562306a36Sopenharmony_ci alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); 18662306a36Sopenharmony_ci if (!alt) 18762306a36Sopenharmony_ci return -ENODEV; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (alt->desc.bNumEndpoints < 1) 19062306a36Sopenharmony_ci return -ENODEV; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* If we don't have enough bandwidth use a lower framerate */ 19562306a36Sopenharmony_ci max_packet_size = sd->sensor->max_packet_size[sd->gspca_dev.curr_mode]; 19662306a36Sopenharmony_ci if (packet_size < max_packet_size) 19762306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1)); 19862306a36Sopenharmony_ci else 19962306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(5)|BIT(3)|BIT(1)); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Setup sensor window */ 20262306a36Sopenharmony_ci if (mode & PB0100_CROP_TO_VGA) { 20362306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_RSTART, 30); 20462306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_CSTART, 20); 20562306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1); 20662306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1); 20762306a36Sopenharmony_ci } else { 20862306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_RSTART, 8); 20962306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_CSTART, 4); 21062306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1); 21162306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (mode & PB0100_SUBSAMPLE) { 21562306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */ 21662306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); 22162306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); 22262306a36Sopenharmony_ci /* larger -> slower */ 22362306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1)); 22762306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Started stream, status: %d\n", err); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return (err < 0) ? err : 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int pb0100_stop(struct sd *sd) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 23562306a36Sopenharmony_ci int err; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (err < 0) 24062306a36Sopenharmony_ci goto out; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Set bit 1 to zero */ 24362306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Halting stream\n"); 24662306a36Sopenharmony_ciout: 24762306a36Sopenharmony_ci return (err < 0) ? err : 0; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* FIXME: Sort the init commands out and put them into tables, 25162306a36Sopenharmony_ci this is only for getting the camera to work */ 25262306a36Sopenharmony_ci/* FIXME: No error handling for now, 25362306a36Sopenharmony_ci add this once the init has been converted to proper tables */ 25462306a36Sopenharmony_cistatic int pb0100_init(struct sd *sd) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_REG00, 1); 25762306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_SCAN_RATE, 0); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* Reset sensor */ 26062306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_RESET, 1); 26162306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_RESET, 0); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Disable chip */ 26462306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Gain stuff...*/ 26762306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6)); 26862306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Set up auto-exposure */ 27162306a36Sopenharmony_ci /* ADC VREF_HI new setting for a transition 27262306a36Sopenharmony_ci from the Expose1 to the Expose2 setting */ 27362306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_R28, 12); 27462306a36Sopenharmony_ci /* gain max for autoexposure */ 27562306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180); 27662306a36Sopenharmony_ci /* gain min for autoexposure */ 27762306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12); 27862306a36Sopenharmony_ci /* Maximum frame integration time (programmed into R8) 27962306a36Sopenharmony_ci allowed for auto-exposure routine */ 28062306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_R54, 3); 28162306a36Sopenharmony_ci /* Minimum frame integration time (programmed into R8) 28262306a36Sopenharmony_ci allowed for auto-exposure routine */ 28362306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_R55, 0); 28462306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_UPDATEINT, 1); 28562306a36Sopenharmony_ci /* R15 Expose0 (maximum that auto-exposure may use) */ 28662306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_R15, 800); 28762306a36Sopenharmony_ci /* R17 Expose2 (minimum that auto-exposure may use) */ 28862306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_R17, 10); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_EXPGAIN, 0); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* 0x14 */ 29362306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_VOFFSET, 0); 29462306a36Sopenharmony_ci /* 0x0D */ 29562306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_ADCGAINH, 11); 29662306a36Sopenharmony_ci /* Set black level (important!) */ 29762306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_ADCGAINL, 0); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* ??? */ 30062306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_REG00, 0x11); 30162306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_REG03, 0x45); 30262306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_REG04, 0x07); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* Scan/timing for the sensor */ 30562306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1)); 30662306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_CFILLIN, 14); 30762306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_VBL, 0); 30862306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_FINTTIME, 0); 30962306a36Sopenharmony_ci stv06xx_write_sensor(sd, PB_RINTTIME, 123); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_REG01, 0xc2); 31262306a36Sopenharmony_ci stv06xx_write_bridge(sd, STV_REG02, 0xb0); 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int pb0100_dump(struct sd *sd) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int err; 32462306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 32562306a36Sopenharmony_ci struct pb0100_ctrls *ctrls = sd->sensor_priv; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_G1GAIN, val); 32862306a36Sopenharmony_ci if (!err) 32962306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_G2GAIN, val); 33062306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set green gain to %d, status: %d\n", 33162306a36Sopenharmony_ci val, err); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (!err) 33462306a36Sopenharmony_ci err = pb0100_set_red_balance(gspca_dev, ctrls->red->val); 33562306a36Sopenharmony_ci if (!err) 33662306a36Sopenharmony_ci err = pb0100_set_blue_balance(gspca_dev, ctrls->blue->val); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return err; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci int err; 34462306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 34562306a36Sopenharmony_ci struct pb0100_ctrls *ctrls = sd->sensor_priv; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci val += ctrls->gain->val; 34862306a36Sopenharmony_ci if (val < 0) 34962306a36Sopenharmony_ci val = 0; 35062306a36Sopenharmony_ci else if (val > 255) 35162306a36Sopenharmony_ci val = 255; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_RGAIN, val); 35462306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set red gain to %d, status: %d\n", 35562306a36Sopenharmony_ci val, err); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return err; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci int err; 36362306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 36462306a36Sopenharmony_ci struct pb0100_ctrls *ctrls = sd->sensor_priv; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci val += ctrls->gain->val; 36762306a36Sopenharmony_ci if (val < 0) 36862306a36Sopenharmony_ci val = 0; 36962306a36Sopenharmony_ci else if (val > 255) 37062306a36Sopenharmony_ci val = 255; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_BGAIN, val); 37362306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set blue gain to %d, status: %d\n", 37462306a36Sopenharmony_ci val, err); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return err; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 38262306a36Sopenharmony_ci int err; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_RINTTIME, val); 38562306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d, status: %d\n", 38662306a36Sopenharmony_ci val, err); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return err; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci int err; 39462306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 39562306a36Sopenharmony_ci struct pb0100_ctrls *ctrls = sd->sensor_priv; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (val) { 39862306a36Sopenharmony_ci if (ctrls->natural->val) 39962306a36Sopenharmony_ci val = BIT(6)|BIT(4)|BIT(0); 40062306a36Sopenharmony_ci else 40162306a36Sopenharmony_ci val = BIT(4)|BIT(0); 40262306a36Sopenharmony_ci } else 40362306a36Sopenharmony_ci val = 0; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_EXPGAIN, val); 40662306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set autogain to %d (natural: %d), status: %d\n", 40762306a36Sopenharmony_ci val, ctrls->natural->val, err); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return err; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci int err, totalpixels, brightpixels, darkpixels; 41562306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Number of pixels counted by the sensor when subsampling the pixels. 41862306a36Sopenharmony_ci * Slightly larger than the real value to avoid oscillation */ 41962306a36Sopenharmony_ci totalpixels = gspca_dev->pixfmt.width * gspca_dev->pixfmt.height; 42062306a36Sopenharmony_ci totalpixels = totalpixels/(8*8) + totalpixels/(64*64); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci brightpixels = (totalpixels * val) >> 8; 42362306a36Sopenharmony_ci darkpixels = totalpixels - brightpixels; 42462306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_R21, brightpixels); 42562306a36Sopenharmony_ci if (!err) 42662306a36Sopenharmony_ci err = stv06xx_write_sensor(sd, PB_R22, darkpixels); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set autogain target to %d, status: %d\n", 42962306a36Sopenharmony_ci val, err); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return err; 43262306a36Sopenharmony_ci} 433