162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sonix sn9c102 (bayer) library 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009-2011 Jean-François Moine <http://moinejf.free.fr> 662306a36Sopenharmony_ci * Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr 762306a36Sopenharmony_ci * Add Pas106 Stefano Mozzi (C) 2004 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* Some documentation on known sonixb registers: 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ciReg Use 1362306a36Sopenharmony_cisn9c101 / sn9c102: 1462306a36Sopenharmony_ci0x10 high nibble red gain low nibble blue gain 1562306a36Sopenharmony_ci0x11 low nibble green gain 1662306a36Sopenharmony_cisn9c103: 1762306a36Sopenharmony_ci0x05 red gain 0-127 1862306a36Sopenharmony_ci0x06 blue gain 0-127 1962306a36Sopenharmony_ci0x07 green gain 0-127 2062306a36Sopenharmony_ciall: 2162306a36Sopenharmony_ci0x08-0x0f i2c / 3wire registers 2262306a36Sopenharmony_ci0x12 hstart 2362306a36Sopenharmony_ci0x13 vstart 2462306a36Sopenharmony_ci0x15 hsize (hsize = register-value * 16) 2562306a36Sopenharmony_ci0x16 vsize (vsize = register-value * 16) 2662306a36Sopenharmony_ci0x17 bit 0 toggle compression quality (according to sn9c102 driver) 2762306a36Sopenharmony_ci0x18 bit 7 enables compression, bit 4-5 set image down scaling: 2862306a36Sopenharmony_ci 00 scale 1, 01 scale 1/2, 10, scale 1/4 2962306a36Sopenharmony_ci0x19 high-nibble is sensor clock divider, changes exposure on sensors which 3062306a36Sopenharmony_ci use a clock generated by the bridge. Some sensors have their own clock. 3162306a36Sopenharmony_ci0x1c auto_exposure area (for avg_lum) startx (startx = register-value * 32) 3262306a36Sopenharmony_ci0x1d auto_exposure area (for avg_lum) starty (starty = register-value * 32) 3362306a36Sopenharmony_ci0x1e auto_exposure area (for avg_lum) stopx (hsize = (0x1e - 0x1c) * 32) 3462306a36Sopenharmony_ci0x1f auto_exposure area (for avg_lum) stopy (vsize = (0x1f - 0x1d) * 32) 3562306a36Sopenharmony_ci*/ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define MODULE_NAME "sonixb" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <linux/input.h> 4062306a36Sopenharmony_ci#include "gspca.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciMODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>"); 4362306a36Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); 4462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* specific webcam descriptor */ 4762306a36Sopenharmony_cistruct sd { 4862306a36Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci struct v4l2_ctrl *brightness; 5162306a36Sopenharmony_ci struct v4l2_ctrl *plfreq; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci atomic_t avg_lum; 5462306a36Sopenharmony_ci int prev_avg_lum; 5562306a36Sopenharmony_ci int exposure_knee; 5662306a36Sopenharmony_ci int header_read; 5762306a36Sopenharmony_ci u8 header[12]; /* Header without sof marker */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci unsigned char autogain_ignore_frames; 6062306a36Sopenharmony_ci unsigned char frames_to_drop; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci __u8 bridge; /* Type of bridge */ 6362306a36Sopenharmony_ci#define BRIDGE_101 0 6462306a36Sopenharmony_ci#define BRIDGE_102 0 /* We make no difference between 101 and 102 */ 6562306a36Sopenharmony_ci#define BRIDGE_103 1 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci __u8 sensor; /* Type of image sensor chip */ 6862306a36Sopenharmony_ci#define SENSOR_HV7131D 0 6962306a36Sopenharmony_ci#define SENSOR_HV7131R 1 7062306a36Sopenharmony_ci#define SENSOR_OV6650 2 7162306a36Sopenharmony_ci#define SENSOR_OV7630 3 7262306a36Sopenharmony_ci#define SENSOR_PAS106 4 7362306a36Sopenharmony_ci#define SENSOR_PAS202 5 7462306a36Sopenharmony_ci#define SENSOR_TAS5110C 6 7562306a36Sopenharmony_ci#define SENSOR_TAS5110D 7 7662306a36Sopenharmony_ci#define SENSOR_TAS5130CXX 8 7762306a36Sopenharmony_ci __u8 reg11; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_citypedef const __u8 sensor_init_t[8]; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistruct sensor_data { 8362306a36Sopenharmony_ci const __u8 *bridge_init; 8462306a36Sopenharmony_ci sensor_init_t *sensor_init; 8562306a36Sopenharmony_ci int sensor_init_size; 8662306a36Sopenharmony_ci int flags; 8762306a36Sopenharmony_ci __u8 sensor_addr; 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* sensor_data flags */ 9162306a36Sopenharmony_ci#define F_SIF 0x01 /* sif or vga */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */ 9462306a36Sopenharmony_ci#define MODE_RAW 0x10 /* raw bayer mode */ 9562306a36Sopenharmony_ci#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define COMP 0xc7 /* 0x87 //0x07 */ 9862306a36Sopenharmony_ci#define COMP1 0xc9 /* 0x89 //0x09 */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define MCK_INIT 0x63 10162306a36Sopenharmony_ci#define MCK_INIT1 0x20 /*fixme: Bayer - 0x50 for JPEG ??*/ 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define SYS_CLK 0x04 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define SENS(bridge, sensor, _flags, _sensor_addr) \ 10662306a36Sopenharmony_ci{ \ 10762306a36Sopenharmony_ci .bridge_init = bridge, \ 10862306a36Sopenharmony_ci .sensor_init = sensor, \ 10962306a36Sopenharmony_ci .sensor_init_size = sizeof(sensor), \ 11062306a36Sopenharmony_ci .flags = _flags, .sensor_addr = _sensor_addr \ 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* We calculate the autogain at the end of the transfer of a frame, at this 11462306a36Sopenharmony_ci moment a frame with the old settings is being captured and transmitted. So 11562306a36Sopenharmony_ci if we adjust the gain or exposure we must ignore at least the next frame for 11662306a36Sopenharmony_ci the new settings to come into effect before doing any other adjustments. */ 11762306a36Sopenharmony_ci#define AUTOGAIN_IGNORE_FRAMES 1 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = { 12062306a36Sopenharmony_ci {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, 12162306a36Sopenharmony_ci .bytesperline = 160, 12262306a36Sopenharmony_ci .sizeimage = 160 * 120, 12362306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 12462306a36Sopenharmony_ci .priv = 2 | MODE_RAW}, 12562306a36Sopenharmony_ci {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, 12662306a36Sopenharmony_ci .bytesperline = 160, 12762306a36Sopenharmony_ci .sizeimage = 160 * 120 * 5 / 4, 12862306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 12962306a36Sopenharmony_ci .priv = 2}, 13062306a36Sopenharmony_ci {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, 13162306a36Sopenharmony_ci .bytesperline = 320, 13262306a36Sopenharmony_ci .sizeimage = 320 * 240 * 5 / 4, 13362306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 13462306a36Sopenharmony_ci .priv = 1}, 13562306a36Sopenharmony_ci {640, 480, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, 13662306a36Sopenharmony_ci .bytesperline = 640, 13762306a36Sopenharmony_ci .sizeimage = 640 * 480 * 5 / 4, 13862306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 13962306a36Sopenharmony_ci .priv = 0}, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_cistatic const struct v4l2_pix_format sif_mode[] = { 14262306a36Sopenharmony_ci {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, 14362306a36Sopenharmony_ci .bytesperline = 160, 14462306a36Sopenharmony_ci .sizeimage = 160 * 120, 14562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 14662306a36Sopenharmony_ci .priv = 1 | MODE_RAW | MODE_REDUCED_SIF}, 14762306a36Sopenharmony_ci {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, 14862306a36Sopenharmony_ci .bytesperline = 160, 14962306a36Sopenharmony_ci .sizeimage = 160 * 120 * 5 / 4, 15062306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 15162306a36Sopenharmony_ci .priv = 1 | MODE_REDUCED_SIF}, 15262306a36Sopenharmony_ci {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, 15362306a36Sopenharmony_ci .bytesperline = 176, 15462306a36Sopenharmony_ci .sizeimage = 176 * 144, 15562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 15662306a36Sopenharmony_ci .priv = 1 | MODE_RAW}, 15762306a36Sopenharmony_ci {176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, 15862306a36Sopenharmony_ci .bytesperline = 176, 15962306a36Sopenharmony_ci .sizeimage = 176 * 144 * 5 / 4, 16062306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 16162306a36Sopenharmony_ci .priv = 1}, 16262306a36Sopenharmony_ci {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, 16362306a36Sopenharmony_ci .bytesperline = 320, 16462306a36Sopenharmony_ci .sizeimage = 320 * 240 * 5 / 4, 16562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 16662306a36Sopenharmony_ci .priv = 0 | MODE_REDUCED_SIF}, 16762306a36Sopenharmony_ci {352, 288, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, 16862306a36Sopenharmony_ci .bytesperline = 352, 16962306a36Sopenharmony_ci .sizeimage = 352 * 288 * 5 / 4, 17062306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 17162306a36Sopenharmony_ci .priv = 0}, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic const __u8 initHv7131d[] = { 17562306a36Sopenharmony_ci 0x04, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, 17662306a36Sopenharmony_ci 0x00, 0x00, 17762306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 17862306a36Sopenharmony_ci 0x28, 0x1e, 0x60, 0x8e, 0x42, 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_cistatic const __u8 hv7131d_sensor_init[][8] = { 18162306a36Sopenharmony_ci {0xa0, 0x11, 0x01, 0x04, 0x00, 0x00, 0x00, 0x17}, 18262306a36Sopenharmony_ci {0xa0, 0x11, 0x02, 0x00, 0x00, 0x00, 0x00, 0x17}, 18362306a36Sopenharmony_ci {0xa0, 0x11, 0x28, 0x00, 0x00, 0x00, 0x00, 0x17}, 18462306a36Sopenharmony_ci {0xa0, 0x11, 0x30, 0x30, 0x00, 0x00, 0x00, 0x17}, /* reset level */ 18562306a36Sopenharmony_ci {0xa0, 0x11, 0x34, 0x02, 0x00, 0x00, 0x00, 0x17}, /* pixel bias volt */ 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic const __u8 initHv7131r[] = { 18962306a36Sopenharmony_ci 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, 19062306a36Sopenharmony_ci 0x00, 0x00, 19162306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 19262306a36Sopenharmony_ci 0x28, 0x1e, 0x60, 0x8a, 0x20, 19362306a36Sopenharmony_ci}; 19462306a36Sopenharmony_cistatic const __u8 hv7131r_sensor_init[][8] = { 19562306a36Sopenharmony_ci {0xc0, 0x11, 0x31, 0x38, 0x2a, 0x2e, 0x00, 0x10}, 19662306a36Sopenharmony_ci {0xa0, 0x11, 0x01, 0x08, 0x2a, 0x2e, 0x00, 0x10}, 19762306a36Sopenharmony_ci {0xb0, 0x11, 0x20, 0x00, 0xd0, 0x2e, 0x00, 0x10}, 19862306a36Sopenharmony_ci {0xc0, 0x11, 0x25, 0x03, 0x0e, 0x28, 0x00, 0x16}, 19962306a36Sopenharmony_ci {0xa0, 0x11, 0x30, 0x10, 0x0e, 0x28, 0x00, 0x15}, 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_cistatic const __u8 initOv6650[] = { 20262306a36Sopenharmony_ci 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 20362306a36Sopenharmony_ci 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20462306a36Sopenharmony_ci 0x00, 0x01, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x8b, 20562306a36Sopenharmony_ci 0x10, 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_cistatic const __u8 ov6650_sensor_init[][8] = { 20862306a36Sopenharmony_ci /* Bright, contrast, etc are set through SCBB interface. 20962306a36Sopenharmony_ci * AVCAP on win2 do not send any data on this controls. */ 21062306a36Sopenharmony_ci /* Anyway, some registers appears to alter bright and constrat */ 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Reset sensor */ 21362306a36Sopenharmony_ci {0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, 21462306a36Sopenharmony_ci /* Set clock register 0x11 low nibble is clock divider */ 21562306a36Sopenharmony_ci {0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10}, 21662306a36Sopenharmony_ci /* Next some unknown stuff */ 21762306a36Sopenharmony_ci {0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10}, 21862306a36Sopenharmony_ci/* {0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10}, 21962306a36Sopenharmony_ci * THIS SET GREEN SCREEN 22062306a36Sopenharmony_ci * (pixels could be innverted in decode kind of "brg", 22162306a36Sopenharmony_ci * but blue wont be there. Avoid this data ... */ 22262306a36Sopenharmony_ci {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */ 22362306a36Sopenharmony_ci {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, 22462306a36Sopenharmony_ci {0xa0, 0x60, 0x30, 0x3d, 0x0a, 0xd8, 0xa4, 0x10}, 22562306a36Sopenharmony_ci /* Enable rgb brightness control */ 22662306a36Sopenharmony_ci {0xa0, 0x60, 0x61, 0x08, 0x00, 0x00, 0x00, 0x10}, 22762306a36Sopenharmony_ci /* HDG: Note windows uses the line below, which sets both register 0x60 22862306a36Sopenharmony_ci and 0x61 I believe these registers of the ov6650 are identical as 22962306a36Sopenharmony_ci those of the ov7630, because if this is true the windows settings 23062306a36Sopenharmony_ci add a bit additional red gain and a lot additional blue gain, which 23162306a36Sopenharmony_ci matches my findings that the windows settings make blue much too 23262306a36Sopenharmony_ci blue and red a little too red. 23362306a36Sopenharmony_ci {0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10}, */ 23462306a36Sopenharmony_ci /* Some more unknown stuff */ 23562306a36Sopenharmony_ci {0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10}, 23662306a36Sopenharmony_ci {0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */ 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic const __u8 initOv7630[] = { 24062306a36Sopenharmony_ci 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */ 24162306a36Sopenharmony_ci 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ 24262306a36Sopenharmony_ci 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ 24362306a36Sopenharmony_ci 0x28, 0x1e, /* H & V sizes r15 .. r16 */ 24462306a36Sopenharmony_ci 0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */ 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_cistatic const __u8 ov7630_sensor_init[][8] = { 24762306a36Sopenharmony_ci {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, 24862306a36Sopenharmony_ci {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, 24962306a36Sopenharmony_ci/* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */ 25062306a36Sopenharmony_ci {0xd0, 0x21, 0x12, 0x5c, 0x00, 0x80, 0x34, 0x10}, /* jfm */ 25162306a36Sopenharmony_ci {0xa0, 0x21, 0x1b, 0x04, 0x00, 0x80, 0x34, 0x10}, 25262306a36Sopenharmony_ci {0xa0, 0x21, 0x20, 0x44, 0x00, 0x80, 0x34, 0x10}, 25362306a36Sopenharmony_ci {0xa0, 0x21, 0x23, 0xee, 0x00, 0x80, 0x34, 0x10}, 25462306a36Sopenharmony_ci {0xd0, 0x21, 0x26, 0xa0, 0x9a, 0xa0, 0x30, 0x10}, 25562306a36Sopenharmony_ci {0xb0, 0x21, 0x2a, 0x80, 0x00, 0xa0, 0x30, 0x10}, 25662306a36Sopenharmony_ci {0xb0, 0x21, 0x2f, 0x3d, 0x24, 0xa0, 0x30, 0x10}, 25762306a36Sopenharmony_ci {0xa0, 0x21, 0x32, 0x86, 0x24, 0xa0, 0x30, 0x10}, 25862306a36Sopenharmony_ci {0xb0, 0x21, 0x60, 0xa9, 0x4a, 0xa0, 0x30, 0x10}, 25962306a36Sopenharmony_ci/* {0xb0, 0x21, 0x60, 0xa9, 0x42, 0xa0, 0x30, 0x10}, * jfm */ 26062306a36Sopenharmony_ci {0xa0, 0x21, 0x65, 0x00, 0x42, 0xa0, 0x30, 0x10}, 26162306a36Sopenharmony_ci {0xa0, 0x21, 0x69, 0x38, 0x42, 0xa0, 0x30, 0x10}, 26262306a36Sopenharmony_ci {0xc0, 0x21, 0x6f, 0x88, 0x0b, 0x00, 0x30, 0x10}, 26362306a36Sopenharmony_ci {0xc0, 0x21, 0x74, 0x21, 0x8e, 0x00, 0x30, 0x10}, 26462306a36Sopenharmony_ci {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10}, 26562306a36Sopenharmony_ci {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic const __u8 initPas106[] = { 26962306a36Sopenharmony_ci 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, 27062306a36Sopenharmony_ci 0x00, 0x00, 27162306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 27262306a36Sopenharmony_ci 0x16, 0x12, 0x24, COMP1, MCK_INIT1, 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci/* compression 0x86 mckinit1 0x2b */ 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/* "Known" PAS106B registers: 27762306a36Sopenharmony_ci 0x02 clock divider 27862306a36Sopenharmony_ci 0x03 Variable framerate bits 4-11 27962306a36Sopenharmony_ci 0x04 Var framerate bits 0-3, one must leave the 4 msb's at 0 !! 28062306a36Sopenharmony_ci The variable framerate control must never be set lower then 300, 28162306a36Sopenharmony_ci which sets the framerate at 90 / reg02, otherwise vsync is lost. 28262306a36Sopenharmony_ci 0x05 Shutter Time Line Offset, this can be used as an exposure control: 28362306a36Sopenharmony_ci 0 = use full frame time, 255 = no exposure at all 28462306a36Sopenharmony_ci Note this may never be larger then "var-framerate control" / 2 - 2. 28562306a36Sopenharmony_ci When var-framerate control is < 514, no exposure is reached at the max 28662306a36Sopenharmony_ci allowed value for the framerate control value, rather then at 255. 28762306a36Sopenharmony_ci 0x06 Shutter Time Pixel Offset, like reg05 this influences exposure, but 28862306a36Sopenharmony_ci only a very little bit, leave at 0xcd 28962306a36Sopenharmony_ci 0x07 offset sign bit (bit0 1 > negative offset) 29062306a36Sopenharmony_ci 0x08 offset 29162306a36Sopenharmony_ci 0x09 Blue Gain 29262306a36Sopenharmony_ci 0x0a Green1 Gain 29362306a36Sopenharmony_ci 0x0b Green2 Gain 29462306a36Sopenharmony_ci 0x0c Red Gain 29562306a36Sopenharmony_ci 0x0e Global gain 29662306a36Sopenharmony_ci 0x13 Write 1 to commit settings to sensor 29762306a36Sopenharmony_ci*/ 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic const __u8 pas106_sensor_init[][8] = { 30062306a36Sopenharmony_ci /* Pixel Clock Divider 6 */ 30162306a36Sopenharmony_ci { 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 }, 30262306a36Sopenharmony_ci /* Frame Time MSB (also seen as 0x12) */ 30362306a36Sopenharmony_ci { 0xa1, 0x40, 0x03, 0x13, 0x00, 0x00, 0x00, 0x14 }, 30462306a36Sopenharmony_ci /* Frame Time LSB (also seen as 0x05) */ 30562306a36Sopenharmony_ci { 0xa1, 0x40, 0x04, 0x06, 0x00, 0x00, 0x00, 0x14 }, 30662306a36Sopenharmony_ci /* Shutter Time Line Offset (also seen as 0x6d) */ 30762306a36Sopenharmony_ci { 0xa1, 0x40, 0x05, 0x65, 0x00, 0x00, 0x00, 0x14 }, 30862306a36Sopenharmony_ci /* Shutter Time Pixel Offset (also seen as 0xb1) */ 30962306a36Sopenharmony_ci { 0xa1, 0x40, 0x06, 0xcd, 0x00, 0x00, 0x00, 0x14 }, 31062306a36Sopenharmony_ci /* Black Level Subtract Sign (also seen 0x00) */ 31162306a36Sopenharmony_ci { 0xa1, 0x40, 0x07, 0xc1, 0x00, 0x00, 0x00, 0x14 }, 31262306a36Sopenharmony_ci /* Black Level Subtract Level (also seen 0x01) */ 31362306a36Sopenharmony_ci { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 }, 31462306a36Sopenharmony_ci { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 }, 31562306a36Sopenharmony_ci /* Color Gain B Pixel 5 a */ 31662306a36Sopenharmony_ci { 0xa1, 0x40, 0x09, 0x05, 0x00, 0x00, 0x00, 0x14 }, 31762306a36Sopenharmony_ci /* Color Gain G1 Pixel 1 5 */ 31862306a36Sopenharmony_ci { 0xa1, 0x40, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x14 }, 31962306a36Sopenharmony_ci /* Color Gain G2 Pixel 1 0 5 */ 32062306a36Sopenharmony_ci { 0xa1, 0x40, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x14 }, 32162306a36Sopenharmony_ci /* Color Gain R Pixel 3 1 */ 32262306a36Sopenharmony_ci { 0xa1, 0x40, 0x0c, 0x05, 0x00, 0x00, 0x00, 0x14 }, 32362306a36Sopenharmony_ci /* Color GainH Pixel */ 32462306a36Sopenharmony_ci { 0xa1, 0x40, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x14 }, 32562306a36Sopenharmony_ci /* Global Gain */ 32662306a36Sopenharmony_ci { 0xa1, 0x40, 0x0e, 0x0e, 0x00, 0x00, 0x00, 0x14 }, 32762306a36Sopenharmony_ci /* Contrast */ 32862306a36Sopenharmony_ci { 0xa1, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14 }, 32962306a36Sopenharmony_ci /* H&V synchro polarity */ 33062306a36Sopenharmony_ci { 0xa1, 0x40, 0x10, 0x06, 0x00, 0x00, 0x00, 0x14 }, 33162306a36Sopenharmony_ci /* ?default */ 33262306a36Sopenharmony_ci { 0xa1, 0x40, 0x11, 0x06, 0x00, 0x00, 0x00, 0x14 }, 33362306a36Sopenharmony_ci /* DAC scale */ 33462306a36Sopenharmony_ci { 0xa1, 0x40, 0x12, 0x06, 0x00, 0x00, 0x00, 0x14 }, 33562306a36Sopenharmony_ci /* ?default */ 33662306a36Sopenharmony_ci { 0xa1, 0x40, 0x14, 0x02, 0x00, 0x00, 0x00, 0x14 }, 33762306a36Sopenharmony_ci /* Validate Settings */ 33862306a36Sopenharmony_ci { 0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14 }, 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic const __u8 initPas202[] = { 34262306a36Sopenharmony_ci 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 34362306a36Sopenharmony_ci 0x00, 0x00, 34462306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a, 34562306a36Sopenharmony_ci 0x28, 0x1e, 0x20, 0x89, 0x20, 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/* "Known" PAS202BCB registers: 34962306a36Sopenharmony_ci 0x02 clock divider 35062306a36Sopenharmony_ci 0x04 Variable framerate bits 6-11 (*) 35162306a36Sopenharmony_ci 0x05 Var framerate bits 0-5, one must leave the 2 msb's at 0 !! 35262306a36Sopenharmony_ci 0x07 Blue Gain 35362306a36Sopenharmony_ci 0x08 Green Gain 35462306a36Sopenharmony_ci 0x09 Red Gain 35562306a36Sopenharmony_ci 0x0b offset sign bit (bit0 1 > negative offset) 35662306a36Sopenharmony_ci 0x0c offset 35762306a36Sopenharmony_ci 0x0e Unknown image is slightly brighter when bit 0 is 0, if reg0f is 0 too, 35862306a36Sopenharmony_ci leave at 1 otherwise we get a jump in our exposure control 35962306a36Sopenharmony_ci 0x0f Exposure 0-255, 0 = use full frame time, 255 = no exposure at all 36062306a36Sopenharmony_ci 0x10 Master gain 0 - 31 36162306a36Sopenharmony_ci 0x11 write 1 to apply changes 36262306a36Sopenharmony_ci (*) The variable framerate control must never be set lower then 500 36362306a36Sopenharmony_ci which sets the framerate at 30 / reg02, otherwise vsync is lost. 36462306a36Sopenharmony_ci*/ 36562306a36Sopenharmony_cistatic const __u8 pas202_sensor_init[][8] = { 36662306a36Sopenharmony_ci /* Set the clock divider to 4 -> 30 / 4 = 7.5 fps, we would like 36762306a36Sopenharmony_ci to set it lower, but for some reason the bridge starts missing 36862306a36Sopenharmony_ci vsync's then */ 36962306a36Sopenharmony_ci {0xa0, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x10}, 37062306a36Sopenharmony_ci {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, 37162306a36Sopenharmony_ci {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, 37262306a36Sopenharmony_ci {0xd0, 0x40, 0x0c, 0x00, 0x0c, 0x01, 0x32, 0x10}, 37362306a36Sopenharmony_ci {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, 37462306a36Sopenharmony_ci {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, 37562306a36Sopenharmony_ci {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, 37662306a36Sopenharmony_ci {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, 37762306a36Sopenharmony_ci {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, 37862306a36Sopenharmony_ci {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, 37962306a36Sopenharmony_ci}; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic const __u8 initTas5110c[] = { 38262306a36Sopenharmony_ci 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 38362306a36Sopenharmony_ci 0x00, 0x00, 38462306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x45, 0x09, 0x0a, 38562306a36Sopenharmony_ci 0x16, 0x12, 0x60, 0x86, 0x2b, 38662306a36Sopenharmony_ci}; 38762306a36Sopenharmony_ci/* Same as above, except a different hstart */ 38862306a36Sopenharmony_cistatic const __u8 initTas5110d[] = { 38962306a36Sopenharmony_ci 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 39062306a36Sopenharmony_ci 0x00, 0x00, 39162306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x41, 0x09, 0x0a, 39262306a36Sopenharmony_ci 0x16, 0x12, 0x60, 0x86, 0x2b, 39362306a36Sopenharmony_ci}; 39462306a36Sopenharmony_ci/* tas5110c is 3 wire, tas5110d is 2 wire (regular i2c) */ 39562306a36Sopenharmony_cistatic const __u8 tas5110c_sensor_init[][8] = { 39662306a36Sopenharmony_ci {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10}, 39762306a36Sopenharmony_ci {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10}, 39862306a36Sopenharmony_ci}; 39962306a36Sopenharmony_ci/* Known TAS5110D registers 40062306a36Sopenharmony_ci * reg02: gain, bit order reversed!! 0 == max gain, 255 == min gain 40162306a36Sopenharmony_ci * reg03: bit3: vflip, bit4: ~hflip, bit7: ~gainboost (~ == inverted) 40262306a36Sopenharmony_ci * Note: writing reg03 seems to only work when written together with 02 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_cistatic const __u8 tas5110d_sensor_init[][8] = { 40562306a36Sopenharmony_ci {0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17}, /* reset */ 40662306a36Sopenharmony_ci}; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic const __u8 initTas5130[] = { 40962306a36Sopenharmony_ci 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 41062306a36Sopenharmony_ci 0x00, 0x00, 41162306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x68, 0x0c, 0x0a, 41262306a36Sopenharmony_ci 0x28, 0x1e, 0x60, COMP, MCK_INIT, 41362306a36Sopenharmony_ci}; 41462306a36Sopenharmony_cistatic const __u8 tas5130_sensor_init[][8] = { 41562306a36Sopenharmony_ci/* {0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10}, 41662306a36Sopenharmony_ci * shutter 0x47 short exposure? */ 41762306a36Sopenharmony_ci {0x30, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x10}, 41862306a36Sopenharmony_ci /* shutter 0x01 long exposure */ 41962306a36Sopenharmony_ci {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}, 42062306a36Sopenharmony_ci}; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic const struct sensor_data sensor_data[] = { 42362306a36Sopenharmony_ci SENS(initHv7131d, hv7131d_sensor_init, 0, 0), 42462306a36Sopenharmony_ci SENS(initHv7131r, hv7131r_sensor_init, 0, 0), 42562306a36Sopenharmony_ci SENS(initOv6650, ov6650_sensor_init, F_SIF, 0x60), 42662306a36Sopenharmony_ci SENS(initOv7630, ov7630_sensor_init, 0, 0x21), 42762306a36Sopenharmony_ci SENS(initPas106, pas106_sensor_init, F_SIF, 0), 42862306a36Sopenharmony_ci SENS(initPas202, pas202_sensor_init, 0, 0), 42962306a36Sopenharmony_ci SENS(initTas5110c, tas5110c_sensor_init, F_SIF, 0), 43062306a36Sopenharmony_ci SENS(initTas5110d, tas5110d_sensor_init, F_SIF, 0), 43162306a36Sopenharmony_ci SENS(initTas5130, tas5130_sensor_init, 0, 0), 43262306a36Sopenharmony_ci}; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* get one byte in gspca_dev->usb_buf */ 43562306a36Sopenharmony_cistatic void reg_r(struct gspca_dev *gspca_dev, 43662306a36Sopenharmony_ci __u16 value) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int res; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (gspca_dev->usb_err < 0) 44162306a36Sopenharmony_ci return; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci res = usb_control_msg(gspca_dev->dev, 44462306a36Sopenharmony_ci usb_rcvctrlpipe(gspca_dev->dev, 0), 44562306a36Sopenharmony_ci 0, /* request */ 44662306a36Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 44762306a36Sopenharmony_ci value, 44862306a36Sopenharmony_ci 0, /* index */ 44962306a36Sopenharmony_ci gspca_dev->usb_buf, 1, 45062306a36Sopenharmony_ci 500); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (res < 0) { 45362306a36Sopenharmony_ci dev_err(gspca_dev->v4l2_dev.dev, 45462306a36Sopenharmony_ci "Error reading register %02x: %d\n", value, res); 45562306a36Sopenharmony_ci gspca_dev->usb_err = res; 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * Make sure the result is zeroed to avoid uninitialized 45862306a36Sopenharmony_ci * values. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci gspca_dev->usb_buf[0] = 0; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic void reg_w(struct gspca_dev *gspca_dev, 46562306a36Sopenharmony_ci __u16 value, 46662306a36Sopenharmony_ci const __u8 *buffer, 46762306a36Sopenharmony_ci int len) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci int res; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (gspca_dev->usb_err < 0) 47262306a36Sopenharmony_ci return; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci memcpy(gspca_dev->usb_buf, buffer, len); 47562306a36Sopenharmony_ci res = usb_control_msg(gspca_dev->dev, 47662306a36Sopenharmony_ci usb_sndctrlpipe(gspca_dev->dev, 0), 47762306a36Sopenharmony_ci 0x08, /* request */ 47862306a36Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 47962306a36Sopenharmony_ci value, 48062306a36Sopenharmony_ci 0, /* index */ 48162306a36Sopenharmony_ci gspca_dev->usb_buf, len, 48262306a36Sopenharmony_ci 500); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (res < 0) { 48562306a36Sopenharmony_ci dev_err(gspca_dev->v4l2_dev.dev, 48662306a36Sopenharmony_ci "Error writing register %02x: %d\n", value, res); 48762306a36Sopenharmony_ci gspca_dev->usb_err = res; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic void i2c_w(struct gspca_dev *gspca_dev, const u8 *buf) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci int retry = 60; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (gspca_dev->usb_err < 0) 49662306a36Sopenharmony_ci return; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* is i2c ready */ 49962306a36Sopenharmony_ci reg_w(gspca_dev, 0x08, buf, 8); 50062306a36Sopenharmony_ci while (retry--) { 50162306a36Sopenharmony_ci if (gspca_dev->usb_err < 0) 50262306a36Sopenharmony_ci return; 50362306a36Sopenharmony_ci msleep(1); 50462306a36Sopenharmony_ci reg_r(gspca_dev, 0x08); 50562306a36Sopenharmony_ci if (gspca_dev->usb_buf[0] & 0x04) { 50662306a36Sopenharmony_ci if (gspca_dev->usb_buf[0] & 0x08) { 50762306a36Sopenharmony_ci dev_err(gspca_dev->v4l2_dev.dev, 50862306a36Sopenharmony_ci "i2c error writing %8ph\n", buf); 50962306a36Sopenharmony_ci gspca_dev->usb_err = -EIO; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci dev_err(gspca_dev->v4l2_dev.dev, "i2c write timeout\n"); 51662306a36Sopenharmony_ci gspca_dev->usb_err = -EIO; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void i2c_w_vector(struct gspca_dev *gspca_dev, 52062306a36Sopenharmony_ci const __u8 buffer[][8], int len) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci for (;;) { 52362306a36Sopenharmony_ci if (gspca_dev->usb_err < 0) 52462306a36Sopenharmony_ci return; 52562306a36Sopenharmony_ci i2c_w(gspca_dev, *buffer); 52662306a36Sopenharmony_ci len -= 8; 52762306a36Sopenharmony_ci if (len <= 0) 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci buffer++; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void setbrightness(struct gspca_dev *gspca_dev) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci switch (sd->sensor) { 53862306a36Sopenharmony_ci case SENSOR_OV6650: 53962306a36Sopenharmony_ci case SENSOR_OV7630: { 54062306a36Sopenharmony_ci __u8 i2cOV[] = 54162306a36Sopenharmony_ci {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* change reg 0x06 */ 54462306a36Sopenharmony_ci i2cOV[1] = sensor_data[sd->sensor].sensor_addr; 54562306a36Sopenharmony_ci i2cOV[3] = sd->brightness->val; 54662306a36Sopenharmony_ci i2c_w(gspca_dev, i2cOV); 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci case SENSOR_PAS106: 55062306a36Sopenharmony_ci case SENSOR_PAS202: { 55162306a36Sopenharmony_ci __u8 i2cpbright[] = 55262306a36Sopenharmony_ci {0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16}; 55362306a36Sopenharmony_ci __u8 i2cpdoit[] = 55462306a36Sopenharmony_ci {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* PAS106 uses reg 7 and 8 instead of b and c */ 55762306a36Sopenharmony_ci if (sd->sensor == SENSOR_PAS106) { 55862306a36Sopenharmony_ci i2cpbright[2] = 7; 55962306a36Sopenharmony_ci i2cpdoit[2] = 0x13; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (sd->brightness->val < 127) { 56362306a36Sopenharmony_ci /* change reg 0x0b, signreg */ 56462306a36Sopenharmony_ci i2cpbright[3] = 0x01; 56562306a36Sopenharmony_ci /* set reg 0x0c, offset */ 56662306a36Sopenharmony_ci i2cpbright[4] = 127 - sd->brightness->val; 56762306a36Sopenharmony_ci } else 56862306a36Sopenharmony_ci i2cpbright[4] = sd->brightness->val - 127; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpbright); 57162306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpdoit); 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci default: 57562306a36Sopenharmony_ci break; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic void setgain(struct gspca_dev *gspca_dev) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 58262306a36Sopenharmony_ci u8 gain = gspca_dev->gain->val; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci switch (sd->sensor) { 58562306a36Sopenharmony_ci case SENSOR_HV7131D: { 58662306a36Sopenharmony_ci __u8 i2c[] = 58762306a36Sopenharmony_ci {0xc0, 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x17}; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci i2c[3] = 0x3f - gain; 59062306a36Sopenharmony_ci i2c[4] = 0x3f - gain; 59162306a36Sopenharmony_ci i2c[5] = 0x3f - gain; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci i2c_w(gspca_dev, i2c); 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci case SENSOR_TAS5110C: 59762306a36Sopenharmony_ci case SENSOR_TAS5130CXX: { 59862306a36Sopenharmony_ci __u8 i2c[] = 59962306a36Sopenharmony_ci {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci i2c[4] = 255 - gain; 60262306a36Sopenharmony_ci i2c_w(gspca_dev, i2c); 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci case SENSOR_TAS5110D: { 60662306a36Sopenharmony_ci __u8 i2c[] = { 60762306a36Sopenharmony_ci 0xb0, 0x61, 0x02, 0x00, 0x10, 0x00, 0x00, 0x17 }; 60862306a36Sopenharmony_ci gain = 255 - gain; 60962306a36Sopenharmony_ci /* The bits in the register are the wrong way around!! */ 61062306a36Sopenharmony_ci i2c[3] |= (gain & 0x80) >> 7; 61162306a36Sopenharmony_ci i2c[3] |= (gain & 0x40) >> 5; 61262306a36Sopenharmony_ci i2c[3] |= (gain & 0x20) >> 3; 61362306a36Sopenharmony_ci i2c[3] |= (gain & 0x10) >> 1; 61462306a36Sopenharmony_ci i2c[3] |= (gain & 0x08) << 1; 61562306a36Sopenharmony_ci i2c[3] |= (gain & 0x04) << 3; 61662306a36Sopenharmony_ci i2c[3] |= (gain & 0x02) << 5; 61762306a36Sopenharmony_ci i2c[3] |= (gain & 0x01) << 7; 61862306a36Sopenharmony_ci i2c_w(gspca_dev, i2c); 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci case SENSOR_OV6650: 62262306a36Sopenharmony_ci case SENSOR_OV7630: { 62362306a36Sopenharmony_ci __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* 62662306a36Sopenharmony_ci * The ov7630's gain is weird, at 32 the gain drops to the 62762306a36Sopenharmony_ci * same level as at 16, so skip 32-47 (of the 0-63 scale). 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci if (sd->sensor == SENSOR_OV7630 && gain >= 32) 63062306a36Sopenharmony_ci gain += 16; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci i2c[1] = sensor_data[sd->sensor].sensor_addr; 63362306a36Sopenharmony_ci i2c[3] = gain; 63462306a36Sopenharmony_ci i2c_w(gspca_dev, i2c); 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci case SENSOR_PAS106: 63862306a36Sopenharmony_ci case SENSOR_PAS202: { 63962306a36Sopenharmony_ci __u8 i2cpgain[] = 64062306a36Sopenharmony_ci {0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15}; 64162306a36Sopenharmony_ci __u8 i2cpcolorgain[] = 64262306a36Sopenharmony_ci {0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15}; 64362306a36Sopenharmony_ci __u8 i2cpdoit[] = 64462306a36Sopenharmony_ci {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* PAS106 uses different regs (and has split green gains) */ 64762306a36Sopenharmony_ci if (sd->sensor == SENSOR_PAS106) { 64862306a36Sopenharmony_ci i2cpgain[2] = 0x0e; 64962306a36Sopenharmony_ci i2cpcolorgain[0] = 0xd0; 65062306a36Sopenharmony_ci i2cpcolorgain[2] = 0x09; 65162306a36Sopenharmony_ci i2cpdoit[2] = 0x13; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci i2cpgain[3] = gain; 65562306a36Sopenharmony_ci i2cpcolorgain[3] = gain >> 1; 65662306a36Sopenharmony_ci i2cpcolorgain[4] = gain >> 1; 65762306a36Sopenharmony_ci i2cpcolorgain[5] = gain >> 1; 65862306a36Sopenharmony_ci i2cpcolorgain[6] = gain >> 1; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpgain); 66162306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpcolorgain); 66262306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpdoit); 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci default: 66662306a36Sopenharmony_ci if (sd->bridge == BRIDGE_103) { 66762306a36Sopenharmony_ci u8 buf[3] = { gain, gain, gain }; /* R, G, B */ 66862306a36Sopenharmony_ci reg_w(gspca_dev, 0x05, buf, 3); 66962306a36Sopenharmony_ci } else { 67062306a36Sopenharmony_ci u8 buf[2]; 67162306a36Sopenharmony_ci buf[0] = gain << 4 | gain; /* Red and blue */ 67262306a36Sopenharmony_ci buf[1] = gain; /* Green */ 67362306a36Sopenharmony_ci reg_w(gspca_dev, 0x10, buf, 2); 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci switch (sd->sensor) { 68362306a36Sopenharmony_ci case SENSOR_HV7131D: { 68462306a36Sopenharmony_ci /* Note the datasheet wrongly says line mode exposure uses reg 68562306a36Sopenharmony_ci 0x26 and 0x27, testing has shown 0x25 + 0x26 */ 68662306a36Sopenharmony_ci __u8 i2c[] = {0xc0, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x17}; 68762306a36Sopenharmony_ci u16 reg = gspca_dev->exposure->val; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci i2c[3] = reg >> 8; 69062306a36Sopenharmony_ci i2c[4] = reg & 0xff; 69162306a36Sopenharmony_ci i2c_w(gspca_dev, i2c); 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci case SENSOR_TAS5110C: 69562306a36Sopenharmony_ci case SENSOR_TAS5110D: { 69662306a36Sopenharmony_ci /* register 19's high nibble contains the sn9c10x clock divider 69762306a36Sopenharmony_ci The high nibble configures the no fps according to the 69862306a36Sopenharmony_ci formula: 60 / high_nibble. With a maximum of 30 fps */ 69962306a36Sopenharmony_ci u8 reg = gspca_dev->exposure->val; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci reg = (reg << 4) | 0x0b; 70262306a36Sopenharmony_ci reg_w(gspca_dev, 0x19, ®, 1); 70362306a36Sopenharmony_ci break; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci case SENSOR_OV6650: 70662306a36Sopenharmony_ci case SENSOR_OV7630: { 70762306a36Sopenharmony_ci /* The ov6650 / ov7630 have 2 registers which both influence 70862306a36Sopenharmony_ci exposure, register 11, whose low nibble sets the nr off fps 70962306a36Sopenharmony_ci according to: fps = 30 / (low_nibble + 1) 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci The fps configures the maximum exposure setting, but it is 71262306a36Sopenharmony_ci possible to use less exposure then what the fps maximum 71362306a36Sopenharmony_ci allows by setting register 10. register 10 configures the 71462306a36Sopenharmony_ci actual exposure as quotient of the full exposure, with 0 71562306a36Sopenharmony_ci being no exposure at all (not very useful) and reg10_max 71662306a36Sopenharmony_ci being max exposure possible at that framerate. 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci The code maps our 0 - 510 ms exposure ctrl to these 2 71962306a36Sopenharmony_ci registers, trying to keep fps as high as possible. 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ci __u8 i2c[] = {0xb0, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}; 72262306a36Sopenharmony_ci int reg10, reg11, reg10_max; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* ov6645 datasheet says reg10_max is 9a, but that uses 72562306a36Sopenharmony_ci tline * 2 * reg10 as formula for calculating texpo, the 72662306a36Sopenharmony_ci ov6650 probably uses the same formula as the 7730 which uses 72762306a36Sopenharmony_ci tline * 4 * reg10, which explains why the reg10max we've 72862306a36Sopenharmony_ci found experimentally for the ov6650 is exactly half that of 72962306a36Sopenharmony_ci the ov6645. The ov7630 datasheet says the max is 0x41. */ 73062306a36Sopenharmony_ci if (sd->sensor == SENSOR_OV6650) { 73162306a36Sopenharmony_ci reg10_max = 0x4d; 73262306a36Sopenharmony_ci i2c[4] = 0xc0; /* OV6650 needs non default vsync pol */ 73362306a36Sopenharmony_ci } else 73462306a36Sopenharmony_ci reg10_max = 0x41; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci reg11 = (15 * gspca_dev->exposure->val + 999) / 1000; 73762306a36Sopenharmony_ci if (reg11 < 1) 73862306a36Sopenharmony_ci reg11 = 1; 73962306a36Sopenharmony_ci else if (reg11 > 16) 74062306a36Sopenharmony_ci reg11 = 16; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* In 640x480, if the reg11 has less than 4, the image is 74362306a36Sopenharmony_ci unstable (the bridge goes into a higher compression mode 74462306a36Sopenharmony_ci which we have not reverse engineered yet). */ 74562306a36Sopenharmony_ci if (gspca_dev->pixfmt.width == 640 && reg11 < 4) 74662306a36Sopenharmony_ci reg11 = 4; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* frame exposure time in ms = 1000 * reg11 / 30 -> 74962306a36Sopenharmony_ci reg10 = (gspca_dev->exposure->val / 2) * reg10_max 75062306a36Sopenharmony_ci / (1000 * reg11 / 30) */ 75162306a36Sopenharmony_ci reg10 = (gspca_dev->exposure->val * 15 * reg10_max) 75262306a36Sopenharmony_ci / (1000 * reg11); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* Don't allow this to get below 10 when using autogain, the 75562306a36Sopenharmony_ci steps become very large (relatively) when below 10 causing 75662306a36Sopenharmony_ci the image to oscillate from much too dark, to much too bright 75762306a36Sopenharmony_ci and back again. */ 75862306a36Sopenharmony_ci if (gspca_dev->autogain->val && reg10 < 10) 75962306a36Sopenharmony_ci reg10 = 10; 76062306a36Sopenharmony_ci else if (reg10 > reg10_max) 76162306a36Sopenharmony_ci reg10 = reg10_max; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* Write reg 10 and reg11 low nibble */ 76462306a36Sopenharmony_ci i2c[1] = sensor_data[sd->sensor].sensor_addr; 76562306a36Sopenharmony_ci i2c[3] = reg10; 76662306a36Sopenharmony_ci i2c[4] |= reg11 - 1; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* If register 11 didn't change, don't change it */ 76962306a36Sopenharmony_ci if (sd->reg11 == reg11) 77062306a36Sopenharmony_ci i2c[0] = 0xa0; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci i2c_w(gspca_dev, i2c); 77362306a36Sopenharmony_ci if (gspca_dev->usb_err == 0) 77462306a36Sopenharmony_ci sd->reg11 = reg11; 77562306a36Sopenharmony_ci break; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci case SENSOR_PAS202: { 77862306a36Sopenharmony_ci __u8 i2cpframerate[] = 77962306a36Sopenharmony_ci {0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16}; 78062306a36Sopenharmony_ci __u8 i2cpexpo[] = 78162306a36Sopenharmony_ci {0xa0, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x16}; 78262306a36Sopenharmony_ci const __u8 i2cpdoit[] = 78362306a36Sopenharmony_ci {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; 78462306a36Sopenharmony_ci int framerate_ctrl; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* The exposure knee for the autogain algorithm is 200 78762306a36Sopenharmony_ci (100 ms / 10 fps on other sensors), for values below this 78862306a36Sopenharmony_ci use the control for setting the partial frame expose time, 78962306a36Sopenharmony_ci above that use variable framerate. This way we run at max 79062306a36Sopenharmony_ci framerate (640x480@7.5 fps, 320x240@10fps) until the knee 79162306a36Sopenharmony_ci is reached. Using the variable framerate control above 200 79262306a36Sopenharmony_ci is better then playing around with both clockdiv + partial 79362306a36Sopenharmony_ci frame exposure times (like we are doing with the ov chips), 79462306a36Sopenharmony_ci as that sometimes leads to jumps in the exposure control, 79562306a36Sopenharmony_ci which are bad for auto exposure. */ 79662306a36Sopenharmony_ci if (gspca_dev->exposure->val < 200) { 79762306a36Sopenharmony_ci i2cpexpo[3] = 255 - (gspca_dev->exposure->val * 255) 79862306a36Sopenharmony_ci / 200; 79962306a36Sopenharmony_ci framerate_ctrl = 500; 80062306a36Sopenharmony_ci } else { 80162306a36Sopenharmony_ci /* The PAS202's exposure control goes from 0 - 4095, 80262306a36Sopenharmony_ci but anything below 500 causes vsync issues, so scale 80362306a36Sopenharmony_ci our 200-1023 to 500-4095 */ 80462306a36Sopenharmony_ci framerate_ctrl = (gspca_dev->exposure->val - 200) 80562306a36Sopenharmony_ci * 1000 / 229 + 500; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci i2cpframerate[3] = framerate_ctrl >> 6; 80962306a36Sopenharmony_ci i2cpframerate[4] = framerate_ctrl & 0x3f; 81062306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpframerate); 81162306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpexpo); 81262306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpdoit); 81362306a36Sopenharmony_ci break; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci case SENSOR_PAS106: { 81662306a36Sopenharmony_ci __u8 i2cpframerate[] = 81762306a36Sopenharmony_ci {0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14}; 81862306a36Sopenharmony_ci __u8 i2cpexpo[] = 81962306a36Sopenharmony_ci {0xa1, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x14}; 82062306a36Sopenharmony_ci const __u8 i2cpdoit[] = 82162306a36Sopenharmony_ci {0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14}; 82262306a36Sopenharmony_ci int framerate_ctrl; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* For values below 150 use partial frame exposure, above 82562306a36Sopenharmony_ci that use framerate ctrl */ 82662306a36Sopenharmony_ci if (gspca_dev->exposure->val < 150) { 82762306a36Sopenharmony_ci i2cpexpo[3] = 150 - gspca_dev->exposure->val; 82862306a36Sopenharmony_ci framerate_ctrl = 300; 82962306a36Sopenharmony_ci } else { 83062306a36Sopenharmony_ci /* The PAS106's exposure control goes from 0 - 4095, 83162306a36Sopenharmony_ci but anything below 300 causes vsync issues, so scale 83262306a36Sopenharmony_ci our 150-1023 to 300-4095 */ 83362306a36Sopenharmony_ci framerate_ctrl = (gspca_dev->exposure->val - 150) 83462306a36Sopenharmony_ci * 1000 / 230 + 300; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci i2cpframerate[3] = framerate_ctrl >> 4; 83862306a36Sopenharmony_ci i2cpframerate[4] = framerate_ctrl & 0x0f; 83962306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpframerate); 84062306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpexpo); 84162306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpdoit); 84262306a36Sopenharmony_ci break; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci default: 84562306a36Sopenharmony_ci break; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic void setfreq(struct gspca_dev *gspca_dev) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) { 85462306a36Sopenharmony_ci /* Framerate adjust register for artificial light 50 hz flicker 85562306a36Sopenharmony_ci compensation, for the ov6650 this is identical to ov6630 85662306a36Sopenharmony_ci 0x2b register, see ov6630 datasheet. 85762306a36Sopenharmony_ci 0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */ 85862306a36Sopenharmony_ci __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}; 85962306a36Sopenharmony_ci switch (sd->plfreq->val) { 86062306a36Sopenharmony_ci default: 86162306a36Sopenharmony_ci/* case 0: * no filter*/ 86262306a36Sopenharmony_ci/* case 2: * 60 hz */ 86362306a36Sopenharmony_ci i2c[3] = 0; 86462306a36Sopenharmony_ci break; 86562306a36Sopenharmony_ci case 1: /* 50 hz */ 86662306a36Sopenharmony_ci i2c[3] = (sd->sensor == SENSOR_OV6650) 86762306a36Sopenharmony_ci ? 0x4f : 0x8a; 86862306a36Sopenharmony_ci break; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci i2c[1] = sensor_data[sd->sensor].sensor_addr; 87162306a36Sopenharmony_ci i2c_w(gspca_dev, i2c); 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic void do_autogain(struct gspca_dev *gspca_dev) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 87862306a36Sopenharmony_ci int deadzone, desired_avg_lum, avg_lum; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci avg_lum = atomic_read(&sd->avg_lum); 88162306a36Sopenharmony_ci if (avg_lum == -1) 88262306a36Sopenharmony_ci return; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (sd->autogain_ignore_frames > 0) { 88562306a36Sopenharmony_ci sd->autogain_ignore_frames--; 88662306a36Sopenharmony_ci return; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* SIF / VGA sensors have a different autoexposure area and thus 89062306a36Sopenharmony_ci different avg_lum values for the same picture brightness */ 89162306a36Sopenharmony_ci if (sensor_data[sd->sensor].flags & F_SIF) { 89262306a36Sopenharmony_ci deadzone = 500; 89362306a36Sopenharmony_ci /* SIF sensors tend to overexpose, so keep this small */ 89462306a36Sopenharmony_ci desired_avg_lum = 5000; 89562306a36Sopenharmony_ci } else { 89662306a36Sopenharmony_ci deadzone = 1500; 89762306a36Sopenharmony_ci desired_avg_lum = 13000; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (sd->brightness) 90162306a36Sopenharmony_ci desired_avg_lum = sd->brightness->val * desired_avg_lum / 127; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (gspca_dev->exposure->maximum < 500) { 90462306a36Sopenharmony_ci if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, 90562306a36Sopenharmony_ci desired_avg_lum, deadzone)) 90662306a36Sopenharmony_ci sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; 90762306a36Sopenharmony_ci } else { 90862306a36Sopenharmony_ci int gain_knee = (s32)gspca_dev->gain->maximum * 9 / 10; 90962306a36Sopenharmony_ci if (gspca_expo_autogain(gspca_dev, avg_lum, desired_avg_lum, 91062306a36Sopenharmony_ci deadzone, gain_knee, sd->exposure_knee)) 91162306a36Sopenharmony_ci sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci/* this function is called at probe time */ 91662306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 91762306a36Sopenharmony_ci const struct usb_device_id *id) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 92062306a36Sopenharmony_ci struct cam *cam; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci reg_r(gspca_dev, 0x00); 92362306a36Sopenharmony_ci if (gspca_dev->usb_buf[0] != 0x10) 92462306a36Sopenharmony_ci return -ENODEV; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci /* copy the webcam info from the device id */ 92762306a36Sopenharmony_ci sd->sensor = id->driver_info >> 8; 92862306a36Sopenharmony_ci sd->bridge = id->driver_info & 0xff; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci cam = &gspca_dev->cam; 93162306a36Sopenharmony_ci if (!(sensor_data[sd->sensor].flags & F_SIF)) { 93262306a36Sopenharmony_ci cam->cam_mode = vga_mode; 93362306a36Sopenharmony_ci cam->nmodes = ARRAY_SIZE(vga_mode); 93462306a36Sopenharmony_ci } else { 93562306a36Sopenharmony_ci cam->cam_mode = sif_mode; 93662306a36Sopenharmony_ci cam->nmodes = ARRAY_SIZE(sif_mode); 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci cam->npkt = 36; /* 36 packets per ISOC message */ 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return 0; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci/* this function is called at probe and resume time */ 94462306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci const __u8 stop = 0x09; /* Disable stream turn of LED */ 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci reg_w(gspca_dev, 0x01, &stop, 1); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return gspca_dev->usb_err; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct gspca_dev *gspca_dev = 95662306a36Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 95762306a36Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci gspca_dev->usb_err = 0; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { 96262306a36Sopenharmony_ci /* when switching to autogain set defaults to make sure 96362306a36Sopenharmony_ci we are on a valid point of the autogain gain / 96462306a36Sopenharmony_ci exposure knee graph, and give this change time to 96562306a36Sopenharmony_ci take effect before doing autogain. */ 96662306a36Sopenharmony_ci gspca_dev->gain->val = gspca_dev->gain->default_value; 96762306a36Sopenharmony_ci gspca_dev->exposure->val = gspca_dev->exposure->default_value; 96862306a36Sopenharmony_ci sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (!gspca_dev->streaming) 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci switch (ctrl->id) { 97562306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 97662306a36Sopenharmony_ci setbrightness(gspca_dev); 97762306a36Sopenharmony_ci break; 97862306a36Sopenharmony_ci case V4L2_CID_AUTOGAIN: 97962306a36Sopenharmony_ci if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) 98062306a36Sopenharmony_ci setexposure(gspca_dev); 98162306a36Sopenharmony_ci if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) 98262306a36Sopenharmony_ci setgain(gspca_dev); 98362306a36Sopenharmony_ci break; 98462306a36Sopenharmony_ci case V4L2_CID_POWER_LINE_FREQUENCY: 98562306a36Sopenharmony_ci setfreq(gspca_dev); 98662306a36Sopenharmony_ci break; 98762306a36Sopenharmony_ci default: 98862306a36Sopenharmony_ci return -EINVAL; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci return gspca_dev->usb_err; 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 99462306a36Sopenharmony_ci .s_ctrl = sd_s_ctrl, 99562306a36Sopenharmony_ci}; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci/* this function is called at probe time */ 99862306a36Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 100162306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 100462306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 5); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630 || 100762306a36Sopenharmony_ci sd->sensor == SENSOR_PAS106 || sd->sensor == SENSOR_PAS202) 100862306a36Sopenharmony_ci sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 100962306a36Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* Gain range is sensor dependent */ 101262306a36Sopenharmony_ci switch (sd->sensor) { 101362306a36Sopenharmony_ci case SENSOR_OV6650: 101462306a36Sopenharmony_ci case SENSOR_PAS106: 101562306a36Sopenharmony_ci case SENSOR_PAS202: 101662306a36Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 101762306a36Sopenharmony_ci V4L2_CID_GAIN, 0, 31, 1, 15); 101862306a36Sopenharmony_ci break; 101962306a36Sopenharmony_ci case SENSOR_OV7630: 102062306a36Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 102162306a36Sopenharmony_ci V4L2_CID_GAIN, 0, 47, 1, 31); 102262306a36Sopenharmony_ci break; 102362306a36Sopenharmony_ci case SENSOR_HV7131D: 102462306a36Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 102562306a36Sopenharmony_ci V4L2_CID_GAIN, 0, 63, 1, 31); 102662306a36Sopenharmony_ci break; 102762306a36Sopenharmony_ci case SENSOR_TAS5110C: 102862306a36Sopenharmony_ci case SENSOR_TAS5110D: 102962306a36Sopenharmony_ci case SENSOR_TAS5130CXX: 103062306a36Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 103162306a36Sopenharmony_ci V4L2_CID_GAIN, 0, 255, 1, 127); 103262306a36Sopenharmony_ci break; 103362306a36Sopenharmony_ci default: 103462306a36Sopenharmony_ci if (sd->bridge == BRIDGE_103) { 103562306a36Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 103662306a36Sopenharmony_ci V4L2_CID_GAIN, 0, 127, 1, 63); 103762306a36Sopenharmony_ci } else { 103862306a36Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 103962306a36Sopenharmony_ci V4L2_CID_GAIN, 0, 15, 1, 7); 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci /* Exposure range is sensor dependent, and not all have exposure */ 104462306a36Sopenharmony_ci switch (sd->sensor) { 104562306a36Sopenharmony_ci case SENSOR_HV7131D: 104662306a36Sopenharmony_ci gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 104762306a36Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 8191, 1, 482); 104862306a36Sopenharmony_ci sd->exposure_knee = 964; 104962306a36Sopenharmony_ci break; 105062306a36Sopenharmony_ci case SENSOR_OV6650: 105162306a36Sopenharmony_ci case SENSOR_OV7630: 105262306a36Sopenharmony_ci case SENSOR_PAS106: 105362306a36Sopenharmony_ci case SENSOR_PAS202: 105462306a36Sopenharmony_ci gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 105562306a36Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 1023, 1, 66); 105662306a36Sopenharmony_ci sd->exposure_knee = 200; 105762306a36Sopenharmony_ci break; 105862306a36Sopenharmony_ci case SENSOR_TAS5110C: 105962306a36Sopenharmony_ci case SENSOR_TAS5110D: 106062306a36Sopenharmony_ci gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 106162306a36Sopenharmony_ci V4L2_CID_EXPOSURE, 2, 15, 1, 2); 106262306a36Sopenharmony_ci break; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (gspca_dev->exposure) { 106662306a36Sopenharmony_ci gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 106762306a36Sopenharmony_ci V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (sd->sensor == SENSOR_OV6650 || sd->sensor == SENSOR_OV7630) 107162306a36Sopenharmony_ci sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, 107262306a36Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY, 107362306a36Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 107462306a36Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (hdl->error) { 107762306a36Sopenharmony_ci pr_err("Could not initialize controls\n"); 107862306a36Sopenharmony_ci return hdl->error; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (gspca_dev->autogain) 108262306a36Sopenharmony_ci v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci return 0; 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci/* -- start the camera -- */ 108862306a36Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 109162306a36Sopenharmony_ci struct cam *cam = &gspca_dev->cam; 109262306a36Sopenharmony_ci int i, mode; 109362306a36Sopenharmony_ci __u8 regs[0x31]; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci mode = cam->cam_mode[gspca_dev->curr_mode].priv & 0x07; 109662306a36Sopenharmony_ci /* Copy registers 0x01 - 0x19 from the template */ 109762306a36Sopenharmony_ci memcpy(®s[0x01], sensor_data[sd->sensor].bridge_init, 0x19); 109862306a36Sopenharmony_ci /* Set the mode */ 109962306a36Sopenharmony_ci regs[0x18] |= mode << 4; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* Set bridge gain to 1.0 */ 110262306a36Sopenharmony_ci if (sd->bridge == BRIDGE_103) { 110362306a36Sopenharmony_ci regs[0x05] = 0x20; /* Red */ 110462306a36Sopenharmony_ci regs[0x06] = 0x20; /* Green */ 110562306a36Sopenharmony_ci regs[0x07] = 0x20; /* Blue */ 110662306a36Sopenharmony_ci } else { 110762306a36Sopenharmony_ci regs[0x10] = 0x00; /* Red and blue */ 110862306a36Sopenharmony_ci regs[0x11] = 0x00; /* Green */ 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci /* Setup pixel numbers and auto exposure window */ 111262306a36Sopenharmony_ci if (sensor_data[sd->sensor].flags & F_SIF) { 111362306a36Sopenharmony_ci regs[0x1a] = 0x14; /* HO_SIZE 640, makes no sense */ 111462306a36Sopenharmony_ci regs[0x1b] = 0x0a; /* VO_SIZE 320, makes no sense */ 111562306a36Sopenharmony_ci regs[0x1c] = 0x02; /* AE H-start 64 */ 111662306a36Sopenharmony_ci regs[0x1d] = 0x02; /* AE V-start 64 */ 111762306a36Sopenharmony_ci regs[0x1e] = 0x09; /* AE H-end 288 */ 111862306a36Sopenharmony_ci regs[0x1f] = 0x07; /* AE V-end 224 */ 111962306a36Sopenharmony_ci } else { 112062306a36Sopenharmony_ci regs[0x1a] = 0x1d; /* HO_SIZE 960, makes no sense */ 112162306a36Sopenharmony_ci regs[0x1b] = 0x10; /* VO_SIZE 512, makes no sense */ 112262306a36Sopenharmony_ci regs[0x1c] = 0x05; /* AE H-start 160 */ 112362306a36Sopenharmony_ci regs[0x1d] = 0x03; /* AE V-start 96 */ 112462306a36Sopenharmony_ci regs[0x1e] = 0x0f; /* AE H-end 480 */ 112562306a36Sopenharmony_ci regs[0x1f] = 0x0c; /* AE V-end 384 */ 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci /* Setup the gamma table (only used with the sn9c103 bridge) */ 112962306a36Sopenharmony_ci for (i = 0; i < 16; i++) 113062306a36Sopenharmony_ci regs[0x20 + i] = i * 16; 113162306a36Sopenharmony_ci regs[0x20 + i] = 255; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci /* Special cases where some regs depend on mode or bridge */ 113462306a36Sopenharmony_ci switch (sd->sensor) { 113562306a36Sopenharmony_ci case SENSOR_TAS5130CXX: 113662306a36Sopenharmony_ci /* FIXME / TESTME 113762306a36Sopenharmony_ci probably not mode specific at all most likely the upper 113862306a36Sopenharmony_ci nibble of 0x19 is exposure (clock divider) just as with 113962306a36Sopenharmony_ci the tas5110, we need someone to test this. */ 114062306a36Sopenharmony_ci regs[0x19] = mode ? 0x23 : 0x43; 114162306a36Sopenharmony_ci break; 114262306a36Sopenharmony_ci case SENSOR_OV7630: 114362306a36Sopenharmony_ci /* FIXME / TESTME for some reason with the 101/102 bridge the 114462306a36Sopenharmony_ci clock is set to 12 Mhz (reg1 == 0x04), rather then 24. 114562306a36Sopenharmony_ci Also the hstart needs to go from 1 to 2 when using a 103, 114662306a36Sopenharmony_ci which is likely related. This does not seem right. */ 114762306a36Sopenharmony_ci if (sd->bridge == BRIDGE_103) { 114862306a36Sopenharmony_ci regs[0x01] = 0x44; /* Select 24 Mhz clock */ 114962306a36Sopenharmony_ci regs[0x12] = 0x02; /* Set hstart to 2 */ 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci break; 115262306a36Sopenharmony_ci case SENSOR_PAS202: 115362306a36Sopenharmony_ci /* For some unknown reason we need to increase hstart by 1 on 115462306a36Sopenharmony_ci the sn9c103, otherwise we get wrong colors (bayer shift). */ 115562306a36Sopenharmony_ci if (sd->bridge == BRIDGE_103) 115662306a36Sopenharmony_ci regs[0x12] += 1; 115762306a36Sopenharmony_ci break; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci /* Disable compression when the raw bayer format has been selected */ 116062306a36Sopenharmony_ci if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) 116162306a36Sopenharmony_ci regs[0x18] &= ~0x80; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* Vga mode emulation on SIF sensor? */ 116462306a36Sopenharmony_ci if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_REDUCED_SIF) { 116562306a36Sopenharmony_ci regs[0x12] += 16; /* hstart adjust */ 116662306a36Sopenharmony_ci regs[0x13] += 24; /* vstart adjust */ 116762306a36Sopenharmony_ci regs[0x15] = 320 / 16; /* hsize */ 116862306a36Sopenharmony_ci regs[0x16] = 240 / 16; /* vsize */ 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci /* reg 0x01 bit 2 video transfert on */ 117262306a36Sopenharmony_ci reg_w(gspca_dev, 0x01, ®s[0x01], 1); 117362306a36Sopenharmony_ci /* reg 0x17 SensorClk enable inv Clk 0x60 */ 117462306a36Sopenharmony_ci reg_w(gspca_dev, 0x17, ®s[0x17], 1); 117562306a36Sopenharmony_ci /* Set the registers from the template */ 117662306a36Sopenharmony_ci reg_w(gspca_dev, 0x01, ®s[0x01], 117762306a36Sopenharmony_ci (sd->bridge == BRIDGE_103) ? 0x30 : 0x1f); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* Init the sensor */ 118062306a36Sopenharmony_ci i2c_w_vector(gspca_dev, sensor_data[sd->sensor].sensor_init, 118162306a36Sopenharmony_ci sensor_data[sd->sensor].sensor_init_size); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* Mode / bridge specific sensor setup */ 118462306a36Sopenharmony_ci switch (sd->sensor) { 118562306a36Sopenharmony_ci case SENSOR_PAS202: { 118662306a36Sopenharmony_ci const __u8 i2cpclockdiv[] = 118762306a36Sopenharmony_ci {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}; 118862306a36Sopenharmony_ci /* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */ 118962306a36Sopenharmony_ci if (mode) 119062306a36Sopenharmony_ci i2c_w(gspca_dev, i2cpclockdiv); 119162306a36Sopenharmony_ci break; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci case SENSOR_OV7630: 119462306a36Sopenharmony_ci /* FIXME / TESTME We should be able to handle this identical 119562306a36Sopenharmony_ci for the 101/102 and the 103 case */ 119662306a36Sopenharmony_ci if (sd->bridge == BRIDGE_103) { 119762306a36Sopenharmony_ci const __u8 i2c[] = { 0xa0, 0x21, 0x13, 119862306a36Sopenharmony_ci 0x80, 0x00, 0x00, 0x00, 0x10 }; 119962306a36Sopenharmony_ci i2c_w(gspca_dev, i2c); 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ 120462306a36Sopenharmony_ci reg_w(gspca_dev, 0x15, ®s[0x15], 2); 120562306a36Sopenharmony_ci /* compression register */ 120662306a36Sopenharmony_ci reg_w(gspca_dev, 0x18, ®s[0x18], 1); 120762306a36Sopenharmony_ci /* H_start */ 120862306a36Sopenharmony_ci reg_w(gspca_dev, 0x12, ®s[0x12], 1); 120962306a36Sopenharmony_ci /* V_START */ 121062306a36Sopenharmony_ci reg_w(gspca_dev, 0x13, ®s[0x13], 1); 121162306a36Sopenharmony_ci /* reset 0x17 SensorClk enable inv Clk 0x60 */ 121262306a36Sopenharmony_ci /*fixme: ov7630 [17]=68 8f (+20 if 102)*/ 121362306a36Sopenharmony_ci reg_w(gspca_dev, 0x17, ®s[0x17], 1); 121462306a36Sopenharmony_ci /*MCKSIZE ->3 */ /*fixme: not ov7630*/ 121562306a36Sopenharmony_ci reg_w(gspca_dev, 0x19, ®s[0x19], 1); 121662306a36Sopenharmony_ci /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ 121762306a36Sopenharmony_ci reg_w(gspca_dev, 0x1c, ®s[0x1c], 4); 121862306a36Sopenharmony_ci /* Enable video transfert */ 121962306a36Sopenharmony_ci reg_w(gspca_dev, 0x01, ®s[0x01], 1); 122062306a36Sopenharmony_ci /* Compression */ 122162306a36Sopenharmony_ci reg_w(gspca_dev, 0x18, ®s[0x18], 2); 122262306a36Sopenharmony_ci msleep(20); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci sd->reg11 = -1; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci setgain(gspca_dev); 122762306a36Sopenharmony_ci setbrightness(gspca_dev); 122862306a36Sopenharmony_ci setexposure(gspca_dev); 122962306a36Sopenharmony_ci setfreq(gspca_dev); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci sd->frames_to_drop = 0; 123262306a36Sopenharmony_ci sd->autogain_ignore_frames = 0; 123362306a36Sopenharmony_ci gspca_dev->exp_too_high_cnt = 0; 123462306a36Sopenharmony_ci gspca_dev->exp_too_low_cnt = 0; 123562306a36Sopenharmony_ci atomic_set(&sd->avg_lum, -1); 123662306a36Sopenharmony_ci return gspca_dev->usb_err; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev) 124062306a36Sopenharmony_ci{ 124162306a36Sopenharmony_ci sd_init(gspca_dev); 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic u8* find_sof(struct gspca_dev *gspca_dev, u8 *data, int len) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 124762306a36Sopenharmony_ci int i, header_size = (sd->bridge == BRIDGE_103) ? 18 : 12; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* frames start with: 125062306a36Sopenharmony_ci * ff ff 00 c4 c4 96 synchro 125162306a36Sopenharmony_ci * 00 (unknown) 125262306a36Sopenharmony_ci * xx (frame sequence / size / compression) 125362306a36Sopenharmony_ci * (xx) (idem - extra byte for sn9c103) 125462306a36Sopenharmony_ci * ll mm brightness sum inside auto exposure 125562306a36Sopenharmony_ci * ll mm brightness sum outside auto exposure 125662306a36Sopenharmony_ci * (xx xx xx xx xx) audio values for snc103 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_ci for (i = 0; i < len; i++) { 125962306a36Sopenharmony_ci switch (sd->header_read) { 126062306a36Sopenharmony_ci case 0: 126162306a36Sopenharmony_ci if (data[i] == 0xff) 126262306a36Sopenharmony_ci sd->header_read++; 126362306a36Sopenharmony_ci break; 126462306a36Sopenharmony_ci case 1: 126562306a36Sopenharmony_ci if (data[i] == 0xff) 126662306a36Sopenharmony_ci sd->header_read++; 126762306a36Sopenharmony_ci else 126862306a36Sopenharmony_ci sd->header_read = 0; 126962306a36Sopenharmony_ci break; 127062306a36Sopenharmony_ci case 2: 127162306a36Sopenharmony_ci if (data[i] == 0x00) 127262306a36Sopenharmony_ci sd->header_read++; 127362306a36Sopenharmony_ci else if (data[i] != 0xff) 127462306a36Sopenharmony_ci sd->header_read = 0; 127562306a36Sopenharmony_ci break; 127662306a36Sopenharmony_ci case 3: 127762306a36Sopenharmony_ci if (data[i] == 0xc4) 127862306a36Sopenharmony_ci sd->header_read++; 127962306a36Sopenharmony_ci else if (data[i] == 0xff) 128062306a36Sopenharmony_ci sd->header_read = 1; 128162306a36Sopenharmony_ci else 128262306a36Sopenharmony_ci sd->header_read = 0; 128362306a36Sopenharmony_ci break; 128462306a36Sopenharmony_ci case 4: 128562306a36Sopenharmony_ci if (data[i] == 0xc4) 128662306a36Sopenharmony_ci sd->header_read++; 128762306a36Sopenharmony_ci else if (data[i] == 0xff) 128862306a36Sopenharmony_ci sd->header_read = 1; 128962306a36Sopenharmony_ci else 129062306a36Sopenharmony_ci sd->header_read = 0; 129162306a36Sopenharmony_ci break; 129262306a36Sopenharmony_ci case 5: 129362306a36Sopenharmony_ci if (data[i] == 0x96) 129462306a36Sopenharmony_ci sd->header_read++; 129562306a36Sopenharmony_ci else if (data[i] == 0xff) 129662306a36Sopenharmony_ci sd->header_read = 1; 129762306a36Sopenharmony_ci else 129862306a36Sopenharmony_ci sd->header_read = 0; 129962306a36Sopenharmony_ci break; 130062306a36Sopenharmony_ci default: 130162306a36Sopenharmony_ci sd->header[sd->header_read - 6] = data[i]; 130262306a36Sopenharmony_ci sd->header_read++; 130362306a36Sopenharmony_ci if (sd->header_read == header_size) { 130462306a36Sopenharmony_ci sd->header_read = 0; 130562306a36Sopenharmony_ci return data + i + 1; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci return NULL; 131062306a36Sopenharmony_ci} 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 131362306a36Sopenharmony_ci u8 *data, /* isoc packet */ 131462306a36Sopenharmony_ci int len) /* iso packet length */ 131562306a36Sopenharmony_ci{ 131662306a36Sopenharmony_ci int fr_h_sz = 0, lum_offset = 0, len_after_sof = 0; 131762306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 131862306a36Sopenharmony_ci struct cam *cam = &gspca_dev->cam; 131962306a36Sopenharmony_ci u8 *sof; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci sof = find_sof(gspca_dev, data, len); 132262306a36Sopenharmony_ci if (sof) { 132362306a36Sopenharmony_ci if (sd->bridge == BRIDGE_103) { 132462306a36Sopenharmony_ci fr_h_sz = 18; 132562306a36Sopenharmony_ci lum_offset = 3; 132662306a36Sopenharmony_ci } else { 132762306a36Sopenharmony_ci fr_h_sz = 12; 132862306a36Sopenharmony_ci lum_offset = 2; 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci len_after_sof = len - (sof - data); 133262306a36Sopenharmony_ci len = (sof - data) - fr_h_sz; 133362306a36Sopenharmony_ci if (len < 0) 133462306a36Sopenharmony_ci len = 0; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) { 133862306a36Sopenharmony_ci /* In raw mode we sometimes get some garbage after the frame 133962306a36Sopenharmony_ci ignore this */ 134062306a36Sopenharmony_ci int used; 134162306a36Sopenharmony_ci int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci used = gspca_dev->image_len; 134462306a36Sopenharmony_ci if (used + len > size) 134562306a36Sopenharmony_ci len = size - used; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (sof) { 135162306a36Sopenharmony_ci int lum = sd->header[lum_offset] + 135262306a36Sopenharmony_ci (sd->header[lum_offset + 1] << 8); 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci /* When exposure changes midway a frame we 135562306a36Sopenharmony_ci get a lum of 0 in this case drop 2 frames 135662306a36Sopenharmony_ci as the frames directly after an exposure 135762306a36Sopenharmony_ci change have an unstable image. Sometimes lum 135862306a36Sopenharmony_ci *really* is 0 (cam used in low light with 135962306a36Sopenharmony_ci low exposure setting), so do not drop frames 136062306a36Sopenharmony_ci if the previous lum was 0 too. */ 136162306a36Sopenharmony_ci if (lum == 0 && sd->prev_avg_lum != 0) { 136262306a36Sopenharmony_ci lum = -1; 136362306a36Sopenharmony_ci sd->frames_to_drop = 2; 136462306a36Sopenharmony_ci sd->prev_avg_lum = 0; 136562306a36Sopenharmony_ci } else 136662306a36Sopenharmony_ci sd->prev_avg_lum = lum; 136762306a36Sopenharmony_ci atomic_set(&sd->avg_lum, lum); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci if (sd->frames_to_drop) 137062306a36Sopenharmony_ci sd->frames_to_drop--; 137162306a36Sopenharmony_ci else 137262306a36Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, sof, len_after_sof); 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT) 137962306a36Sopenharmony_cistatic int sd_int_pkt_scan(struct gspca_dev *gspca_dev, 138062306a36Sopenharmony_ci u8 *data, /* interrupt packet data */ 138162306a36Sopenharmony_ci int len) /* interrupt packet length */ 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci int ret = -EINVAL; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci if (len == 1 && data[0] == 1) { 138662306a36Sopenharmony_ci input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); 138762306a36Sopenharmony_ci input_sync(gspca_dev->input_dev); 138862306a36Sopenharmony_ci input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); 138962306a36Sopenharmony_ci input_sync(gspca_dev->input_dev); 139062306a36Sopenharmony_ci ret = 0; 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci return ret; 139462306a36Sopenharmony_ci} 139562306a36Sopenharmony_ci#endif 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci/* sub-driver description */ 139862306a36Sopenharmony_cistatic const struct sd_desc sd_desc = { 139962306a36Sopenharmony_ci .name = MODULE_NAME, 140062306a36Sopenharmony_ci .config = sd_config, 140162306a36Sopenharmony_ci .init = sd_init, 140262306a36Sopenharmony_ci .init_controls = sd_init_controls, 140362306a36Sopenharmony_ci .start = sd_start, 140462306a36Sopenharmony_ci .stopN = sd_stopN, 140562306a36Sopenharmony_ci .pkt_scan = sd_pkt_scan, 140662306a36Sopenharmony_ci .dq_callback = do_autogain, 140762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT) 140862306a36Sopenharmony_ci .int_pkt_scan = sd_int_pkt_scan, 140962306a36Sopenharmony_ci#endif 141062306a36Sopenharmony_ci}; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci/* -- module initialisation -- */ 141362306a36Sopenharmony_ci#define SB(sensor, bridge) \ 141462306a36Sopenharmony_ci .driver_info = (SENSOR_ ## sensor << 8) | BRIDGE_ ## bridge 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = { 141862306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110C, 102)}, /* TAS5110C1B */ 141962306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */ 142062306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110D, 101)}, /* TAS5110D */ 142162306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)}, 142262306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)}, 142362306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)}, 142462306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)}, 142562306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)}, 142662306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)}, 142762306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6027), SB(OV7630, 101)}, /* Genius Eye 310 */ 142862306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)}, 142962306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)}, 143062306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x602a), SB(HV7131D, 102)}, 143162306a36Sopenharmony_ci /* {USB_DEVICE(0x0c45, 0x602b), SB(MI0343, 102)}, */ 143262306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)}, 143362306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)}, 143462306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)}, 143562306a36Sopenharmony_ci /* {USB_DEVICE(0x0c45, 0x6030), SB(MI03XX, 102)}, */ /* MI0343 MI0360 MI0330 */ 143662306a36Sopenharmony_ci /* {USB_DEVICE(0x0c45, 0x6082), SB(MI03XX, 103)}, */ /* MI0343 MI0360 */ 143762306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x6083), SB(HV7131D, 103)}, 143862306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x608c), SB(HV7131R, 103)}, 143962306a36Sopenharmony_ci /* {USB_DEVICE(0x0c45, 0x608e), SB(CISVF10, 103)}, */ 144062306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x608f), SB(OV7630, 103)}, 144162306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x60a8), SB(PAS106, 103)}, 144262306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x60aa), SB(TAS5130CXX, 103)}, 144362306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x60af), SB(PAS202, 103)}, 144462306a36Sopenharmony_ci {USB_DEVICE(0x0c45, 0x60b0), SB(OV7630, 103)}, 144562306a36Sopenharmony_ci {} 144662306a36Sopenharmony_ci}; 144762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci/* -- device connect -- */ 145062306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 145162306a36Sopenharmony_ci const struct usb_device_id *id) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), 145462306a36Sopenharmony_ci THIS_MODULE); 145562306a36Sopenharmony_ci} 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic struct usb_driver sd_driver = { 145862306a36Sopenharmony_ci .name = MODULE_NAME, 145962306a36Sopenharmony_ci .id_table = device_table, 146062306a36Sopenharmony_ci .probe = sd_probe, 146162306a36Sopenharmony_ci .disconnect = gspca_disconnect, 146262306a36Sopenharmony_ci#ifdef CONFIG_PM 146362306a36Sopenharmony_ci .suspend = gspca_suspend, 146462306a36Sopenharmony_ci .resume = gspca_resume, 146562306a36Sopenharmony_ci .reset_resume = gspca_resume, 146662306a36Sopenharmony_ci#endif 146762306a36Sopenharmony_ci}; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_cimodule_usb_driver(sd_driver); 1470