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