1*a10e763bSThomas 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