114ccffa3SMauro Carvalho Chehab 20c0d06caSMauro Carvalho Chehab /* 30c0d06caSMauro Carvalho Chehab * Driver for the ov9650 sensor 40c0d06caSMauro Carvalho Chehab * 50c0d06caSMauro Carvalho Chehab * Copyright (C) 2008 Erik Andrén 60c0d06caSMauro Carvalho Chehab * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. 70c0d06caSMauro Carvalho Chehab * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> 80c0d06caSMauro Carvalho Chehab * 90c0d06caSMauro Carvalho Chehab * Portions of code to USB interface and ALi driver software, 100c0d06caSMauro Carvalho Chehab * Copyright (c) 2006 Willem Duinker 110c0d06caSMauro Carvalho Chehab * v4l2 interface modeled after the V4L2 driver 120c0d06caSMauro Carvalho Chehab * for SN9C10x PC Camera Controllers 130c0d06caSMauro Carvalho Chehab * 140c0d06caSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or 150c0d06caSMauro Carvalho Chehab * modify it under the terms of the GNU General Public License as 160c0d06caSMauro Carvalho Chehab * published by the Free Software Foundation, version 2. 170c0d06caSMauro Carvalho Chehab * 180c0d06caSMauro Carvalho Chehab */ 190c0d06caSMauro Carvalho Chehab 200c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 210c0d06caSMauro Carvalho Chehab 220c0d06caSMauro Carvalho Chehab #include "m5602_ov9650.h" 230c0d06caSMauro Carvalho Chehab 24c84e412fSHans de Goede static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl); 25c84e412fSHans de Goede static void ov9650_dump_registers(struct sd *sd); 260c0d06caSMauro Carvalho Chehab 2714ccffa3SMauro Carvalho Chehab static const unsigned char preinit_ov9650[][3] = { 2814ccffa3SMauro Carvalho Chehab /* [INITCAM] */ 2914ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, 3014ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, 3114ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 3214ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 3314ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 3414ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, 3514ccffa3SMauro Carvalho Chehab 3614ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, 3714ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 3814ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 3914ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 4014ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 4114ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, 4214ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 4314ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, 4414ccffa3SMauro Carvalho Chehab /* Reset chip */ 4514ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, 4614ccffa3SMauro Carvalho Chehab /* Enable double clock */ 4714ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_CLKRC, 0x80}, 4814ccffa3SMauro Carvalho Chehab /* Do something out of spec with the power */ 4914ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_OFON, 0x40} 5014ccffa3SMauro Carvalho Chehab }; 5114ccffa3SMauro Carvalho Chehab 5214ccffa3SMauro Carvalho Chehab static const unsigned char init_ov9650[][3] = { 5314ccffa3SMauro Carvalho Chehab /* [INITCAM] */ 5414ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, 5514ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, 5614ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 5714ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 5814ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 5914ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, 6014ccffa3SMauro Carvalho Chehab 6114ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, 6214ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 6314ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 6414ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 6514ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 6614ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, 6714ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 6814ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, 6914ccffa3SMauro Carvalho Chehab 7014ccffa3SMauro Carvalho Chehab /* Reset chip */ 7114ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, 7214ccffa3SMauro Carvalho Chehab /* One extra reset is needed in order to make the sensor behave 7314ccffa3SMauro Carvalho Chehab properly when resuming from ram, could be a timing issue */ 7414ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, 7514ccffa3SMauro Carvalho Chehab 7614ccffa3SMauro Carvalho Chehab /* Enable double clock */ 7714ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_CLKRC, 0x80}, 7814ccffa3SMauro Carvalho Chehab /* Do something out of spec with the power */ 7914ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_OFON, 0x40}, 8014ccffa3SMauro Carvalho Chehab 8114ccffa3SMauro Carvalho Chehab /* Set fast AGC/AEC algorithm with unlimited step size */ 8214ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC | 8314ccffa3SMauro Carvalho Chehab OV9650_AEC_UNLIM_STEP_SIZE}, 8414ccffa3SMauro Carvalho Chehab 8514ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_CHLF, 0x10}, 8614ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_ARBLM, 0xbf}, 8714ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_ACOM38, 0x81}, 8814ccffa3SMauro Carvalho Chehab /* Turn off color matrix coefficient double option */ 8914ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM16, 0x00}, 9014ccffa3SMauro Carvalho Chehab /* Enable color matrix for RGB/YUV, Delay Y channel, 9114ccffa3SMauro Carvalho Chehab set output Y/UV delay to 1 */ 9214ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM13, 0x19}, 9314ccffa3SMauro Carvalho Chehab /* Enable digital BLC, Set output mode to U Y V Y */ 9414ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_TSLB, 0x0c}, 9514ccffa3SMauro Carvalho Chehab /* Limit the AGC/AEC stable upper region */ 9614ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM24, 0x00}, 9714ccffa3SMauro Carvalho Chehab /* Enable HREF and some out of spec things */ 9814ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM12, 0x73}, 9914ccffa3SMauro Carvalho Chehab /* Set all DBLC offset signs to positive and 10014ccffa3SMauro Carvalho Chehab do some out of spec stuff */ 10114ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_DBLC1, 0xdf}, 10214ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM21, 0x06}, 10314ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_RSVD35, 0x91}, 10414ccffa3SMauro Carvalho Chehab /* Necessary, no camera stream without it */ 10514ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_RSVD16, 0x06}, 10614ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_RSVD94, 0x99}, 10714ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_RSVD95, 0x99}, 10814ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_RSVD96, 0x04}, 10914ccffa3SMauro Carvalho Chehab /* Enable full range output */ 11014ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM15, 0x0}, 11114ccffa3SMauro Carvalho Chehab /* Enable HREF at optical black, enable ADBLC bias, 11214ccffa3SMauro Carvalho Chehab enable ADBLC, reset timings at format change */ 11314ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM6, 0x4b}, 11414ccffa3SMauro Carvalho Chehab /* Subtract 32 from the B channel bias */ 11514ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_BBIAS, 0xa0}, 11614ccffa3SMauro Carvalho Chehab /* Subtract 32 from the Gb channel bias */ 11714ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_GbBIAS, 0xa0}, 11814ccffa3SMauro Carvalho Chehab /* Do not bypass the analog BLC and to some out of spec stuff */ 11914ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_Gr_COM, 0x00}, 12014ccffa3SMauro Carvalho Chehab /* Subtract 32 from the R channel bias */ 12114ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_RBIAS, 0xa0}, 12214ccffa3SMauro Carvalho Chehab /* Subtract 32 from the R channel bias */ 12314ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_RBIAS, 0x0}, 12414ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM26, 0x80}, 12514ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_ACOMA9, 0x98}, 12614ccffa3SMauro Carvalho Chehab /* Set the AGC/AEC stable region upper limit */ 12714ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_AEW, 0x68}, 12814ccffa3SMauro Carvalho Chehab /* Set the AGC/AEC stable region lower limit */ 12914ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_AEB, 0x5c}, 13014ccffa3SMauro Carvalho Chehab /* Set the high and low limit nibbles to 3 */ 13114ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_VPT, 0xc3}, 13214ccffa3SMauro Carvalho Chehab /* Set the Automatic Gain Ceiling (AGC) to 128x, 13314ccffa3SMauro Carvalho Chehab drop VSYNC at frame drop, 13414ccffa3SMauro Carvalho Chehab limit exposure timing, 13514ccffa3SMauro Carvalho Chehab drop frame when the AEC step is larger than the exposure gap */ 13614ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM9, 0x6e}, 13714ccffa3SMauro Carvalho Chehab /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync) 13814ccffa3SMauro Carvalho Chehab and set PWDN to SLVS (slave mode vertical sync) */ 13914ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM10, 0x42}, 14014ccffa3SMauro Carvalho Chehab /* Set horizontal column start high to default value */ 14114ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */ 14214ccffa3SMauro Carvalho Chehab /* Set horizontal column end */ 14314ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */ 14414ccffa3SMauro Carvalho Chehab /* Complementing register to the two writes above */ 14514ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_HREF, 0xb2}, 14614ccffa3SMauro Carvalho Chehab /* Set vertical row start high bits */ 14714ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_VSTRT, 0x02}, 14814ccffa3SMauro Carvalho Chehab /* Set vertical row end low bits */ 14914ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_VSTOP, 0x7e}, 15014ccffa3SMauro Carvalho Chehab /* Set complementing vertical frame control */ 15114ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_VREF, 0x10}, 15214ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_ADC, 0x04}, 15314ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_HV, 0x40}, 15414ccffa3SMauro Carvalho Chehab 15514ccffa3SMauro Carvalho Chehab /* Enable denoise, and white-pixel erase */ 15614ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE | 15714ccffa3SMauro Carvalho Chehab OV9650_WHITE_PIXEL_ENABLE | 15814ccffa3SMauro Carvalho Chehab OV9650_WHITE_PIXEL_OPTION}, 15914ccffa3SMauro Carvalho Chehab 16014ccffa3SMauro Carvalho Chehab /* Enable VARIOPIXEL */ 16114ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL}, 16214ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL}, 16314ccffa3SMauro Carvalho Chehab 16414ccffa3SMauro Carvalho Chehab /* Put the sensor in soft sleep mode */ 16514ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X}, 16614ccffa3SMauro Carvalho Chehab }; 16714ccffa3SMauro Carvalho Chehab 16814ccffa3SMauro Carvalho Chehab static const unsigned char res_init_ov9650[][3] = { 16914ccffa3SMauro Carvalho Chehab {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X}, 17014ccffa3SMauro Carvalho Chehab 17114ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82}, 17214ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00}, 17314ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, 17414ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00}, 17514ccffa3SMauro Carvalho Chehab {BRIDGE, M5602_XB_SIG_INI, 0x01} 17614ccffa3SMauro Carvalho Chehab }; 17714ccffa3SMauro Carvalho Chehab 1780c0d06caSMauro Carvalho Chehab /* Vertically and horizontally flips the image if matched, needed for machines 1790c0d06caSMauro Carvalho Chehab where the sensor is mounted upside down */ 1800c0d06caSMauro Carvalho Chehab static 1810c0d06caSMauro Carvalho Chehab const 1820c0d06caSMauro Carvalho Chehab struct dmi_system_id ov9650_flip_dmi_table[] = { 1830c0d06caSMauro Carvalho Chehab { 1840c0d06caSMauro Carvalho Chehab .ident = "ASUS A6Ja", 1850c0d06caSMauro Carvalho Chehab .matches = { 1860c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 1870c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_PRODUCT_NAME, "A6J") 1880c0d06caSMauro Carvalho Chehab } 1890c0d06caSMauro Carvalho Chehab }, 1900c0d06caSMauro Carvalho Chehab { 1910c0d06caSMauro Carvalho Chehab .ident = "ASUS A6JC", 1920c0d06caSMauro Carvalho Chehab .matches = { 1930c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 1940c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") 1950c0d06caSMauro Carvalho Chehab } 1960c0d06caSMauro Carvalho Chehab }, 1970c0d06caSMauro Carvalho Chehab { 1980c0d06caSMauro Carvalho Chehab .ident = "ASUS A6K", 1990c0d06caSMauro Carvalho Chehab .matches = { 2000c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2010c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_PRODUCT_NAME, "A6K") 2020c0d06caSMauro Carvalho Chehab } 2030c0d06caSMauro Carvalho Chehab }, 2040c0d06caSMauro Carvalho Chehab { 2050c0d06caSMauro Carvalho Chehab .ident = "ASUS A6Kt", 2060c0d06caSMauro Carvalho Chehab .matches = { 2070c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2080c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt") 2090c0d06caSMauro Carvalho Chehab } 2100c0d06caSMauro Carvalho Chehab }, 2110c0d06caSMauro Carvalho Chehab { 2120c0d06caSMauro Carvalho Chehab .ident = "ASUS A6VA", 2130c0d06caSMauro Carvalho Chehab .matches = { 2140c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2150c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_PRODUCT_NAME, "A6VA") 2160c0d06caSMauro Carvalho Chehab } 2170c0d06caSMauro Carvalho Chehab }, 2180c0d06caSMauro Carvalho Chehab { 2190c0d06caSMauro Carvalho Chehab 2200c0d06caSMauro Carvalho Chehab .ident = "ASUS A6VC", 2210c0d06caSMauro Carvalho Chehab .matches = { 2220c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2230c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_PRODUCT_NAME, "A6VC") 2240c0d06caSMauro Carvalho Chehab } 2250c0d06caSMauro Carvalho Chehab }, 2260c0d06caSMauro Carvalho Chehab { 2270c0d06caSMauro Carvalho Chehab .ident = "ASUS A6VM", 2280c0d06caSMauro Carvalho Chehab .matches = { 2290c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2300c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") 2310c0d06caSMauro Carvalho Chehab } 2320c0d06caSMauro Carvalho Chehab }, 2330c0d06caSMauro Carvalho Chehab { 2340c0d06caSMauro Carvalho Chehab .ident = "ASUS A7V", 2350c0d06caSMauro Carvalho Chehab .matches = { 2360c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2370c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_PRODUCT_NAME, "A7V") 2380c0d06caSMauro Carvalho Chehab } 2390c0d06caSMauro Carvalho Chehab }, 2400c0d06caSMauro Carvalho Chehab { 2410c0d06caSMauro Carvalho Chehab .ident = "Alienware Aurora m9700", 2420c0d06caSMauro Carvalho Chehab .matches = { 2430c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_SYS_VENDOR, "alienware"), 2440c0d06caSMauro Carvalho Chehab DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700") 2450c0d06caSMauro Carvalho Chehab } 2460c0d06caSMauro Carvalho Chehab }, 2470c0d06caSMauro Carvalho Chehab {} 2480c0d06caSMauro Carvalho Chehab }; 2490c0d06caSMauro Carvalho Chehab 2500c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format ov9650_modes[] = { 2510c0d06caSMauro Carvalho Chehab { 2520c0d06caSMauro Carvalho Chehab 176, 2530c0d06caSMauro Carvalho Chehab 144, 2540c0d06caSMauro Carvalho Chehab V4L2_PIX_FMT_SBGGR8, 2550c0d06caSMauro Carvalho Chehab V4L2_FIELD_NONE, 2560c0d06caSMauro Carvalho Chehab .sizeimage = 2570c0d06caSMauro Carvalho Chehab 176 * 144, 2580c0d06caSMauro Carvalho Chehab .bytesperline = 176, 2590c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 2600c0d06caSMauro Carvalho Chehab .priv = 9 2610c0d06caSMauro Carvalho Chehab }, { 2620c0d06caSMauro Carvalho Chehab 320, 2630c0d06caSMauro Carvalho Chehab 240, 2640c0d06caSMauro Carvalho Chehab V4L2_PIX_FMT_SBGGR8, 2650c0d06caSMauro Carvalho Chehab V4L2_FIELD_NONE, 2660c0d06caSMauro Carvalho Chehab .sizeimage = 2670c0d06caSMauro Carvalho Chehab 320 * 240, 2680c0d06caSMauro Carvalho Chehab .bytesperline = 320, 2690c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 2700c0d06caSMauro Carvalho Chehab .priv = 8 2710c0d06caSMauro Carvalho Chehab }, { 2720c0d06caSMauro Carvalho Chehab 352, 2730c0d06caSMauro Carvalho Chehab 288, 2740c0d06caSMauro Carvalho Chehab V4L2_PIX_FMT_SBGGR8, 2750c0d06caSMauro Carvalho Chehab V4L2_FIELD_NONE, 2760c0d06caSMauro Carvalho Chehab .sizeimage = 2770c0d06caSMauro Carvalho Chehab 352 * 288, 2780c0d06caSMauro Carvalho Chehab .bytesperline = 352, 2790c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 2800c0d06caSMauro Carvalho Chehab .priv = 9 2810c0d06caSMauro Carvalho Chehab }, { 2820c0d06caSMauro Carvalho Chehab 640, 2830c0d06caSMauro Carvalho Chehab 480, 2840c0d06caSMauro Carvalho Chehab V4L2_PIX_FMT_SBGGR8, 2850c0d06caSMauro Carvalho Chehab V4L2_FIELD_NONE, 2860c0d06caSMauro Carvalho Chehab .sizeimage = 2870c0d06caSMauro Carvalho Chehab 640 * 480, 2880c0d06caSMauro Carvalho Chehab .bytesperline = 640, 2890c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 2900c0d06caSMauro Carvalho Chehab .priv = 9 2910c0d06caSMauro Carvalho Chehab } 2920c0d06caSMauro Carvalho Chehab }; 2930c0d06caSMauro Carvalho Chehab 294c84e412fSHans de Goede static const struct v4l2_ctrl_ops ov9650_ctrl_ops = { 295c84e412fSHans de Goede .s_ctrl = ov9650_s_ctrl, 296c84e412fSHans de Goede }; 2970c0d06caSMauro Carvalho Chehab 2980c0d06caSMauro Carvalho Chehab int ov9650_probe(struct sd *sd) 2990c0d06caSMauro Carvalho Chehab { 3000c0d06caSMauro Carvalho Chehab int err = 0; 3010c0d06caSMauro Carvalho Chehab u8 prod_id = 0, ver_id = 0, i; 302c93396e1STheodore Kilgore struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 3030c0d06caSMauro Carvalho Chehab 3040c0d06caSMauro Carvalho Chehab if (force_sensor) { 3050c0d06caSMauro Carvalho Chehab if (force_sensor == OV9650_SENSOR) { 3060c0d06caSMauro Carvalho Chehab pr_info("Forcing an %s sensor\n", ov9650.name); 3070c0d06caSMauro Carvalho Chehab goto sensor_found; 3080c0d06caSMauro Carvalho Chehab } 3090c0d06caSMauro Carvalho Chehab /* If we want to force another sensor, 3100c0d06caSMauro Carvalho Chehab don't try to probe this one */ 3110c0d06caSMauro Carvalho Chehab return -ENODEV; 3120c0d06caSMauro Carvalho Chehab } 3130c0d06caSMauro Carvalho Chehab 31437d5efb0SJoe Perches gspca_dbg(gspca_dev, D_PROBE, "Probing for an ov9650 sensor\n"); 3150c0d06caSMauro Carvalho Chehab 3160c0d06caSMauro Carvalho Chehab /* Run the pre-init before probing the sensor */ 3170c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) { 3180c0d06caSMauro Carvalho Chehab u8 data = preinit_ov9650[i][2]; 3190c0d06caSMauro Carvalho Chehab if (preinit_ov9650[i][0] == SENSOR) 3200c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, 3210c0d06caSMauro Carvalho Chehab preinit_ov9650[i][1], &data, 1); 3220c0d06caSMauro Carvalho Chehab else 3230c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, 3240c0d06caSMauro Carvalho Chehab preinit_ov9650[i][1], data); 3250c0d06caSMauro Carvalho Chehab } 3260c0d06caSMauro Carvalho Chehab 3270c0d06caSMauro Carvalho Chehab if (err < 0) 3280c0d06caSMauro Carvalho Chehab return err; 3290c0d06caSMauro Carvalho Chehab 3300c0d06caSMauro Carvalho Chehab if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1)) 3310c0d06caSMauro Carvalho Chehab return -ENODEV; 3320c0d06caSMauro Carvalho Chehab 3330c0d06caSMauro Carvalho Chehab if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1)) 3340c0d06caSMauro Carvalho Chehab return -ENODEV; 3350c0d06caSMauro Carvalho Chehab 3360c0d06caSMauro Carvalho Chehab if ((prod_id == 0x96) && (ver_id == 0x52)) { 3370c0d06caSMauro Carvalho Chehab pr_info("Detected an ov9650 sensor\n"); 3380c0d06caSMauro Carvalho Chehab goto sensor_found; 3390c0d06caSMauro Carvalho Chehab } 3400c0d06caSMauro Carvalho Chehab return -ENODEV; 3410c0d06caSMauro Carvalho Chehab 3420c0d06caSMauro Carvalho Chehab sensor_found: 3430c0d06caSMauro Carvalho Chehab sd->gspca_dev.cam.cam_mode = ov9650_modes; 3440c0d06caSMauro Carvalho Chehab sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes); 3450c0d06caSMauro Carvalho Chehab 3460c0d06caSMauro Carvalho Chehab return 0; 3470c0d06caSMauro Carvalho Chehab } 3480c0d06caSMauro Carvalho Chehab 3490c0d06caSMauro Carvalho Chehab int ov9650_init(struct sd *sd) 3500c0d06caSMauro Carvalho Chehab { 3510c0d06caSMauro Carvalho Chehab int i, err = 0; 3520c0d06caSMauro Carvalho Chehab u8 data; 3530c0d06caSMauro Carvalho Chehab 3540c0d06caSMauro Carvalho Chehab if (dump_sensor) 3550c0d06caSMauro Carvalho Chehab ov9650_dump_registers(sd); 3560c0d06caSMauro Carvalho Chehab 3570c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) { 3580c0d06caSMauro Carvalho Chehab data = init_ov9650[i][2]; 3590c0d06caSMauro Carvalho Chehab if (init_ov9650[i][0] == SENSOR) 3600c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, init_ov9650[i][1], 3610c0d06caSMauro Carvalho Chehab &data, 1); 3620c0d06caSMauro Carvalho Chehab else 3630c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, init_ov9650[i][1], data); 3640c0d06caSMauro Carvalho Chehab } 3650c0d06caSMauro Carvalho Chehab 366c84e412fSHans de Goede return 0; 367c84e412fSHans de Goede } 3680c0d06caSMauro Carvalho Chehab 369c84e412fSHans de Goede int ov9650_init_controls(struct sd *sd) 370c84e412fSHans de Goede { 371c84e412fSHans de Goede struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; 3720c0d06caSMauro Carvalho Chehab 373c84e412fSHans de Goede sd->gspca_dev.vdev.ctrl_handler = hdl; 374c84e412fSHans de Goede v4l2_ctrl_handler_init(hdl, 9); 3750c0d06caSMauro Carvalho Chehab 376c84e412fSHans de Goede sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 377c84e412fSHans de Goede V4L2_CID_AUTO_WHITE_BALANCE, 378c84e412fSHans de Goede 0, 1, 1, 1); 379c84e412fSHans de Goede sd->red_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 380c84e412fSHans de Goede V4L2_CID_RED_BALANCE, 0, 255, 1, 381c84e412fSHans de Goede RED_GAIN_DEFAULT); 382c84e412fSHans de Goede sd->blue_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 383c84e412fSHans de Goede V4L2_CID_BLUE_BALANCE, 0, 255, 1, 384c84e412fSHans de Goede BLUE_GAIN_DEFAULT); 3850c0d06caSMauro Carvalho Chehab 386c84e412fSHans de Goede sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &ov9650_ctrl_ops, 387c84e412fSHans de Goede V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); 388c84e412fSHans de Goede sd->expo = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_EXPOSURE, 389c84e412fSHans de Goede 0, 0x1ff, 4, EXPOSURE_DEFAULT); 3900c0d06caSMauro Carvalho Chehab 391c84e412fSHans de Goede sd->autogain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 392c84e412fSHans de Goede V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 393c84e412fSHans de Goede sd->gain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_GAIN, 0, 394c84e412fSHans de Goede 0x3ff, 1, GAIN_DEFAULT); 3950c0d06caSMauro Carvalho Chehab 396c84e412fSHans de Goede sd->hflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_HFLIP, 397c84e412fSHans de Goede 0, 1, 1, 0); 398c84e412fSHans de Goede sd->vflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_VFLIP, 399c84e412fSHans de Goede 0, 1, 1, 0); 4000c0d06caSMauro Carvalho Chehab 401c84e412fSHans de Goede if (hdl->error) { 402c84e412fSHans de Goede pr_err("Could not initialize controls\n"); 403c84e412fSHans de Goede return hdl->error; 404c84e412fSHans de Goede } 4050c0d06caSMauro Carvalho Chehab 406c84e412fSHans de Goede v4l2_ctrl_auto_cluster(3, &sd->auto_white_bal, 0, false); 407c84e412fSHans de Goede v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false); 408c84e412fSHans de Goede v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); 409c84e412fSHans de Goede v4l2_ctrl_cluster(2, &sd->hflip); 410c84e412fSHans de Goede 411c84e412fSHans de Goede return 0; 4120c0d06caSMauro Carvalho Chehab } 4130c0d06caSMauro Carvalho Chehab 4140c0d06caSMauro Carvalho Chehab int ov9650_start(struct sd *sd) 4150c0d06caSMauro Carvalho Chehab { 4160c0d06caSMauro Carvalho Chehab u8 data; 4170c0d06caSMauro Carvalho Chehab int i, err = 0; 4180c0d06caSMauro Carvalho Chehab struct cam *cam = &sd->gspca_dev.cam; 4190c0d06caSMauro Carvalho Chehab 4200c0d06caSMauro Carvalho Chehab int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; 4210c0d06caSMauro Carvalho Chehab int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; 4220c0d06caSMauro Carvalho Chehab int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; 4230c0d06caSMauro Carvalho Chehab int hor_offs = OV9650_LEFT_OFFSET; 424c93396e1STheodore Kilgore struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 4250c0d06caSMauro Carvalho Chehab 4260c0d06caSMauro Carvalho Chehab if ((!dmi_check_system(ov9650_flip_dmi_table) && 427c84e412fSHans de Goede sd->vflip->val) || 4280c0d06caSMauro Carvalho Chehab (dmi_check_system(ov9650_flip_dmi_table) && 429c84e412fSHans de Goede !sd->vflip->val)) 4300c0d06caSMauro Carvalho Chehab ver_offs--; 4310c0d06caSMauro Carvalho Chehab 4320c0d06caSMauro Carvalho Chehab if (width <= 320) 4330c0d06caSMauro Carvalho Chehab hor_offs /= 2; 4340c0d06caSMauro Carvalho Chehab 4350c0d06caSMauro Carvalho Chehab /* Synthesize the vsync/hsync setup */ 4360c0d06caSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) { 4370c0d06caSMauro Carvalho Chehab if (res_init_ov9650[i][0] == BRIDGE) 4380c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, res_init_ov9650[i][1], 4390c0d06caSMauro Carvalho Chehab res_init_ov9650[i][2]); 4400c0d06caSMauro Carvalho Chehab else if (res_init_ov9650[i][0] == SENSOR) { 4410c0d06caSMauro Carvalho Chehab data = res_init_ov9650[i][2]; 4420c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, 4430c0d06caSMauro Carvalho Chehab res_init_ov9650[i][1], &data, 1); 4440c0d06caSMauro Carvalho Chehab } 4450c0d06caSMauro Carvalho Chehab } 4460c0d06caSMauro Carvalho Chehab if (err < 0) 4470c0d06caSMauro Carvalho Chehab return err; 4480c0d06caSMauro Carvalho Chehab 4490c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 4500c0d06caSMauro Carvalho Chehab ((ver_offs >> 8) & 0xff)); 4510c0d06caSMauro Carvalho Chehab if (err < 0) 4520c0d06caSMauro Carvalho Chehab return err; 4530c0d06caSMauro Carvalho Chehab 4540c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff)); 4550c0d06caSMauro Carvalho Chehab if (err < 0) 4560c0d06caSMauro Carvalho Chehab return err; 4570c0d06caSMauro Carvalho Chehab 4580c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); 4590c0d06caSMauro Carvalho Chehab if (err < 0) 4600c0d06caSMauro Carvalho Chehab return err; 4610c0d06caSMauro Carvalho Chehab 4620c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); 4630c0d06caSMauro Carvalho Chehab if (err < 0) 4640c0d06caSMauro Carvalho Chehab return err; 4650c0d06caSMauro Carvalho Chehab 4660c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); 4670c0d06caSMauro Carvalho Chehab if (err < 0) 4680c0d06caSMauro Carvalho Chehab return err; 4690c0d06caSMauro Carvalho Chehab 4700c0d06caSMauro Carvalho Chehab for (i = 0; i < 2 && !err; i++) 4710c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); 4720c0d06caSMauro Carvalho Chehab if (err < 0) 4730c0d06caSMauro Carvalho Chehab return err; 4740c0d06caSMauro Carvalho Chehab 4750c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); 4760c0d06caSMauro Carvalho Chehab if (err < 0) 4770c0d06caSMauro Carvalho Chehab return err; 4780c0d06caSMauro Carvalho Chehab 4790c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2); 4800c0d06caSMauro Carvalho Chehab if (err < 0) 4810c0d06caSMauro Carvalho Chehab return err; 4820c0d06caSMauro Carvalho Chehab 4830c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 4840c0d06caSMauro Carvalho Chehab (hor_offs >> 8) & 0xff); 4850c0d06caSMauro Carvalho Chehab if (err < 0) 4860c0d06caSMauro Carvalho Chehab return err; 4870c0d06caSMauro Carvalho Chehab 4880c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff); 4890c0d06caSMauro Carvalho Chehab if (err < 0) 4900c0d06caSMauro Carvalho Chehab return err; 4910c0d06caSMauro Carvalho Chehab 4920c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 4930c0d06caSMauro Carvalho Chehab ((width + hor_offs) >> 8) & 0xff); 4940c0d06caSMauro Carvalho Chehab if (err < 0) 4950c0d06caSMauro Carvalho Chehab return err; 4960c0d06caSMauro Carvalho Chehab 4970c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 4980c0d06caSMauro Carvalho Chehab ((width + hor_offs) & 0xff)); 4990c0d06caSMauro Carvalho Chehab if (err < 0) 5000c0d06caSMauro Carvalho Chehab return err; 5010c0d06caSMauro Carvalho Chehab 5020c0d06caSMauro Carvalho Chehab err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); 5030c0d06caSMauro Carvalho Chehab if (err < 0) 5040c0d06caSMauro Carvalho Chehab return err; 5050c0d06caSMauro Carvalho Chehab 5060c0d06caSMauro Carvalho Chehab switch (width) { 5070c0d06caSMauro Carvalho Chehab case 640: 50837d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Configuring camera for VGA mode\n"); 5090c0d06caSMauro Carvalho Chehab 5100c0d06caSMauro Carvalho Chehab data = OV9650_VGA_SELECT | OV9650_RGB_SELECT | 5110c0d06caSMauro Carvalho Chehab OV9650_RAW_RGB_SELECT; 5120c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 5130c0d06caSMauro Carvalho Chehab break; 5140c0d06caSMauro Carvalho Chehab 5150c0d06caSMauro Carvalho Chehab case 352: 51637d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Configuring camera for CIF mode\n"); 5170c0d06caSMauro Carvalho Chehab 5180c0d06caSMauro Carvalho Chehab data = OV9650_CIF_SELECT | OV9650_RGB_SELECT | 5190c0d06caSMauro Carvalho Chehab OV9650_RAW_RGB_SELECT; 5200c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 5210c0d06caSMauro Carvalho Chehab break; 5220c0d06caSMauro Carvalho Chehab 5230c0d06caSMauro Carvalho Chehab case 320: 52437d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QVGA mode\n"); 5250c0d06caSMauro Carvalho Chehab 5260c0d06caSMauro Carvalho Chehab data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT | 5270c0d06caSMauro Carvalho Chehab OV9650_RAW_RGB_SELECT; 5280c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 5290c0d06caSMauro Carvalho Chehab break; 5300c0d06caSMauro Carvalho Chehab 5310c0d06caSMauro Carvalho Chehab case 176: 53237d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QCIF mode\n"); 5330c0d06caSMauro Carvalho Chehab 5340c0d06caSMauro Carvalho Chehab data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT | 5350c0d06caSMauro Carvalho Chehab OV9650_RAW_RGB_SELECT; 5360c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 5370c0d06caSMauro Carvalho Chehab break; 5380c0d06caSMauro Carvalho Chehab } 5390c0d06caSMauro Carvalho Chehab return err; 5400c0d06caSMauro Carvalho Chehab } 5410c0d06caSMauro Carvalho Chehab 5420c0d06caSMauro Carvalho Chehab int ov9650_stop(struct sd *sd) 5430c0d06caSMauro Carvalho Chehab { 5440c0d06caSMauro Carvalho Chehab u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X; 5450c0d06caSMauro Carvalho Chehab return m5602_write_sensor(sd, OV9650_COM2, &data, 1); 5460c0d06caSMauro Carvalho Chehab } 5470c0d06caSMauro Carvalho Chehab 5480c0d06caSMauro Carvalho Chehab void ov9650_disconnect(struct sd *sd) 5490c0d06caSMauro Carvalho Chehab { 5500c0d06caSMauro Carvalho Chehab ov9650_stop(sd); 5510c0d06caSMauro Carvalho Chehab 5520c0d06caSMauro Carvalho Chehab sd->sensor = NULL; 5530c0d06caSMauro Carvalho Chehab } 5540c0d06caSMauro Carvalho Chehab 5550c0d06caSMauro Carvalho Chehab static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 5560c0d06caSMauro Carvalho Chehab { 5570c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 5580c0d06caSMauro Carvalho Chehab u8 i2c_data; 5590c0d06caSMauro Carvalho Chehab int err; 5600c0d06caSMauro Carvalho Chehab 56137d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", val); 5620c0d06caSMauro Carvalho Chehab 5630c0d06caSMauro Carvalho Chehab /* The 6 MSBs */ 5640c0d06caSMauro Carvalho Chehab i2c_data = (val >> 10) & 0x3f; 5650c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_AECHM, 5660c0d06caSMauro Carvalho Chehab &i2c_data, 1); 5670c0d06caSMauro Carvalho Chehab if (err < 0) 5680c0d06caSMauro Carvalho Chehab return err; 5690c0d06caSMauro Carvalho Chehab 5700c0d06caSMauro Carvalho Chehab /* The 8 middle bits */ 5710c0d06caSMauro Carvalho Chehab i2c_data = (val >> 2) & 0xff; 5720c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_AECH, 5730c0d06caSMauro Carvalho Chehab &i2c_data, 1); 5740c0d06caSMauro Carvalho Chehab if (err < 0) 5750c0d06caSMauro Carvalho Chehab return err; 5760c0d06caSMauro Carvalho Chehab 5770c0d06caSMauro Carvalho Chehab /* The 2 LSBs */ 5780c0d06caSMauro Carvalho Chehab i2c_data = val & 0x03; 5790c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1); 5800c0d06caSMauro Carvalho Chehab return err; 5810c0d06caSMauro Carvalho Chehab } 5820c0d06caSMauro Carvalho Chehab 5830c0d06caSMauro Carvalho Chehab static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) 5840c0d06caSMauro Carvalho Chehab { 5850c0d06caSMauro Carvalho Chehab int err; 5860c0d06caSMauro Carvalho Chehab u8 i2c_data; 5870c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 5880c0d06caSMauro Carvalho Chehab 58937d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Setting gain to %d\n", val); 5900c0d06caSMauro Carvalho Chehab 5910c0d06caSMauro Carvalho Chehab /* The 2 MSB */ 5920c0d06caSMauro Carvalho Chehab /* Read the OV9650_VREF register first to avoid 5930c0d06caSMauro Carvalho Chehab corrupting the VREF high and low bits */ 5940c0d06caSMauro Carvalho Chehab err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); 5950c0d06caSMauro Carvalho Chehab if (err < 0) 5960c0d06caSMauro Carvalho Chehab return err; 5970c0d06caSMauro Carvalho Chehab 5980c0d06caSMauro Carvalho Chehab /* Mask away all uninteresting bits */ 5990c0d06caSMauro Carvalho Chehab i2c_data = ((val & 0x0300) >> 2) | 6000c0d06caSMauro Carvalho Chehab (i2c_data & 0x3f); 6010c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1); 6020c0d06caSMauro Carvalho Chehab if (err < 0) 6030c0d06caSMauro Carvalho Chehab return err; 6040c0d06caSMauro Carvalho Chehab 6050c0d06caSMauro Carvalho Chehab /* The 8 LSBs */ 6060c0d06caSMauro Carvalho Chehab i2c_data = val & 0xff; 6070c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); 6080c0d06caSMauro Carvalho Chehab return err; 6090c0d06caSMauro Carvalho Chehab } 6100c0d06caSMauro Carvalho Chehab 6110c0d06caSMauro Carvalho Chehab static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) 6120c0d06caSMauro Carvalho Chehab { 6130c0d06caSMauro Carvalho Chehab int err; 6140c0d06caSMauro Carvalho Chehab u8 i2c_data; 6150c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 6160c0d06caSMauro Carvalho Chehab 61737d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Set red gain to %d\n", val); 6180c0d06caSMauro Carvalho Chehab 6190c0d06caSMauro Carvalho Chehab i2c_data = val & 0xff; 6200c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1); 6210c0d06caSMauro Carvalho Chehab return err; 6220c0d06caSMauro Carvalho Chehab } 6230c0d06caSMauro Carvalho Chehab 6240c0d06caSMauro Carvalho Chehab static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) 6250c0d06caSMauro Carvalho Chehab { 6260c0d06caSMauro Carvalho Chehab int err; 6270c0d06caSMauro Carvalho Chehab u8 i2c_data; 6280c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 6290c0d06caSMauro Carvalho Chehab 63037d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Set blue gain to %d\n", val); 6310c0d06caSMauro Carvalho Chehab 6320c0d06caSMauro Carvalho Chehab i2c_data = val & 0xff; 6330c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1); 6340c0d06caSMauro Carvalho Chehab return err; 6350c0d06caSMauro Carvalho Chehab } 6360c0d06caSMauro Carvalho Chehab 637c84e412fSHans de Goede static int ov9650_set_hvflip(struct gspca_dev *gspca_dev) 6380c0d06caSMauro Carvalho Chehab { 6390c0d06caSMauro Carvalho Chehab int err; 6400c0d06caSMauro Carvalho Chehab u8 i2c_data; 6410c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 642c84e412fSHans de Goede int hflip = sd->hflip->val; 643c84e412fSHans de Goede int vflip = sd->vflip->val; 6440c0d06caSMauro Carvalho Chehab 64537d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Set hvflip to %d %d\n", hflip, vflip); 6460c0d06caSMauro Carvalho Chehab 6470c0d06caSMauro Carvalho Chehab if (dmi_check_system(ov9650_flip_dmi_table)) 648c84e412fSHans de Goede vflip = !vflip; 6490c0d06caSMauro Carvalho Chehab 650c84e412fSHans de Goede i2c_data = (hflip << 5) | (vflip << 4); 6510c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); 6520c0d06caSMauro Carvalho Chehab if (err < 0) 6530c0d06caSMauro Carvalho Chehab return err; 6540c0d06caSMauro Carvalho Chehab 6550c0d06caSMauro Carvalho Chehab /* When vflip is toggled we need to readjust the bridge hsync/vsync */ 6560c0d06caSMauro Carvalho Chehab if (gspca_dev->streaming) 6570c0d06caSMauro Carvalho Chehab err = ov9650_start(sd); 6580c0d06caSMauro Carvalho Chehab 6590c0d06caSMauro Carvalho Chehab return err; 6600c0d06caSMauro Carvalho Chehab } 6610c0d06caSMauro Carvalho Chehab 6620c0d06caSMauro Carvalho Chehab static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, 6630c0d06caSMauro Carvalho Chehab __s32 val) 6640c0d06caSMauro Carvalho Chehab { 6650c0d06caSMauro Carvalho Chehab int err; 6660c0d06caSMauro Carvalho Chehab u8 i2c_data; 6670c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 6680c0d06caSMauro Carvalho Chehab 66937d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Set auto exposure control to %d\n", val); 6700c0d06caSMauro Carvalho Chehab 6710c0d06caSMauro Carvalho Chehab err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); 6720c0d06caSMauro Carvalho Chehab if (err < 0) 6730c0d06caSMauro Carvalho Chehab return err; 6740c0d06caSMauro Carvalho Chehab 675c84e412fSHans de Goede val = (val == V4L2_EXPOSURE_AUTO); 6760c0d06caSMauro Carvalho Chehab i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); 6770c0d06caSMauro Carvalho Chehab 6780c0d06caSMauro Carvalho Chehab return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); 6790c0d06caSMauro Carvalho Chehab } 6800c0d06caSMauro Carvalho Chehab 6810c0d06caSMauro Carvalho Chehab static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, 6820c0d06caSMauro Carvalho Chehab __s32 val) 6830c0d06caSMauro Carvalho Chehab { 6840c0d06caSMauro Carvalho Chehab int err; 6850c0d06caSMauro Carvalho Chehab u8 i2c_data; 6860c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 6870c0d06caSMauro Carvalho Chehab 68837d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Set auto white balance to %d\n", val); 6890c0d06caSMauro Carvalho Chehab 6900c0d06caSMauro Carvalho Chehab err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); 6910c0d06caSMauro Carvalho Chehab if (err < 0) 6920c0d06caSMauro Carvalho Chehab return err; 6930c0d06caSMauro Carvalho Chehab 6940c0d06caSMauro Carvalho Chehab i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); 6950c0d06caSMauro Carvalho Chehab err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); 6960c0d06caSMauro Carvalho Chehab 6970c0d06caSMauro Carvalho Chehab return err; 6980c0d06caSMauro Carvalho Chehab } 6990c0d06caSMauro Carvalho Chehab 7000c0d06caSMauro Carvalho Chehab static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) 7010c0d06caSMauro Carvalho Chehab { 7020c0d06caSMauro Carvalho Chehab int err; 7030c0d06caSMauro Carvalho Chehab u8 i2c_data; 7040c0d06caSMauro Carvalho Chehab struct sd *sd = (struct sd *) gspca_dev; 7050c0d06caSMauro Carvalho Chehab 70637d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "Set auto gain control to %d\n", val); 7070c0d06caSMauro Carvalho Chehab 7080c0d06caSMauro Carvalho Chehab err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); 7090c0d06caSMauro Carvalho Chehab if (err < 0) 7100c0d06caSMauro Carvalho Chehab return err; 7110c0d06caSMauro Carvalho Chehab 7120c0d06caSMauro Carvalho Chehab i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); 7130c0d06caSMauro Carvalho Chehab 7140c0d06caSMauro Carvalho Chehab return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); 7150c0d06caSMauro Carvalho Chehab } 7160c0d06caSMauro Carvalho Chehab 717c84e412fSHans de Goede static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl) 718c84e412fSHans de Goede { 719c84e412fSHans de Goede struct gspca_dev *gspca_dev = 720c84e412fSHans de Goede container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 721c84e412fSHans de Goede struct sd *sd = (struct sd *) gspca_dev; 722c84e412fSHans de Goede int err; 723c84e412fSHans de Goede 724c84e412fSHans de Goede if (!gspca_dev->streaming) 725c84e412fSHans de Goede return 0; 726c84e412fSHans de Goede 727c84e412fSHans de Goede switch (ctrl->id) { 728c84e412fSHans de Goede case V4L2_CID_AUTO_WHITE_BALANCE: 729c84e412fSHans de Goede err = ov9650_set_auto_white_balance(gspca_dev, ctrl->val); 730c84e412fSHans de Goede if (err || ctrl->val) 731c84e412fSHans de Goede return err; 732c84e412fSHans de Goede err = ov9650_set_red_balance(gspca_dev, sd->red_bal->val); 733c84e412fSHans de Goede if (err) 734c84e412fSHans de Goede return err; 735c84e412fSHans de Goede err = ov9650_set_blue_balance(gspca_dev, sd->blue_bal->val); 736c84e412fSHans de Goede break; 737c84e412fSHans de Goede case V4L2_CID_EXPOSURE_AUTO: 738c84e412fSHans de Goede err = ov9650_set_auto_exposure(gspca_dev, ctrl->val); 739c84e412fSHans de Goede if (err || ctrl->val == V4L2_EXPOSURE_AUTO) 740c84e412fSHans de Goede return err; 741c84e412fSHans de Goede err = ov9650_set_exposure(gspca_dev, sd->expo->val); 742c84e412fSHans de Goede break; 743c84e412fSHans de Goede case V4L2_CID_AUTOGAIN: 744c84e412fSHans de Goede err = ov9650_set_auto_gain(gspca_dev, ctrl->val); 745c84e412fSHans de Goede if (err || ctrl->val) 746c84e412fSHans de Goede return err; 747c84e412fSHans de Goede err = ov9650_set_gain(gspca_dev, sd->gain->val); 748c84e412fSHans de Goede break; 749c84e412fSHans de Goede case V4L2_CID_HFLIP: 750c84e412fSHans de Goede err = ov9650_set_hvflip(gspca_dev); 751c84e412fSHans de Goede break; 752c84e412fSHans de Goede default: 753c84e412fSHans de Goede return -EINVAL; 754c84e412fSHans de Goede } 755c84e412fSHans de Goede 756c84e412fSHans de Goede return err; 757c84e412fSHans de Goede } 758c84e412fSHans de Goede 7590c0d06caSMauro Carvalho Chehab static void ov9650_dump_registers(struct sd *sd) 7600c0d06caSMauro Carvalho Chehab { 7610c0d06caSMauro Carvalho Chehab int address; 7620c0d06caSMauro Carvalho Chehab pr_info("Dumping the ov9650 register state\n"); 7630c0d06caSMauro Carvalho Chehab for (address = 0; address < 0xa9; address++) { 7640c0d06caSMauro Carvalho Chehab u8 value; 7650c0d06caSMauro Carvalho Chehab m5602_read_sensor(sd, address, &value, 1); 7660c0d06caSMauro Carvalho Chehab pr_info("register 0x%x contains 0x%x\n", address, value); 7670c0d06caSMauro Carvalho Chehab } 7680c0d06caSMauro Carvalho Chehab 7690c0d06caSMauro Carvalho Chehab pr_info("ov9650 register state dump complete\n"); 7700c0d06caSMauro Carvalho Chehab 7710c0d06caSMauro Carvalho Chehab pr_info("Probing for which registers that are read/write\n"); 7720c0d06caSMauro Carvalho Chehab for (address = 0; address < 0xff; address++) { 7730c0d06caSMauro Carvalho Chehab u8 old_value, ctrl_value; 7740c0d06caSMauro Carvalho Chehab u8 test_value[2] = {0xff, 0xff}; 7750c0d06caSMauro Carvalho Chehab 7760c0d06caSMauro Carvalho Chehab m5602_read_sensor(sd, address, &old_value, 1); 7770c0d06caSMauro Carvalho Chehab m5602_write_sensor(sd, address, test_value, 1); 7780c0d06caSMauro Carvalho Chehab m5602_read_sensor(sd, address, &ctrl_value, 1); 7790c0d06caSMauro Carvalho Chehab 7800c0d06caSMauro Carvalho Chehab if (ctrl_value == test_value[0]) 7810c0d06caSMauro Carvalho Chehab pr_info("register 0x%x is writeable\n", address); 7820c0d06caSMauro Carvalho Chehab else 7830c0d06caSMauro Carvalho Chehab pr_info("register 0x%x is read only\n", address); 7840c0d06caSMauro Carvalho Chehab 7850c0d06caSMauro Carvalho Chehab /* Restore original value */ 7860c0d06caSMauro Carvalho Chehab m5602_write_sensor(sd, address, &old_value, 1); 7870c0d06caSMauro Carvalho Chehab } 7880c0d06caSMauro Carvalho Chehab } 789