162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Driver for the ov9650 sensor 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2008 Erik Andrén 762306a36Sopenharmony_ci * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. 862306a36Sopenharmony_ci * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Portions of code to USB interface and ALi driver software, 1162306a36Sopenharmony_ci * Copyright (c) 2006 Willem Duinker 1262306a36Sopenharmony_ci * v4l2 interface modeled after the V4L2 driver 1362306a36Sopenharmony_ci * for SN9C10x PC Camera Controllers 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "m5602_ov9650.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic int ov9650_s_ctrl(struct v4l2_ctrl *ctrl); 2162306a36Sopenharmony_cistatic void ov9650_dump_registers(struct sd *sd); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic const unsigned char preinit_ov9650[][3] = { 2462306a36Sopenharmony_ci /* [INITCAM] */ 2562306a36Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, 2662306a36Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, 2762306a36Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 2862306a36Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 2962306a36Sopenharmony_ci {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 3062306a36Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, 3362306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 3462306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 3562306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 3662306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 3762306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, 3862306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 3962306a36Sopenharmony_ci {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, 4062306a36Sopenharmony_ci /* Reset chip */ 4162306a36Sopenharmony_ci {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, 4262306a36Sopenharmony_ci /* Enable double clock */ 4362306a36Sopenharmony_ci {SENSOR, OV9650_CLKRC, 0x80}, 4462306a36Sopenharmony_ci /* Do something out of spec with the power */ 4562306a36Sopenharmony_ci {SENSOR, OV9650_OFON, 0x40} 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic const unsigned char init_ov9650[][3] = { 4962306a36Sopenharmony_ci /* [INITCAM] */ 5062306a36Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, 5162306a36Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, 5262306a36Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 5362306a36Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 5462306a36Sopenharmony_ci {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 5562306a36Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, 5862306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 5962306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 6062306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 6162306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 6262306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, 6362306a36Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 6462306a36Sopenharmony_ci {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Reset chip */ 6762306a36Sopenharmony_ci {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, 6862306a36Sopenharmony_ci /* One extra reset is needed in order to make the sensor behave 6962306a36Sopenharmony_ci properly when resuming from ram, could be a timing issue */ 7062306a36Sopenharmony_ci {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Enable double clock */ 7362306a36Sopenharmony_ci {SENSOR, OV9650_CLKRC, 0x80}, 7462306a36Sopenharmony_ci /* Do something out of spec with the power */ 7562306a36Sopenharmony_ci {SENSOR, OV9650_OFON, 0x40}, 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Set fast AGC/AEC algorithm with unlimited step size */ 7862306a36Sopenharmony_ci {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC | 7962306a36Sopenharmony_ci OV9650_AEC_UNLIM_STEP_SIZE}, 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci {SENSOR, OV9650_CHLF, 0x10}, 8262306a36Sopenharmony_ci {SENSOR, OV9650_ARBLM, 0xbf}, 8362306a36Sopenharmony_ci {SENSOR, OV9650_ACOM38, 0x81}, 8462306a36Sopenharmony_ci /* Turn off color matrix coefficient double option */ 8562306a36Sopenharmony_ci {SENSOR, OV9650_COM16, 0x00}, 8662306a36Sopenharmony_ci /* Enable color matrix for RGB/YUV, Delay Y channel, 8762306a36Sopenharmony_ci set output Y/UV delay to 1 */ 8862306a36Sopenharmony_ci {SENSOR, OV9650_COM13, 0x19}, 8962306a36Sopenharmony_ci /* Enable digital BLC, Set output mode to U Y V Y */ 9062306a36Sopenharmony_ci {SENSOR, OV9650_TSLB, 0x0c}, 9162306a36Sopenharmony_ci /* Limit the AGC/AEC stable upper region */ 9262306a36Sopenharmony_ci {SENSOR, OV9650_COM24, 0x00}, 9362306a36Sopenharmony_ci /* Enable HREF and some out of spec things */ 9462306a36Sopenharmony_ci {SENSOR, OV9650_COM12, 0x73}, 9562306a36Sopenharmony_ci /* Set all DBLC offset signs to positive and 9662306a36Sopenharmony_ci do some out of spec stuff */ 9762306a36Sopenharmony_ci {SENSOR, OV9650_DBLC1, 0xdf}, 9862306a36Sopenharmony_ci {SENSOR, OV9650_COM21, 0x06}, 9962306a36Sopenharmony_ci {SENSOR, OV9650_RSVD35, 0x91}, 10062306a36Sopenharmony_ci /* Necessary, no camera stream without it */ 10162306a36Sopenharmony_ci {SENSOR, OV9650_RSVD16, 0x06}, 10262306a36Sopenharmony_ci {SENSOR, OV9650_RSVD94, 0x99}, 10362306a36Sopenharmony_ci {SENSOR, OV9650_RSVD95, 0x99}, 10462306a36Sopenharmony_ci {SENSOR, OV9650_RSVD96, 0x04}, 10562306a36Sopenharmony_ci /* Enable full range output */ 10662306a36Sopenharmony_ci {SENSOR, OV9650_COM15, 0x0}, 10762306a36Sopenharmony_ci /* Enable HREF at optical black, enable ADBLC bias, 10862306a36Sopenharmony_ci enable ADBLC, reset timings at format change */ 10962306a36Sopenharmony_ci {SENSOR, OV9650_COM6, 0x4b}, 11062306a36Sopenharmony_ci /* Subtract 32 from the B channel bias */ 11162306a36Sopenharmony_ci {SENSOR, OV9650_BBIAS, 0xa0}, 11262306a36Sopenharmony_ci /* Subtract 32 from the Gb channel bias */ 11362306a36Sopenharmony_ci {SENSOR, OV9650_GbBIAS, 0xa0}, 11462306a36Sopenharmony_ci /* Do not bypass the analog BLC and to some out of spec stuff */ 11562306a36Sopenharmony_ci {SENSOR, OV9650_Gr_COM, 0x00}, 11662306a36Sopenharmony_ci /* Subtract 32 from the R channel bias */ 11762306a36Sopenharmony_ci {SENSOR, OV9650_RBIAS, 0xa0}, 11862306a36Sopenharmony_ci /* Subtract 32 from the R channel bias */ 11962306a36Sopenharmony_ci {SENSOR, OV9650_RBIAS, 0x0}, 12062306a36Sopenharmony_ci {SENSOR, OV9650_COM26, 0x80}, 12162306a36Sopenharmony_ci {SENSOR, OV9650_ACOMA9, 0x98}, 12262306a36Sopenharmony_ci /* Set the AGC/AEC stable region upper limit */ 12362306a36Sopenharmony_ci {SENSOR, OV9650_AEW, 0x68}, 12462306a36Sopenharmony_ci /* Set the AGC/AEC stable region lower limit */ 12562306a36Sopenharmony_ci {SENSOR, OV9650_AEB, 0x5c}, 12662306a36Sopenharmony_ci /* Set the high and low limit nibbles to 3 */ 12762306a36Sopenharmony_ci {SENSOR, OV9650_VPT, 0xc3}, 12862306a36Sopenharmony_ci /* Set the Automatic Gain Ceiling (AGC) to 128x, 12962306a36Sopenharmony_ci drop VSYNC at frame drop, 13062306a36Sopenharmony_ci limit exposure timing, 13162306a36Sopenharmony_ci drop frame when the AEC step is larger than the exposure gap */ 13262306a36Sopenharmony_ci {SENSOR, OV9650_COM9, 0x6e}, 13362306a36Sopenharmony_ci /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync) 13462306a36Sopenharmony_ci and set PWDN to SLVS (slave mode vertical sync) */ 13562306a36Sopenharmony_ci {SENSOR, OV9650_COM10, 0x42}, 13662306a36Sopenharmony_ci /* Set horizontal column start high to default value */ 13762306a36Sopenharmony_ci {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */ 13862306a36Sopenharmony_ci /* Set horizontal column end */ 13962306a36Sopenharmony_ci {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */ 14062306a36Sopenharmony_ci /* Complementing register to the two writes above */ 14162306a36Sopenharmony_ci {SENSOR, OV9650_HREF, 0xb2}, 14262306a36Sopenharmony_ci /* Set vertical row start high bits */ 14362306a36Sopenharmony_ci {SENSOR, OV9650_VSTRT, 0x02}, 14462306a36Sopenharmony_ci /* Set vertical row end low bits */ 14562306a36Sopenharmony_ci {SENSOR, OV9650_VSTOP, 0x7e}, 14662306a36Sopenharmony_ci /* Set complementing vertical frame control */ 14762306a36Sopenharmony_ci {SENSOR, OV9650_VREF, 0x10}, 14862306a36Sopenharmony_ci {SENSOR, OV9650_ADC, 0x04}, 14962306a36Sopenharmony_ci {SENSOR, OV9650_HV, 0x40}, 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Enable denoise, and white-pixel erase */ 15262306a36Sopenharmony_ci {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE | 15362306a36Sopenharmony_ci OV9650_WHITE_PIXEL_ENABLE | 15462306a36Sopenharmony_ci OV9650_WHITE_PIXEL_OPTION}, 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Enable VARIOPIXEL */ 15762306a36Sopenharmony_ci {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL}, 15862306a36Sopenharmony_ci {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL}, 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Put the sensor in soft sleep mode */ 16162306a36Sopenharmony_ci {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X}, 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic const unsigned char res_init_ov9650[][3] = { 16562306a36Sopenharmony_ci {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X}, 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82}, 16862306a36Sopenharmony_ci {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00}, 16962306a36Sopenharmony_ci {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, 17062306a36Sopenharmony_ci {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00}, 17162306a36Sopenharmony_ci {BRIDGE, M5602_XB_SIG_INI, 0x01} 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* Vertically and horizontally flips the image if matched, needed for machines 17562306a36Sopenharmony_ci where the sensor is mounted upside down */ 17662306a36Sopenharmony_cistatic 17762306a36Sopenharmony_ci const 17862306a36Sopenharmony_ci struct dmi_system_id ov9650_flip_dmi_table[] = { 17962306a36Sopenharmony_ci { 18062306a36Sopenharmony_ci .ident = "ASUS A6Ja", 18162306a36Sopenharmony_ci .matches = { 18262306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 18362306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6J") 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci }, 18662306a36Sopenharmony_ci { 18762306a36Sopenharmony_ci .ident = "ASUS A6JC", 18862306a36Sopenharmony_ci .matches = { 18962306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 19062306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci }, 19362306a36Sopenharmony_ci { 19462306a36Sopenharmony_ci .ident = "ASUS A6K", 19562306a36Sopenharmony_ci .matches = { 19662306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 19762306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6K") 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci }, 20062306a36Sopenharmony_ci { 20162306a36Sopenharmony_ci .ident = "ASUS A6Kt", 20262306a36Sopenharmony_ci .matches = { 20362306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 20462306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt") 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci }, 20762306a36Sopenharmony_ci { 20862306a36Sopenharmony_ci .ident = "ASUS A6VA", 20962306a36Sopenharmony_ci .matches = { 21062306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 21162306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6VA") 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci }, 21462306a36Sopenharmony_ci { 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci .ident = "ASUS A6VC", 21762306a36Sopenharmony_ci .matches = { 21862306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 21962306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6VC") 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci }, 22262306a36Sopenharmony_ci { 22362306a36Sopenharmony_ci .ident = "ASUS A6VM", 22462306a36Sopenharmony_ci .matches = { 22562306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 22662306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci }, 22962306a36Sopenharmony_ci { 23062306a36Sopenharmony_ci .ident = "ASUS A7V", 23162306a36Sopenharmony_ci .matches = { 23262306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 23362306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A7V") 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci }, 23662306a36Sopenharmony_ci { 23762306a36Sopenharmony_ci .ident = "Alienware Aurora m9700", 23862306a36Sopenharmony_ci .matches = { 23962306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "alienware"), 24062306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700") 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci }, 24362306a36Sopenharmony_ci {} 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic struct v4l2_pix_format ov9650_modes[] = { 24762306a36Sopenharmony_ci { 24862306a36Sopenharmony_ci 176, 24962306a36Sopenharmony_ci 144, 25062306a36Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 25162306a36Sopenharmony_ci V4L2_FIELD_NONE, 25262306a36Sopenharmony_ci .sizeimage = 25362306a36Sopenharmony_ci 176 * 144, 25462306a36Sopenharmony_ci .bytesperline = 176, 25562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 25662306a36Sopenharmony_ci .priv = 9 25762306a36Sopenharmony_ci }, { 25862306a36Sopenharmony_ci 320, 25962306a36Sopenharmony_ci 240, 26062306a36Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 26162306a36Sopenharmony_ci V4L2_FIELD_NONE, 26262306a36Sopenharmony_ci .sizeimage = 26362306a36Sopenharmony_ci 320 * 240, 26462306a36Sopenharmony_ci .bytesperline = 320, 26562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 26662306a36Sopenharmony_ci .priv = 8 26762306a36Sopenharmony_ci }, { 26862306a36Sopenharmony_ci 352, 26962306a36Sopenharmony_ci 288, 27062306a36Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 27162306a36Sopenharmony_ci V4L2_FIELD_NONE, 27262306a36Sopenharmony_ci .sizeimage = 27362306a36Sopenharmony_ci 352 * 288, 27462306a36Sopenharmony_ci .bytesperline = 352, 27562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 27662306a36Sopenharmony_ci .priv = 9 27762306a36Sopenharmony_ci }, { 27862306a36Sopenharmony_ci 640, 27962306a36Sopenharmony_ci 480, 28062306a36Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 28162306a36Sopenharmony_ci V4L2_FIELD_NONE, 28262306a36Sopenharmony_ci .sizeimage = 28362306a36Sopenharmony_ci 640 * 480, 28462306a36Sopenharmony_ci .bytesperline = 640, 28562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 28662306a36Sopenharmony_ci .priv = 9 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops ov9650_ctrl_ops = { 29162306a36Sopenharmony_ci .s_ctrl = ov9650_s_ctrl, 29262306a36Sopenharmony_ci}; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ciint ov9650_probe(struct sd *sd) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci int err = 0; 29762306a36Sopenharmony_ci u8 prod_id = 0, ver_id = 0, i; 29862306a36Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (force_sensor) { 30162306a36Sopenharmony_ci if (force_sensor == OV9650_SENSOR) { 30262306a36Sopenharmony_ci pr_info("Forcing an %s sensor\n", ov9650.name); 30362306a36Sopenharmony_ci goto sensor_found; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci /* If we want to force another sensor, 30662306a36Sopenharmony_ci don't try to probe this one */ 30762306a36Sopenharmony_ci return -ENODEV; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "Probing for an ov9650 sensor\n"); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* Run the pre-init before probing the sensor */ 31362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) { 31462306a36Sopenharmony_ci u8 data = preinit_ov9650[i][2]; 31562306a36Sopenharmony_ci if (preinit_ov9650[i][0] == SENSOR) 31662306a36Sopenharmony_ci err = m5602_write_sensor(sd, 31762306a36Sopenharmony_ci preinit_ov9650[i][1], &data, 1); 31862306a36Sopenharmony_ci else 31962306a36Sopenharmony_ci err = m5602_write_bridge(sd, 32062306a36Sopenharmony_ci preinit_ov9650[i][1], data); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (err < 0) 32462306a36Sopenharmony_ci return err; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1)) 32762306a36Sopenharmony_ci return -ENODEV; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1)) 33062306a36Sopenharmony_ci return -ENODEV; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if ((prod_id == 0x96) && (ver_id == 0x52)) { 33362306a36Sopenharmony_ci pr_info("Detected an ov9650 sensor\n"); 33462306a36Sopenharmony_ci goto sensor_found; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci return -ENODEV; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cisensor_found: 33962306a36Sopenharmony_ci sd->gspca_dev.cam.cam_mode = ov9650_modes; 34062306a36Sopenharmony_ci sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ciint ov9650_init(struct sd *sd) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci int i, err = 0; 34862306a36Sopenharmony_ci u8 data; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (dump_sensor) 35162306a36Sopenharmony_ci ov9650_dump_registers(sd); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) { 35462306a36Sopenharmony_ci data = init_ov9650[i][2]; 35562306a36Sopenharmony_ci if (init_ov9650[i][0] == SENSOR) 35662306a36Sopenharmony_ci err = m5602_write_sensor(sd, init_ov9650[i][1], 35762306a36Sopenharmony_ci &data, 1); 35862306a36Sopenharmony_ci else 35962306a36Sopenharmony_ci err = m5602_write_bridge(sd, init_ov9650[i][1], data); 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ciint ov9650_init_controls(struct sd *sd) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci sd->gspca_dev.vdev.ctrl_handler = hdl; 37062306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 9); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 37362306a36Sopenharmony_ci V4L2_CID_AUTO_WHITE_BALANCE, 37462306a36Sopenharmony_ci 0, 1, 1, 1); 37562306a36Sopenharmony_ci sd->red_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 37662306a36Sopenharmony_ci V4L2_CID_RED_BALANCE, 0, 255, 1, 37762306a36Sopenharmony_ci RED_GAIN_DEFAULT); 37862306a36Sopenharmony_ci sd->blue_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 37962306a36Sopenharmony_ci V4L2_CID_BLUE_BALANCE, 0, 255, 1, 38062306a36Sopenharmony_ci BLUE_GAIN_DEFAULT); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &ov9650_ctrl_ops, 38362306a36Sopenharmony_ci V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); 38462306a36Sopenharmony_ci sd->expo = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_EXPOSURE, 38562306a36Sopenharmony_ci 0, 0x1ff, 4, EXPOSURE_DEFAULT); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci sd->autogain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 38862306a36Sopenharmony_ci V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 38962306a36Sopenharmony_ci sd->gain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_GAIN, 0, 39062306a36Sopenharmony_ci 0x3ff, 1, GAIN_DEFAULT); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci sd->hflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_HFLIP, 39362306a36Sopenharmony_ci 0, 1, 1, 0); 39462306a36Sopenharmony_ci sd->vflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_VFLIP, 39562306a36Sopenharmony_ci 0, 1, 1, 0); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (hdl->error) { 39862306a36Sopenharmony_ci pr_err("Could not initialize controls\n"); 39962306a36Sopenharmony_ci return hdl->error; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci v4l2_ctrl_auto_cluster(3, &sd->auto_white_bal, 0, false); 40362306a36Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false); 40462306a36Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); 40562306a36Sopenharmony_ci v4l2_ctrl_cluster(2, &sd->hflip); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ciint ov9650_start(struct sd *sd) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci u8 data; 41362306a36Sopenharmony_ci int i, err = 0; 41462306a36Sopenharmony_ci struct cam *cam = &sd->gspca_dev.cam; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; 41762306a36Sopenharmony_ci int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; 41862306a36Sopenharmony_ci int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; 41962306a36Sopenharmony_ci int hor_offs = OV9650_LEFT_OFFSET; 42062306a36Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if ((!dmi_check_system(ov9650_flip_dmi_table) && 42362306a36Sopenharmony_ci sd->vflip->val) || 42462306a36Sopenharmony_ci (dmi_check_system(ov9650_flip_dmi_table) && 42562306a36Sopenharmony_ci !sd->vflip->val)) 42662306a36Sopenharmony_ci ver_offs--; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (width <= 320) 42962306a36Sopenharmony_ci hor_offs /= 2; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Synthesize the vsync/hsync setup */ 43262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) { 43362306a36Sopenharmony_ci if (res_init_ov9650[i][0] == BRIDGE) 43462306a36Sopenharmony_ci err = m5602_write_bridge(sd, res_init_ov9650[i][1], 43562306a36Sopenharmony_ci res_init_ov9650[i][2]); 43662306a36Sopenharmony_ci else if (res_init_ov9650[i][0] == SENSOR) { 43762306a36Sopenharmony_ci data = res_init_ov9650[i][2]; 43862306a36Sopenharmony_ci err = m5602_write_sensor(sd, 43962306a36Sopenharmony_ci res_init_ov9650[i][1], &data, 1); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci if (err < 0) 44362306a36Sopenharmony_ci return err; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 44662306a36Sopenharmony_ci ((ver_offs >> 8) & 0xff)); 44762306a36Sopenharmony_ci if (err < 0) 44862306a36Sopenharmony_ci return err; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff)); 45162306a36Sopenharmony_ci if (err < 0) 45262306a36Sopenharmony_ci return err; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); 45562306a36Sopenharmony_ci if (err < 0) 45662306a36Sopenharmony_ci return err; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); 45962306a36Sopenharmony_ci if (err < 0) 46062306a36Sopenharmony_ci return err; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); 46362306a36Sopenharmony_ci if (err < 0) 46462306a36Sopenharmony_ci return err; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci for (i = 0; i < 2 && !err; i++) 46762306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); 46862306a36Sopenharmony_ci if (err < 0) 46962306a36Sopenharmony_ci return err; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); 47262306a36Sopenharmony_ci if (err < 0) 47362306a36Sopenharmony_ci return err; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2); 47662306a36Sopenharmony_ci if (err < 0) 47762306a36Sopenharmony_ci return err; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 48062306a36Sopenharmony_ci (hor_offs >> 8) & 0xff); 48162306a36Sopenharmony_ci if (err < 0) 48262306a36Sopenharmony_ci return err; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff); 48562306a36Sopenharmony_ci if (err < 0) 48662306a36Sopenharmony_ci return err; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 48962306a36Sopenharmony_ci ((width + hor_offs) >> 8) & 0xff); 49062306a36Sopenharmony_ci if (err < 0) 49162306a36Sopenharmony_ci return err; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 49462306a36Sopenharmony_ci ((width + hor_offs) & 0xff)); 49562306a36Sopenharmony_ci if (err < 0) 49662306a36Sopenharmony_ci return err; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); 49962306a36Sopenharmony_ci if (err < 0) 50062306a36Sopenharmony_ci return err; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci switch (width) { 50362306a36Sopenharmony_ci case 640: 50462306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Configuring camera for VGA mode\n"); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci data = OV9650_VGA_SELECT | OV9650_RGB_SELECT | 50762306a36Sopenharmony_ci OV9650_RAW_RGB_SELECT; 50862306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci case 352: 51262306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Configuring camera for CIF mode\n"); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci data = OV9650_CIF_SELECT | OV9650_RGB_SELECT | 51562306a36Sopenharmony_ci OV9650_RAW_RGB_SELECT; 51662306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci case 320: 52062306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QVGA mode\n"); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT | 52362306a36Sopenharmony_ci OV9650_RAW_RGB_SELECT; 52462306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 52562306a36Sopenharmony_ci break; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci case 176: 52862306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QCIF mode\n"); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT | 53162306a36Sopenharmony_ci OV9650_RAW_RGB_SELECT; 53262306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci return err; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ciint ov9650_stop(struct sd *sd) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X; 54162306a36Sopenharmony_ci return m5602_write_sensor(sd, OV9650_COM2, &data, 1); 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_civoid ov9650_disconnect(struct sd *sd) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci ov9650_stop(sd); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci sd->sensor = NULL; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 55462306a36Sopenharmony_ci u8 i2c_data; 55562306a36Sopenharmony_ci int err; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", val); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* The 6 MSBs */ 56062306a36Sopenharmony_ci i2c_data = (val >> 10) & 0x3f; 56162306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_AECHM, 56262306a36Sopenharmony_ci &i2c_data, 1); 56362306a36Sopenharmony_ci if (err < 0) 56462306a36Sopenharmony_ci return err; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* The 8 middle bits */ 56762306a36Sopenharmony_ci i2c_data = (val >> 2) & 0xff; 56862306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_AECH, 56962306a36Sopenharmony_ci &i2c_data, 1); 57062306a36Sopenharmony_ci if (err < 0) 57162306a36Sopenharmony_ci return err; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* The 2 LSBs */ 57462306a36Sopenharmony_ci i2c_data = val & 0x03; 57562306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1); 57662306a36Sopenharmony_ci return err; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci int err; 58262306a36Sopenharmony_ci u8 i2c_data; 58362306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Setting gain to %d\n", val); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* The 2 MSB */ 58862306a36Sopenharmony_ci /* Read the OV9650_VREF register first to avoid 58962306a36Sopenharmony_ci corrupting the VREF high and low bits */ 59062306a36Sopenharmony_ci err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); 59162306a36Sopenharmony_ci if (err < 0) 59262306a36Sopenharmony_ci return err; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* Mask away all uninteresting bits */ 59562306a36Sopenharmony_ci i2c_data = ((val & 0x0300) >> 2) | 59662306a36Sopenharmony_ci (i2c_data & 0x3f); 59762306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1); 59862306a36Sopenharmony_ci if (err < 0) 59962306a36Sopenharmony_ci return err; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* The 8 LSBs */ 60262306a36Sopenharmony_ci i2c_data = val & 0xff; 60362306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); 60462306a36Sopenharmony_ci return err; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci int err; 61062306a36Sopenharmony_ci u8 i2c_data; 61162306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set red gain to %d\n", val); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci i2c_data = val & 0xff; 61662306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1); 61762306a36Sopenharmony_ci return err; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci int err; 62362306a36Sopenharmony_ci u8 i2c_data; 62462306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set blue gain to %d\n", val); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci i2c_data = val & 0xff; 62962306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1); 63062306a36Sopenharmony_ci return err; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int ov9650_set_hvflip(struct gspca_dev *gspca_dev) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci int err; 63662306a36Sopenharmony_ci u8 i2c_data; 63762306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 63862306a36Sopenharmony_ci int hflip = sd->hflip->val; 63962306a36Sopenharmony_ci int vflip = sd->vflip->val; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set hvflip to %d %d\n", hflip, vflip); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (dmi_check_system(ov9650_flip_dmi_table)) 64462306a36Sopenharmony_ci vflip = !vflip; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci i2c_data = (hflip << 5) | (vflip << 4); 64762306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); 64862306a36Sopenharmony_ci if (err < 0) 64962306a36Sopenharmony_ci return err; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* When vflip is toggled we need to readjust the bridge hsync/vsync */ 65262306a36Sopenharmony_ci if (gspca_dev->streaming) 65362306a36Sopenharmony_ci err = ov9650_start(sd); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return err; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, 65962306a36Sopenharmony_ci __s32 val) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci int err; 66262306a36Sopenharmony_ci u8 i2c_data; 66362306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set auto exposure control to %d\n", val); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); 66862306a36Sopenharmony_ci if (err < 0) 66962306a36Sopenharmony_ci return err; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci val = (val == V4L2_EXPOSURE_AUTO); 67262306a36Sopenharmony_ci i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, 67862306a36Sopenharmony_ci __s32 val) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci int err; 68162306a36Sopenharmony_ci u8 i2c_data; 68262306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set auto white balance to %d\n", val); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); 68762306a36Sopenharmony_ci if (err < 0) 68862306a36Sopenharmony_ci return err; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); 69162306a36Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci return err; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci int err; 69962306a36Sopenharmony_ci u8 i2c_data; 70062306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set auto gain control to %d\n", val); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); 70562306a36Sopenharmony_ci if (err < 0) 70662306a36Sopenharmony_ci return err; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic int ov9650_s_ctrl(struct v4l2_ctrl *ctrl) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct gspca_dev *gspca_dev = 71662306a36Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 71762306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 71862306a36Sopenharmony_ci int err; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (!gspca_dev->streaming) 72162306a36Sopenharmony_ci return 0; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci switch (ctrl->id) { 72462306a36Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 72562306a36Sopenharmony_ci err = ov9650_set_auto_white_balance(gspca_dev, ctrl->val); 72662306a36Sopenharmony_ci if (err || ctrl->val) 72762306a36Sopenharmony_ci return err; 72862306a36Sopenharmony_ci err = ov9650_set_red_balance(gspca_dev, sd->red_bal->val); 72962306a36Sopenharmony_ci if (err) 73062306a36Sopenharmony_ci return err; 73162306a36Sopenharmony_ci err = ov9650_set_blue_balance(gspca_dev, sd->blue_bal->val); 73262306a36Sopenharmony_ci break; 73362306a36Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 73462306a36Sopenharmony_ci err = ov9650_set_auto_exposure(gspca_dev, ctrl->val); 73562306a36Sopenharmony_ci if (err || ctrl->val == V4L2_EXPOSURE_AUTO) 73662306a36Sopenharmony_ci return err; 73762306a36Sopenharmony_ci err = ov9650_set_exposure(gspca_dev, sd->expo->val); 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci case V4L2_CID_AUTOGAIN: 74062306a36Sopenharmony_ci err = ov9650_set_auto_gain(gspca_dev, ctrl->val); 74162306a36Sopenharmony_ci if (err || ctrl->val) 74262306a36Sopenharmony_ci return err; 74362306a36Sopenharmony_ci err = ov9650_set_gain(gspca_dev, sd->gain->val); 74462306a36Sopenharmony_ci break; 74562306a36Sopenharmony_ci case V4L2_CID_HFLIP: 74662306a36Sopenharmony_ci err = ov9650_set_hvflip(gspca_dev); 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci default: 74962306a36Sopenharmony_ci return -EINVAL; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci return err; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic void ov9650_dump_registers(struct sd *sd) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int address; 75862306a36Sopenharmony_ci pr_info("Dumping the ov9650 register state\n"); 75962306a36Sopenharmony_ci for (address = 0; address < 0xa9; address++) { 76062306a36Sopenharmony_ci u8 value; 76162306a36Sopenharmony_ci m5602_read_sensor(sd, address, &value, 1); 76262306a36Sopenharmony_ci pr_info("register 0x%x contains 0x%x\n", address, value); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci pr_info("ov9650 register state dump complete\n"); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci pr_info("Probing for which registers that are read/write\n"); 76862306a36Sopenharmony_ci for (address = 0; address < 0xff; address++) { 76962306a36Sopenharmony_ci u8 old_value, ctrl_value; 77062306a36Sopenharmony_ci u8 test_value[2] = {0xff, 0xff}; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci m5602_read_sensor(sd, address, &old_value, 1); 77362306a36Sopenharmony_ci m5602_write_sensor(sd, address, test_value, 1); 77462306a36Sopenharmony_ci m5602_read_sensor(sd, address, &ctrl_value, 1); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (ctrl_value == test_value[0]) 77762306a36Sopenharmony_ci pr_info("register 0x%x is writeable\n", address); 77862306a36Sopenharmony_ci else 77962306a36Sopenharmony_ci pr_info("register 0x%x is read only\n", address); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* Restore original value */ 78262306a36Sopenharmony_ci m5602_write_sensor(sd, address, &old_value, 1); 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci} 785