18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Driver for the ov9650 sensor 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Erik Andrén 78c2ecf20Sopenharmony_ci * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. 88c2ecf20Sopenharmony_ci * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Portions of code to USB interface and ALi driver software, 118c2ecf20Sopenharmony_ci * Copyright (c) 2006 Willem Duinker 128c2ecf20Sopenharmony_ci * v4l2 interface modeled after the V4L2 driver 138c2ecf20Sopenharmony_ci * for SN9C10x PC Camera Controllers 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "m5602_ov9650.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int ov9650_s_ctrl(struct v4l2_ctrl *ctrl); 218c2ecf20Sopenharmony_cistatic void ov9650_dump_registers(struct sd *sd); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic const unsigned char preinit_ov9650[][3] = { 248c2ecf20Sopenharmony_ci /* [INITCAM] */ 258c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, 268c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, 278c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 288c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 298c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 308c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, 338c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 348c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 358c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 368c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 378c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, 388c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 398c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, 408c2ecf20Sopenharmony_ci /* Reset chip */ 418c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, 428c2ecf20Sopenharmony_ci /* Enable double clock */ 438c2ecf20Sopenharmony_ci {SENSOR, OV9650_CLKRC, 0x80}, 448c2ecf20Sopenharmony_ci /* Do something out of spec with the power */ 458c2ecf20Sopenharmony_ci {SENSOR, OV9650_OFON, 0x40} 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic const unsigned char init_ov9650[][3] = { 498c2ecf20Sopenharmony_ci /* [INITCAM] */ 508c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, 518c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, 528c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, 538c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, 548c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, 558c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, 588c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, 598c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, 608c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, 618c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, 628c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, 638c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, 648c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Reset chip */ 678c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, 688c2ecf20Sopenharmony_ci /* One extra reset is needed in order to make the sensor behave 698c2ecf20Sopenharmony_ci properly when resuming from ram, could be a timing issue */ 708c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Enable double clock */ 738c2ecf20Sopenharmony_ci {SENSOR, OV9650_CLKRC, 0x80}, 748c2ecf20Sopenharmony_ci /* Do something out of spec with the power */ 758c2ecf20Sopenharmony_ci {SENSOR, OV9650_OFON, 0x40}, 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* Set fast AGC/AEC algorithm with unlimited step size */ 788c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC | 798c2ecf20Sopenharmony_ci OV9650_AEC_UNLIM_STEP_SIZE}, 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci {SENSOR, OV9650_CHLF, 0x10}, 828c2ecf20Sopenharmony_ci {SENSOR, OV9650_ARBLM, 0xbf}, 838c2ecf20Sopenharmony_ci {SENSOR, OV9650_ACOM38, 0x81}, 848c2ecf20Sopenharmony_ci /* Turn off color matrix coefficient double option */ 858c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM16, 0x00}, 868c2ecf20Sopenharmony_ci /* Enable color matrix for RGB/YUV, Delay Y channel, 878c2ecf20Sopenharmony_ci set output Y/UV delay to 1 */ 888c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM13, 0x19}, 898c2ecf20Sopenharmony_ci /* Enable digital BLC, Set output mode to U Y V Y */ 908c2ecf20Sopenharmony_ci {SENSOR, OV9650_TSLB, 0x0c}, 918c2ecf20Sopenharmony_ci /* Limit the AGC/AEC stable upper region */ 928c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM24, 0x00}, 938c2ecf20Sopenharmony_ci /* Enable HREF and some out of spec things */ 948c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM12, 0x73}, 958c2ecf20Sopenharmony_ci /* Set all DBLC offset signs to positive and 968c2ecf20Sopenharmony_ci do some out of spec stuff */ 978c2ecf20Sopenharmony_ci {SENSOR, OV9650_DBLC1, 0xdf}, 988c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM21, 0x06}, 998c2ecf20Sopenharmony_ci {SENSOR, OV9650_RSVD35, 0x91}, 1008c2ecf20Sopenharmony_ci /* Necessary, no camera stream without it */ 1018c2ecf20Sopenharmony_ci {SENSOR, OV9650_RSVD16, 0x06}, 1028c2ecf20Sopenharmony_ci {SENSOR, OV9650_RSVD94, 0x99}, 1038c2ecf20Sopenharmony_ci {SENSOR, OV9650_RSVD95, 0x99}, 1048c2ecf20Sopenharmony_ci {SENSOR, OV9650_RSVD96, 0x04}, 1058c2ecf20Sopenharmony_ci /* Enable full range output */ 1068c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM15, 0x0}, 1078c2ecf20Sopenharmony_ci /* Enable HREF at optical black, enable ADBLC bias, 1088c2ecf20Sopenharmony_ci enable ADBLC, reset timings at format change */ 1098c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM6, 0x4b}, 1108c2ecf20Sopenharmony_ci /* Subtract 32 from the B channel bias */ 1118c2ecf20Sopenharmony_ci {SENSOR, OV9650_BBIAS, 0xa0}, 1128c2ecf20Sopenharmony_ci /* Subtract 32 from the Gb channel bias */ 1138c2ecf20Sopenharmony_ci {SENSOR, OV9650_GbBIAS, 0xa0}, 1148c2ecf20Sopenharmony_ci /* Do not bypass the analog BLC and to some out of spec stuff */ 1158c2ecf20Sopenharmony_ci {SENSOR, OV9650_Gr_COM, 0x00}, 1168c2ecf20Sopenharmony_ci /* Subtract 32 from the R channel bias */ 1178c2ecf20Sopenharmony_ci {SENSOR, OV9650_RBIAS, 0xa0}, 1188c2ecf20Sopenharmony_ci /* Subtract 32 from the R channel bias */ 1198c2ecf20Sopenharmony_ci {SENSOR, OV9650_RBIAS, 0x0}, 1208c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM26, 0x80}, 1218c2ecf20Sopenharmony_ci {SENSOR, OV9650_ACOMA9, 0x98}, 1228c2ecf20Sopenharmony_ci /* Set the AGC/AEC stable region upper limit */ 1238c2ecf20Sopenharmony_ci {SENSOR, OV9650_AEW, 0x68}, 1248c2ecf20Sopenharmony_ci /* Set the AGC/AEC stable region lower limit */ 1258c2ecf20Sopenharmony_ci {SENSOR, OV9650_AEB, 0x5c}, 1268c2ecf20Sopenharmony_ci /* Set the high and low limit nibbles to 3 */ 1278c2ecf20Sopenharmony_ci {SENSOR, OV9650_VPT, 0xc3}, 1288c2ecf20Sopenharmony_ci /* Set the Automatic Gain Ceiling (AGC) to 128x, 1298c2ecf20Sopenharmony_ci drop VSYNC at frame drop, 1308c2ecf20Sopenharmony_ci limit exposure timing, 1318c2ecf20Sopenharmony_ci drop frame when the AEC step is larger than the exposure gap */ 1328c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM9, 0x6e}, 1338c2ecf20Sopenharmony_ci /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync) 1348c2ecf20Sopenharmony_ci and set PWDN to SLVS (slave mode vertical sync) */ 1358c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM10, 0x42}, 1368c2ecf20Sopenharmony_ci /* Set horizontal column start high to default value */ 1378c2ecf20Sopenharmony_ci {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */ 1388c2ecf20Sopenharmony_ci /* Set horizontal column end */ 1398c2ecf20Sopenharmony_ci {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */ 1408c2ecf20Sopenharmony_ci /* Complementing register to the two writes above */ 1418c2ecf20Sopenharmony_ci {SENSOR, OV9650_HREF, 0xb2}, 1428c2ecf20Sopenharmony_ci /* Set vertical row start high bits */ 1438c2ecf20Sopenharmony_ci {SENSOR, OV9650_VSTRT, 0x02}, 1448c2ecf20Sopenharmony_ci /* Set vertical row end low bits */ 1458c2ecf20Sopenharmony_ci {SENSOR, OV9650_VSTOP, 0x7e}, 1468c2ecf20Sopenharmony_ci /* Set complementing vertical frame control */ 1478c2ecf20Sopenharmony_ci {SENSOR, OV9650_VREF, 0x10}, 1488c2ecf20Sopenharmony_ci {SENSOR, OV9650_ADC, 0x04}, 1498c2ecf20Sopenharmony_ci {SENSOR, OV9650_HV, 0x40}, 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Enable denoise, and white-pixel erase */ 1528c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE | 1538c2ecf20Sopenharmony_ci OV9650_WHITE_PIXEL_ENABLE | 1548c2ecf20Sopenharmony_ci OV9650_WHITE_PIXEL_OPTION}, 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Enable VARIOPIXEL */ 1578c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL}, 1588c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL}, 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Put the sensor in soft sleep mode */ 1618c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X}, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const unsigned char res_init_ov9650[][3] = { 1658c2ecf20Sopenharmony_ci {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X}, 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82}, 1688c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00}, 1698c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, 1708c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00}, 1718c2ecf20Sopenharmony_ci {BRIDGE, M5602_XB_SIG_INI, 0x01} 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* Vertically and horizontally flips the image if matched, needed for machines 1758c2ecf20Sopenharmony_ci where the sensor is mounted upside down */ 1768c2ecf20Sopenharmony_cistatic 1778c2ecf20Sopenharmony_ci const 1788c2ecf20Sopenharmony_ci struct dmi_system_id ov9650_flip_dmi_table[] = { 1798c2ecf20Sopenharmony_ci { 1808c2ecf20Sopenharmony_ci .ident = "ASUS A6Ja", 1818c2ecf20Sopenharmony_ci .matches = { 1828c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 1838c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6J") 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci }, 1868c2ecf20Sopenharmony_ci { 1878c2ecf20Sopenharmony_ci .ident = "ASUS A6JC", 1888c2ecf20Sopenharmony_ci .matches = { 1898c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 1908c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci }, 1938c2ecf20Sopenharmony_ci { 1948c2ecf20Sopenharmony_ci .ident = "ASUS A6K", 1958c2ecf20Sopenharmony_ci .matches = { 1968c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 1978c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6K") 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci }, 2008c2ecf20Sopenharmony_ci { 2018c2ecf20Sopenharmony_ci .ident = "ASUS A6Kt", 2028c2ecf20Sopenharmony_ci .matches = { 2038c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2048c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt") 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci }, 2078c2ecf20Sopenharmony_ci { 2088c2ecf20Sopenharmony_ci .ident = "ASUS A6VA", 2098c2ecf20Sopenharmony_ci .matches = { 2108c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2118c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6VA") 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci }, 2148c2ecf20Sopenharmony_ci { 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci .ident = "ASUS A6VC", 2178c2ecf20Sopenharmony_ci .matches = { 2188c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2198c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6VC") 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci }, 2228c2ecf20Sopenharmony_ci { 2238c2ecf20Sopenharmony_ci .ident = "ASUS A6VM", 2248c2ecf20Sopenharmony_ci .matches = { 2258c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2268c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci }, 2298c2ecf20Sopenharmony_ci { 2308c2ecf20Sopenharmony_ci .ident = "ASUS A7V", 2318c2ecf20Sopenharmony_ci .matches = { 2328c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 2338c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A7V") 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci }, 2368c2ecf20Sopenharmony_ci { 2378c2ecf20Sopenharmony_ci .ident = "Alienware Aurora m9700", 2388c2ecf20Sopenharmony_ci .matches = { 2398c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "alienware"), 2408c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700") 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci }, 2438c2ecf20Sopenharmony_ci {} 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic struct v4l2_pix_format ov9650_modes[] = { 2478c2ecf20Sopenharmony_ci { 2488c2ecf20Sopenharmony_ci 176, 2498c2ecf20Sopenharmony_ci 144, 2508c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 2518c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 2528c2ecf20Sopenharmony_ci .sizeimage = 2538c2ecf20Sopenharmony_ci 176 * 144, 2548c2ecf20Sopenharmony_ci .bytesperline = 176, 2558c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 2568c2ecf20Sopenharmony_ci .priv = 9 2578c2ecf20Sopenharmony_ci }, { 2588c2ecf20Sopenharmony_ci 320, 2598c2ecf20Sopenharmony_ci 240, 2608c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 2618c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 2628c2ecf20Sopenharmony_ci .sizeimage = 2638c2ecf20Sopenharmony_ci 320 * 240, 2648c2ecf20Sopenharmony_ci .bytesperline = 320, 2658c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 2668c2ecf20Sopenharmony_ci .priv = 8 2678c2ecf20Sopenharmony_ci }, { 2688c2ecf20Sopenharmony_ci 352, 2698c2ecf20Sopenharmony_ci 288, 2708c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 2718c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 2728c2ecf20Sopenharmony_ci .sizeimage = 2738c2ecf20Sopenharmony_ci 352 * 288, 2748c2ecf20Sopenharmony_ci .bytesperline = 352, 2758c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 2768c2ecf20Sopenharmony_ci .priv = 9 2778c2ecf20Sopenharmony_ci }, { 2788c2ecf20Sopenharmony_ci 640, 2798c2ecf20Sopenharmony_ci 480, 2808c2ecf20Sopenharmony_ci V4L2_PIX_FMT_SBGGR8, 2818c2ecf20Sopenharmony_ci V4L2_FIELD_NONE, 2828c2ecf20Sopenharmony_ci .sizeimage = 2838c2ecf20Sopenharmony_ci 640 * 480, 2848c2ecf20Sopenharmony_ci .bytesperline = 640, 2858c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 2868c2ecf20Sopenharmony_ci .priv = 9 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops ov9650_ctrl_ops = { 2918c2ecf20Sopenharmony_ci .s_ctrl = ov9650_s_ctrl, 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciint ov9650_probe(struct sd *sd) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci int err = 0; 2978c2ecf20Sopenharmony_ci u8 prod_id = 0, ver_id = 0, i; 2988c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (force_sensor) { 3018c2ecf20Sopenharmony_ci if (force_sensor == OV9650_SENSOR) { 3028c2ecf20Sopenharmony_ci pr_info("Forcing an %s sensor\n", ov9650.name); 3038c2ecf20Sopenharmony_ci goto sensor_found; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci /* If we want to force another sensor, 3068c2ecf20Sopenharmony_ci don't try to probe this one */ 3078c2ecf20Sopenharmony_ci return -ENODEV; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "Probing for an ov9650 sensor\n"); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* Run the pre-init before probing the sensor */ 3138c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) { 3148c2ecf20Sopenharmony_ci u8 data = preinit_ov9650[i][2]; 3158c2ecf20Sopenharmony_ci if (preinit_ov9650[i][0] == SENSOR) 3168c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, 3178c2ecf20Sopenharmony_ci preinit_ov9650[i][1], &data, 1); 3188c2ecf20Sopenharmony_ci else 3198c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, 3208c2ecf20Sopenharmony_ci preinit_ov9650[i][1], data); 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (err < 0) 3248c2ecf20Sopenharmony_ci return err; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1)) 3278c2ecf20Sopenharmony_ci return -ENODEV; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1)) 3308c2ecf20Sopenharmony_ci return -ENODEV; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if ((prod_id == 0x96) && (ver_id == 0x52)) { 3338c2ecf20Sopenharmony_ci pr_info("Detected an ov9650 sensor\n"); 3348c2ecf20Sopenharmony_ci goto sensor_found; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci return -ENODEV; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cisensor_found: 3398c2ecf20Sopenharmony_ci sd->gspca_dev.cam.cam_mode = ov9650_modes; 3408c2ecf20Sopenharmony_ci sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ciint ov9650_init(struct sd *sd) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci int i, err = 0; 3488c2ecf20Sopenharmony_ci u8 data; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (dump_sensor) 3518c2ecf20Sopenharmony_ci ov9650_dump_registers(sd); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) { 3548c2ecf20Sopenharmony_ci data = init_ov9650[i][2]; 3558c2ecf20Sopenharmony_ci if (init_ov9650[i][0] == SENSOR) 3568c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, init_ov9650[i][1], 3578c2ecf20Sopenharmony_ci &data, 1); 3588c2ecf20Sopenharmony_ci else 3598c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, init_ov9650[i][1], data); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ciint ov9650_init_controls(struct sd *sd) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci sd->gspca_dev.vdev.ctrl_handler = hdl; 3708c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 9); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 3738c2ecf20Sopenharmony_ci V4L2_CID_AUTO_WHITE_BALANCE, 3748c2ecf20Sopenharmony_ci 0, 1, 1, 1); 3758c2ecf20Sopenharmony_ci sd->red_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 3768c2ecf20Sopenharmony_ci V4L2_CID_RED_BALANCE, 0, 255, 1, 3778c2ecf20Sopenharmony_ci RED_GAIN_DEFAULT); 3788c2ecf20Sopenharmony_ci sd->blue_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 3798c2ecf20Sopenharmony_ci V4L2_CID_BLUE_BALANCE, 0, 255, 1, 3808c2ecf20Sopenharmony_ci BLUE_GAIN_DEFAULT); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &ov9650_ctrl_ops, 3838c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); 3848c2ecf20Sopenharmony_ci sd->expo = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_EXPOSURE, 3858c2ecf20Sopenharmony_ci 0, 0x1ff, 4, EXPOSURE_DEFAULT); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci sd->autogain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, 3888c2ecf20Sopenharmony_ci V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 3898c2ecf20Sopenharmony_ci sd->gain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_GAIN, 0, 3908c2ecf20Sopenharmony_ci 0x3ff, 1, GAIN_DEFAULT); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci sd->hflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_HFLIP, 3938c2ecf20Sopenharmony_ci 0, 1, 1, 0); 3948c2ecf20Sopenharmony_ci sd->vflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_VFLIP, 3958c2ecf20Sopenharmony_ci 0, 1, 1, 0); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (hdl->error) { 3988c2ecf20Sopenharmony_ci pr_err("Could not initialize controls\n"); 3998c2ecf20Sopenharmony_ci return hdl->error; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(3, &sd->auto_white_bal, 0, false); 4038c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false); 4048c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); 4058c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(2, &sd->hflip); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ciint ov9650_start(struct sd *sd) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci u8 data; 4138c2ecf20Sopenharmony_ci int i, err = 0; 4148c2ecf20Sopenharmony_ci struct cam *cam = &sd->gspca_dev.cam; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; 4178c2ecf20Sopenharmony_ci int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; 4188c2ecf20Sopenharmony_ci int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; 4198c2ecf20Sopenharmony_ci int hor_offs = OV9650_LEFT_OFFSET; 4208c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = (struct gspca_dev *)sd; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if ((!dmi_check_system(ov9650_flip_dmi_table) && 4238c2ecf20Sopenharmony_ci sd->vflip->val) || 4248c2ecf20Sopenharmony_ci (dmi_check_system(ov9650_flip_dmi_table) && 4258c2ecf20Sopenharmony_ci !sd->vflip->val)) 4268c2ecf20Sopenharmony_ci ver_offs--; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (width <= 320) 4298c2ecf20Sopenharmony_ci hor_offs /= 2; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Synthesize the vsync/hsync setup */ 4328c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) { 4338c2ecf20Sopenharmony_ci if (res_init_ov9650[i][0] == BRIDGE) 4348c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, res_init_ov9650[i][1], 4358c2ecf20Sopenharmony_ci res_init_ov9650[i][2]); 4368c2ecf20Sopenharmony_ci else if (res_init_ov9650[i][0] == SENSOR) { 4378c2ecf20Sopenharmony_ci data = res_init_ov9650[i][2]; 4388c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, 4398c2ecf20Sopenharmony_ci res_init_ov9650[i][1], &data, 1); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci if (err < 0) 4438c2ecf20Sopenharmony_ci return err; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 4468c2ecf20Sopenharmony_ci ((ver_offs >> 8) & 0xff)); 4478c2ecf20Sopenharmony_ci if (err < 0) 4488c2ecf20Sopenharmony_ci return err; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff)); 4518c2ecf20Sopenharmony_ci if (err < 0) 4528c2ecf20Sopenharmony_ci return err; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); 4558c2ecf20Sopenharmony_ci if (err < 0) 4568c2ecf20Sopenharmony_ci return err; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); 4598c2ecf20Sopenharmony_ci if (err < 0) 4608c2ecf20Sopenharmony_ci return err; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); 4638c2ecf20Sopenharmony_ci if (err < 0) 4648c2ecf20Sopenharmony_ci return err; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci for (i = 0; i < 2 && !err; i++) 4678c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); 4688c2ecf20Sopenharmony_ci if (err < 0) 4698c2ecf20Sopenharmony_ci return err; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); 4728c2ecf20Sopenharmony_ci if (err < 0) 4738c2ecf20Sopenharmony_ci return err; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2); 4768c2ecf20Sopenharmony_ci if (err < 0) 4778c2ecf20Sopenharmony_ci return err; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 4808c2ecf20Sopenharmony_ci (hor_offs >> 8) & 0xff); 4818c2ecf20Sopenharmony_ci if (err < 0) 4828c2ecf20Sopenharmony_ci return err; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff); 4858c2ecf20Sopenharmony_ci if (err < 0) 4868c2ecf20Sopenharmony_ci return err; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 4898c2ecf20Sopenharmony_ci ((width + hor_offs) >> 8) & 0xff); 4908c2ecf20Sopenharmony_ci if (err < 0) 4918c2ecf20Sopenharmony_ci return err; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 4948c2ecf20Sopenharmony_ci ((width + hor_offs) & 0xff)); 4958c2ecf20Sopenharmony_ci if (err < 0) 4968c2ecf20Sopenharmony_ci return err; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); 4998c2ecf20Sopenharmony_ci if (err < 0) 5008c2ecf20Sopenharmony_ci return err; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci switch (width) { 5038c2ecf20Sopenharmony_ci case 640: 5048c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Configuring camera for VGA mode\n"); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci data = OV9650_VGA_SELECT | OV9650_RGB_SELECT | 5078c2ecf20Sopenharmony_ci OV9650_RAW_RGB_SELECT; 5088c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci case 352: 5128c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Configuring camera for CIF mode\n"); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci data = OV9650_CIF_SELECT | OV9650_RGB_SELECT | 5158c2ecf20Sopenharmony_ci OV9650_RAW_RGB_SELECT; 5168c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 5178c2ecf20Sopenharmony_ci break; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci case 320: 5208c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QVGA mode\n"); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT | 5238c2ecf20Sopenharmony_ci OV9650_RAW_RGB_SELECT; 5248c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 5258c2ecf20Sopenharmony_ci break; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci case 176: 5288c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Configuring camera for QCIF mode\n"); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT | 5318c2ecf20Sopenharmony_ci OV9650_RAW_RGB_SELECT; 5328c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci return err; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ciint ov9650_stop(struct sd *sd) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X; 5418c2ecf20Sopenharmony_ci return m5602_write_sensor(sd, OV9650_COM2, &data, 1); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_civoid ov9650_disconnect(struct sd *sd) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci ov9650_stop(sd); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci sd->sensor = NULL; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5548c2ecf20Sopenharmony_ci u8 i2c_data; 5558c2ecf20Sopenharmony_ci int err; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", val); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* The 6 MSBs */ 5608c2ecf20Sopenharmony_ci i2c_data = (val >> 10) & 0x3f; 5618c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_AECHM, 5628c2ecf20Sopenharmony_ci &i2c_data, 1); 5638c2ecf20Sopenharmony_ci if (err < 0) 5648c2ecf20Sopenharmony_ci return err; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* The 8 middle bits */ 5678c2ecf20Sopenharmony_ci i2c_data = (val >> 2) & 0xff; 5688c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_AECH, 5698c2ecf20Sopenharmony_ci &i2c_data, 1); 5708c2ecf20Sopenharmony_ci if (err < 0) 5718c2ecf20Sopenharmony_ci return err; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* The 2 LSBs */ 5748c2ecf20Sopenharmony_ci i2c_data = val & 0x03; 5758c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1); 5768c2ecf20Sopenharmony_ci return err; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci int err; 5828c2ecf20Sopenharmony_ci u8 i2c_data; 5838c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Setting gain to %d\n", val); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* The 2 MSB */ 5888c2ecf20Sopenharmony_ci /* Read the OV9650_VREF register first to avoid 5898c2ecf20Sopenharmony_ci corrupting the VREF high and low bits */ 5908c2ecf20Sopenharmony_ci err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); 5918c2ecf20Sopenharmony_ci if (err < 0) 5928c2ecf20Sopenharmony_ci return err; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* Mask away all uninteresting bits */ 5958c2ecf20Sopenharmony_ci i2c_data = ((val & 0x0300) >> 2) | 5968c2ecf20Sopenharmony_ci (i2c_data & 0x3f); 5978c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1); 5988c2ecf20Sopenharmony_ci if (err < 0) 5998c2ecf20Sopenharmony_ci return err; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* The 8 LSBs */ 6028c2ecf20Sopenharmony_ci i2c_data = val & 0xff; 6038c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); 6048c2ecf20Sopenharmony_ci return err; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci int err; 6108c2ecf20Sopenharmony_ci u8 i2c_data; 6118c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set red gain to %d\n", val); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci i2c_data = val & 0xff; 6168c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1); 6178c2ecf20Sopenharmony_ci return err; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci int err; 6238c2ecf20Sopenharmony_ci u8 i2c_data; 6248c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set blue gain to %d\n", val); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci i2c_data = val & 0xff; 6298c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1); 6308c2ecf20Sopenharmony_ci return err; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int ov9650_set_hvflip(struct gspca_dev *gspca_dev) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci int err; 6368c2ecf20Sopenharmony_ci u8 i2c_data; 6378c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 6388c2ecf20Sopenharmony_ci int hflip = sd->hflip->val; 6398c2ecf20Sopenharmony_ci int vflip = sd->vflip->val; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set hvflip to %d %d\n", hflip, vflip); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (dmi_check_system(ov9650_flip_dmi_table)) 6448c2ecf20Sopenharmony_ci vflip = !vflip; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci i2c_data = (hflip << 5) | (vflip << 4); 6478c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); 6488c2ecf20Sopenharmony_ci if (err < 0) 6498c2ecf20Sopenharmony_ci return err; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* When vflip is toggled we need to readjust the bridge hsync/vsync */ 6528c2ecf20Sopenharmony_ci if (gspca_dev->streaming) 6538c2ecf20Sopenharmony_ci err = ov9650_start(sd); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return err; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, 6598c2ecf20Sopenharmony_ci __s32 val) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci int err; 6628c2ecf20Sopenharmony_ci u8 i2c_data; 6638c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set auto exposure control to %d\n", val); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); 6688c2ecf20Sopenharmony_ci if (err < 0) 6698c2ecf20Sopenharmony_ci return err; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci val = (val == V4L2_EXPOSURE_AUTO); 6728c2ecf20Sopenharmony_ci i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, 6788c2ecf20Sopenharmony_ci __s32 val) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci int err; 6818c2ecf20Sopenharmony_ci u8 i2c_data; 6828c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set auto white balance to %d\n", val); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); 6878c2ecf20Sopenharmony_ci if (err < 0) 6888c2ecf20Sopenharmony_ci return err; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); 6918c2ecf20Sopenharmony_ci err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci return err; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci int err; 6998c2ecf20Sopenharmony_ci u8 i2c_data; 7008c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_CONF, "Set auto gain control to %d\n", val); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); 7058c2ecf20Sopenharmony_ci if (err < 0) 7068c2ecf20Sopenharmony_ci return err; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic int ov9650_s_ctrl(struct v4l2_ctrl *ctrl) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 7168c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 7178c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 7188c2ecf20Sopenharmony_ci int err; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci switch (ctrl->id) { 7248c2ecf20Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 7258c2ecf20Sopenharmony_ci err = ov9650_set_auto_white_balance(gspca_dev, ctrl->val); 7268c2ecf20Sopenharmony_ci if (err || ctrl->val) 7278c2ecf20Sopenharmony_ci return err; 7288c2ecf20Sopenharmony_ci err = ov9650_set_red_balance(gspca_dev, sd->red_bal->val); 7298c2ecf20Sopenharmony_ci if (err) 7308c2ecf20Sopenharmony_ci return err; 7318c2ecf20Sopenharmony_ci err = ov9650_set_blue_balance(gspca_dev, sd->blue_bal->val); 7328c2ecf20Sopenharmony_ci break; 7338c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 7348c2ecf20Sopenharmony_ci err = ov9650_set_auto_exposure(gspca_dev, ctrl->val); 7358c2ecf20Sopenharmony_ci if (err || ctrl->val == V4L2_EXPOSURE_AUTO) 7368c2ecf20Sopenharmony_ci return err; 7378c2ecf20Sopenharmony_ci err = ov9650_set_exposure(gspca_dev, sd->expo->val); 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci case V4L2_CID_AUTOGAIN: 7408c2ecf20Sopenharmony_ci err = ov9650_set_auto_gain(gspca_dev, ctrl->val); 7418c2ecf20Sopenharmony_ci if (err || ctrl->val) 7428c2ecf20Sopenharmony_ci return err; 7438c2ecf20Sopenharmony_ci err = ov9650_set_gain(gspca_dev, sd->gain->val); 7448c2ecf20Sopenharmony_ci break; 7458c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 7468c2ecf20Sopenharmony_ci err = ov9650_set_hvflip(gspca_dev); 7478c2ecf20Sopenharmony_ci break; 7488c2ecf20Sopenharmony_ci default: 7498c2ecf20Sopenharmony_ci return -EINVAL; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci return err; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic void ov9650_dump_registers(struct sd *sd) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci int address; 7588c2ecf20Sopenharmony_ci pr_info("Dumping the ov9650 register state\n"); 7598c2ecf20Sopenharmony_ci for (address = 0; address < 0xa9; address++) { 7608c2ecf20Sopenharmony_ci u8 value; 7618c2ecf20Sopenharmony_ci m5602_read_sensor(sd, address, &value, 1); 7628c2ecf20Sopenharmony_ci pr_info("register 0x%x contains 0x%x\n", address, value); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci pr_info("ov9650 register state dump complete\n"); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci pr_info("Probing for which registers that are read/write\n"); 7688c2ecf20Sopenharmony_ci for (address = 0; address < 0xff; address++) { 7698c2ecf20Sopenharmony_ci u8 old_value, ctrl_value; 7708c2ecf20Sopenharmony_ci u8 test_value[2] = {0xff, 0xff}; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci m5602_read_sensor(sd, address, &old_value, 1); 7738c2ecf20Sopenharmony_ci m5602_write_sensor(sd, address, test_value, 1); 7748c2ecf20Sopenharmony_ci m5602_read_sensor(sd, address, &ctrl_value, 1); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (ctrl_value == test_value[0]) 7778c2ecf20Sopenharmony_ci pr_info("register 0x%x is writeable\n", address); 7788c2ecf20Sopenharmony_ci else 7798c2ecf20Sopenharmony_ci pr_info("register 0x%x is read only\n", address); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* Restore original value */ 7828c2ecf20Sopenharmony_ci m5602_write_sensor(sd, address, &old_value, 1); 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci} 785