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