18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Omnivision OV9650/OV9652 CMOS Image Sensor driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013, Sylwester Nawrocki <sylvester.nawrocki@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Register definitions and initial settings based on a driver written 88c2ecf20Sopenharmony_ci * by Vladimir Fonov. 98c2ecf20Sopenharmony_ci * Copyright (c) 2010, Vladimir Fonov 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio.h> 148c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/media.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 208c2ecf20Sopenharmony_ci#include <linux/regmap.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/string.h> 238c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <media/media-entity.h> 268c2ecf20Sopenharmony_ci#include <media/v4l2-async.h> 278c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 288c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 298c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 308c2ecf20Sopenharmony_ci#include <media/v4l2-image-sizes.h> 318c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 328c2ecf20Sopenharmony_ci#include <media/v4l2-mediabus.h> 338c2ecf20Sopenharmony_ci#include <media/i2c/ov9650.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int debug; 368c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0-2)"); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define DRIVER_NAME "OV9650" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * OV9650/OV9652 register definitions 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci#define REG_GAIN 0x00 /* Gain control, AGC[7:0] */ 458c2ecf20Sopenharmony_ci#define REG_BLUE 0x01 /* AWB - Blue channel gain */ 468c2ecf20Sopenharmony_ci#define REG_RED 0x02 /* AWB - Red channel gain */ 478c2ecf20Sopenharmony_ci#define REG_VREF 0x03 /* [7:6] - AGC[9:8], [5:3]/[2:0] */ 488c2ecf20Sopenharmony_ci#define VREF_GAIN_MASK 0xc0 /* - VREF end/start low 3 bits */ 498c2ecf20Sopenharmony_ci#define REG_COM1 0x04 508c2ecf20Sopenharmony_ci#define COM1_CCIR656 0x40 518c2ecf20Sopenharmony_ci#define REG_B_AVE 0x05 528c2ecf20Sopenharmony_ci#define REG_GB_AVE 0x06 538c2ecf20Sopenharmony_ci#define REG_GR_AVE 0x07 548c2ecf20Sopenharmony_ci#define REG_R_AVE 0x08 558c2ecf20Sopenharmony_ci#define REG_COM2 0x09 568c2ecf20Sopenharmony_ci#define REG_PID 0x0a /* Product ID MSB */ 578c2ecf20Sopenharmony_ci#define REG_VER 0x0b /* Product ID LSB */ 588c2ecf20Sopenharmony_ci#define REG_COM3 0x0c 598c2ecf20Sopenharmony_ci#define COM3_SWAP 0x40 608c2ecf20Sopenharmony_ci#define COM3_VARIOPIXEL1 0x04 618c2ecf20Sopenharmony_ci#define REG_COM4 0x0d /* Vario Pixels */ 628c2ecf20Sopenharmony_ci#define COM4_VARIOPIXEL2 0x80 638c2ecf20Sopenharmony_ci#define REG_COM5 0x0e /* System clock options */ 648c2ecf20Sopenharmony_ci#define COM5_SLAVE_MODE 0x10 658c2ecf20Sopenharmony_ci#define COM5_SYSTEMCLOCK48MHZ 0x80 668c2ecf20Sopenharmony_ci#define REG_COM6 0x0f /* HREF & ADBLC options */ 678c2ecf20Sopenharmony_ci#define REG_AECH 0x10 /* Exposure value, AEC[9:2] */ 688c2ecf20Sopenharmony_ci#define REG_CLKRC 0x11 /* Clock control */ 698c2ecf20Sopenharmony_ci#define CLK_EXT 0x40 /* Use external clock directly */ 708c2ecf20Sopenharmony_ci#define CLK_SCALE 0x3f /* Mask for internal clock scale */ 718c2ecf20Sopenharmony_ci#define REG_COM7 0x12 /* SCCB reset, output format */ 728c2ecf20Sopenharmony_ci#define COM7_RESET 0x80 738c2ecf20Sopenharmony_ci#define COM7_FMT_MASK 0x38 748c2ecf20Sopenharmony_ci#define COM7_FMT_VGA 0x40 758c2ecf20Sopenharmony_ci#define COM7_FMT_CIF 0x20 768c2ecf20Sopenharmony_ci#define COM7_FMT_QVGA 0x10 778c2ecf20Sopenharmony_ci#define COM7_FMT_QCIF 0x08 788c2ecf20Sopenharmony_ci#define COM7_RGB 0x04 798c2ecf20Sopenharmony_ci#define COM7_YUV 0x00 808c2ecf20Sopenharmony_ci#define COM7_BAYER 0x01 818c2ecf20Sopenharmony_ci#define COM7_PBAYER 0x05 828c2ecf20Sopenharmony_ci#define REG_COM8 0x13 /* AGC/AEC options */ 838c2ecf20Sopenharmony_ci#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ 848c2ecf20Sopenharmony_ci#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ 858c2ecf20Sopenharmony_ci#define COM8_BFILT 0x20 /* Band filter enable */ 868c2ecf20Sopenharmony_ci#define COM8_AGC 0x04 /* Auto gain enable */ 878c2ecf20Sopenharmony_ci#define COM8_AWB 0x02 /* White balance enable */ 888c2ecf20Sopenharmony_ci#define COM8_AEC 0x01 /* Auto exposure enable */ 898c2ecf20Sopenharmony_ci#define REG_COM9 0x14 /* Gain ceiling */ 908c2ecf20Sopenharmony_ci#define COM9_GAIN_CEIL_MASK 0x70 /* */ 918c2ecf20Sopenharmony_ci#define REG_COM10 0x15 /* PCLK, HREF, HSYNC signals polarity */ 928c2ecf20Sopenharmony_ci#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ 938c2ecf20Sopenharmony_ci#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ 948c2ecf20Sopenharmony_ci#define COM10_HREF_REV 0x08 /* Reverse HREF */ 958c2ecf20Sopenharmony_ci#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ 968c2ecf20Sopenharmony_ci#define COM10_VS_NEG 0x02 /* VSYNC negative */ 978c2ecf20Sopenharmony_ci#define COM10_HS_NEG 0x01 /* HSYNC negative */ 988c2ecf20Sopenharmony_ci#define REG_HSTART 0x17 /* Horiz start high bits */ 998c2ecf20Sopenharmony_ci#define REG_HSTOP 0x18 /* Horiz stop high bits */ 1008c2ecf20Sopenharmony_ci#define REG_VSTART 0x19 /* Vert start high bits */ 1018c2ecf20Sopenharmony_ci#define REG_VSTOP 0x1a /* Vert stop high bits */ 1028c2ecf20Sopenharmony_ci#define REG_PSHFT 0x1b /* Pixel delay after HREF */ 1038c2ecf20Sopenharmony_ci#define REG_MIDH 0x1c /* Manufacturer ID MSB */ 1048c2ecf20Sopenharmony_ci#define REG_MIDL 0x1d /* Manufufacturer ID LSB */ 1058c2ecf20Sopenharmony_ci#define REG_MVFP 0x1e /* Image mirror/flip */ 1068c2ecf20Sopenharmony_ci#define MVFP_MIRROR 0x20 /* Mirror image */ 1078c2ecf20Sopenharmony_ci#define MVFP_FLIP 0x10 /* Vertical flip */ 1088c2ecf20Sopenharmony_ci#define REG_BOS 0x20 /* B channel Offset */ 1098c2ecf20Sopenharmony_ci#define REG_GBOS 0x21 /* Gb channel Offset */ 1108c2ecf20Sopenharmony_ci#define REG_GROS 0x22 /* Gr channel Offset */ 1118c2ecf20Sopenharmony_ci#define REG_ROS 0x23 /* R channel Offset */ 1128c2ecf20Sopenharmony_ci#define REG_AEW 0x24 /* AGC upper limit */ 1138c2ecf20Sopenharmony_ci#define REG_AEB 0x25 /* AGC lower limit */ 1148c2ecf20Sopenharmony_ci#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ 1158c2ecf20Sopenharmony_ci#define REG_BBIAS 0x27 /* B channel output bias */ 1168c2ecf20Sopenharmony_ci#define REG_GBBIAS 0x28 /* Gb channel output bias */ 1178c2ecf20Sopenharmony_ci#define REG_GRCOM 0x29 /* Analog BLC & regulator */ 1188c2ecf20Sopenharmony_ci#define REG_EXHCH 0x2a /* Dummy pixel insert MSB */ 1198c2ecf20Sopenharmony_ci#define REG_EXHCL 0x2b /* Dummy pixel insert LSB */ 1208c2ecf20Sopenharmony_ci#define REG_RBIAS 0x2c /* R channel output bias */ 1218c2ecf20Sopenharmony_ci#define REG_ADVFL 0x2d /* LSB of dummy line insert */ 1228c2ecf20Sopenharmony_ci#define REG_ADVFH 0x2e /* MSB of dummy line insert */ 1238c2ecf20Sopenharmony_ci#define REG_YAVE 0x2f /* Y/G channel average value */ 1248c2ecf20Sopenharmony_ci#define REG_HSYST 0x30 /* HSYNC rising edge delay LSB*/ 1258c2ecf20Sopenharmony_ci#define REG_HSYEN 0x31 /* HSYNC falling edge delay LSB*/ 1268c2ecf20Sopenharmony_ci#define REG_HREF 0x32 /* HREF pieces */ 1278c2ecf20Sopenharmony_ci#define REG_CHLF 0x33 /* reserved */ 1288c2ecf20Sopenharmony_ci#define REG_ADC 0x37 /* reserved */ 1298c2ecf20Sopenharmony_ci#define REG_ACOM 0x38 /* reserved */ 1308c2ecf20Sopenharmony_ci#define REG_OFON 0x39 /* Power down register */ 1318c2ecf20Sopenharmony_ci#define OFON_PWRDN 0x08 /* Power down bit */ 1328c2ecf20Sopenharmony_ci#define REG_TSLB 0x3a /* YUVU format */ 1338c2ecf20Sopenharmony_ci#define TSLB_YUYV_MASK 0x0c /* UYVY or VYUY - see com13 */ 1348c2ecf20Sopenharmony_ci#define REG_COM11 0x3b /* Night mode, banding filter enable */ 1358c2ecf20Sopenharmony_ci#define COM11_NIGHT 0x80 /* Night mode enable */ 1368c2ecf20Sopenharmony_ci#define COM11_NMFR 0x60 /* Two bit NM frame rate */ 1378c2ecf20Sopenharmony_ci#define COM11_BANDING 0x01 /* Banding filter */ 1388c2ecf20Sopenharmony_ci#define COM11_AEC_REF_MASK 0x18 /* AEC reference area selection */ 1398c2ecf20Sopenharmony_ci#define REG_COM12 0x3c /* HREF option, UV average */ 1408c2ecf20Sopenharmony_ci#define COM12_HREF 0x80 /* HREF always */ 1418c2ecf20Sopenharmony_ci#define REG_COM13 0x3d /* Gamma selection, Color matrix en. */ 1428c2ecf20Sopenharmony_ci#define COM13_GAMMA 0x80 /* Gamma enable */ 1438c2ecf20Sopenharmony_ci#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ 1448c2ecf20Sopenharmony_ci#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ 1458c2ecf20Sopenharmony_ci#define REG_COM14 0x3e /* Edge enhancement options */ 1468c2ecf20Sopenharmony_ci#define COM14_EDGE_EN 0x02 1478c2ecf20Sopenharmony_ci#define COM14_EEF_X2 0x01 1488c2ecf20Sopenharmony_ci#define REG_EDGE 0x3f /* Edge enhancement factor */ 1498c2ecf20Sopenharmony_ci#define EDGE_FACTOR_MASK 0x0f 1508c2ecf20Sopenharmony_ci#define REG_COM15 0x40 /* Output range, RGB 555/565 */ 1518c2ecf20Sopenharmony_ci#define COM15_R10F0 0x00 /* Data range 10 to F0 */ 1528c2ecf20Sopenharmony_ci#define COM15_R01FE 0x80 /* 01 to FE */ 1538c2ecf20Sopenharmony_ci#define COM15_R00FF 0xc0 /* 00 to FF */ 1548c2ecf20Sopenharmony_ci#define COM15_RGB565 0x10 /* RGB565 output */ 1558c2ecf20Sopenharmony_ci#define COM15_RGB555 0x30 /* RGB555 output */ 1568c2ecf20Sopenharmony_ci#define COM15_SWAPRB 0x04 /* Swap R&B */ 1578c2ecf20Sopenharmony_ci#define REG_COM16 0x41 /* Color matrix coeff options */ 1588c2ecf20Sopenharmony_ci#define REG_COM17 0x42 /* Single frame out, banding filter */ 1598c2ecf20Sopenharmony_ci/* n = 1...9, 0x4f..0x57 */ 1608c2ecf20Sopenharmony_ci#define REG_MTX(__n) (0x4f + (__n) - 1) 1618c2ecf20Sopenharmony_ci#define REG_MTXS 0x58 1628c2ecf20Sopenharmony_ci/* Lens Correction Option 1...5, __n = 0...5 */ 1638c2ecf20Sopenharmony_ci#define REG_LCC(__n) (0x62 + (__n) - 1) 1648c2ecf20Sopenharmony_ci#define LCC5_LCC_ENABLE 0x01 /* LCC5, enable lens correction */ 1658c2ecf20Sopenharmony_ci#define LCC5_LCC_COLOR 0x04 1668c2ecf20Sopenharmony_ci#define REG_MANU 0x67 /* Manual U value */ 1678c2ecf20Sopenharmony_ci#define REG_MANV 0x68 /* Manual V value */ 1688c2ecf20Sopenharmony_ci#define REG_HV 0x69 /* Manual banding filter MSB */ 1698c2ecf20Sopenharmony_ci#define REG_MBD 0x6a /* Manual banding filter value */ 1708c2ecf20Sopenharmony_ci#define REG_DBLV 0x6b /* reserved */ 1718c2ecf20Sopenharmony_ci#define REG_GSP 0x6c /* Gamma curve */ 1728c2ecf20Sopenharmony_ci#define GSP_LEN 15 1738c2ecf20Sopenharmony_ci#define REG_GST 0x7c /* Gamma curve */ 1748c2ecf20Sopenharmony_ci#define GST_LEN 15 1758c2ecf20Sopenharmony_ci#define REG_COM21 0x8b 1768c2ecf20Sopenharmony_ci#define REG_COM22 0x8c /* Edge enhancement, denoising */ 1778c2ecf20Sopenharmony_ci#define COM22_WHTPCOR 0x02 /* White pixel correction enable */ 1788c2ecf20Sopenharmony_ci#define COM22_WHTPCOROPT 0x01 /* White pixel correction option */ 1798c2ecf20Sopenharmony_ci#define COM22_DENOISE 0x10 /* White pixel correction option */ 1808c2ecf20Sopenharmony_ci#define REG_COM23 0x8d /* Color bar test, color gain */ 1818c2ecf20Sopenharmony_ci#define COM23_TEST_MODE 0x10 1828c2ecf20Sopenharmony_ci#define REG_DBLC1 0x8f /* Digital BLC */ 1838c2ecf20Sopenharmony_ci#define REG_DBLC_B 0x90 /* Digital BLC B channel offset */ 1848c2ecf20Sopenharmony_ci#define REG_DBLC_R 0x91 /* Digital BLC R channel offset */ 1858c2ecf20Sopenharmony_ci#define REG_DM_LNL 0x92 /* Dummy line low 8 bits */ 1868c2ecf20Sopenharmony_ci#define REG_DM_LNH 0x93 /* Dummy line high 8 bits */ 1878c2ecf20Sopenharmony_ci#define REG_LCCFB 0x9d /* Lens Correction B channel */ 1888c2ecf20Sopenharmony_ci#define REG_LCCFR 0x9e /* Lens Correction R channel */ 1898c2ecf20Sopenharmony_ci#define REG_DBLC_GB 0x9f /* Digital BLC GB chan offset */ 1908c2ecf20Sopenharmony_ci#define REG_DBLC_GR 0xa0 /* Digital BLC GR chan offset */ 1918c2ecf20Sopenharmony_ci#define REG_AECHM 0xa1 /* Exposure value - bits AEC[15:10] */ 1928c2ecf20Sopenharmony_ci#define REG_BD50ST 0xa2 /* Banding filter value for 50Hz */ 1938c2ecf20Sopenharmony_ci#define REG_BD60ST 0xa3 /* Banding filter value for 60Hz */ 1948c2ecf20Sopenharmony_ci#define REG_NULL 0xff /* Array end token */ 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci#define DEF_CLKRC 0x80 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci#define OV965X_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) 1998c2ecf20Sopenharmony_ci#define OV9650_ID 0x9650 2008c2ecf20Sopenharmony_ci#define OV9652_ID 0x9652 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistruct ov965x_ctrls { 2038c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler handler; 2048c2ecf20Sopenharmony_ci struct { 2058c2ecf20Sopenharmony_ci struct v4l2_ctrl *auto_exp; 2068c2ecf20Sopenharmony_ci struct v4l2_ctrl *exposure; 2078c2ecf20Sopenharmony_ci }; 2088c2ecf20Sopenharmony_ci struct { 2098c2ecf20Sopenharmony_ci struct v4l2_ctrl *auto_wb; 2108c2ecf20Sopenharmony_ci struct v4l2_ctrl *blue_balance; 2118c2ecf20Sopenharmony_ci struct v4l2_ctrl *red_balance; 2128c2ecf20Sopenharmony_ci }; 2138c2ecf20Sopenharmony_ci struct { 2148c2ecf20Sopenharmony_ci struct v4l2_ctrl *hflip; 2158c2ecf20Sopenharmony_ci struct v4l2_ctrl *vflip; 2168c2ecf20Sopenharmony_ci }; 2178c2ecf20Sopenharmony_ci struct { 2188c2ecf20Sopenharmony_ci struct v4l2_ctrl *auto_gain; 2198c2ecf20Sopenharmony_ci struct v4l2_ctrl *gain; 2208c2ecf20Sopenharmony_ci }; 2218c2ecf20Sopenharmony_ci struct v4l2_ctrl *brightness; 2228c2ecf20Sopenharmony_ci struct v4l2_ctrl *saturation; 2238c2ecf20Sopenharmony_ci struct v4l2_ctrl *sharpness; 2248c2ecf20Sopenharmony_ci struct v4l2_ctrl *light_freq; 2258c2ecf20Sopenharmony_ci u8 update; 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistruct ov965x_framesize { 2298c2ecf20Sopenharmony_ci u16 width; 2308c2ecf20Sopenharmony_ci u16 height; 2318c2ecf20Sopenharmony_ci u16 max_exp_lines; 2328c2ecf20Sopenharmony_ci const u8 *regs; 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistruct ov965x_interval { 2368c2ecf20Sopenharmony_ci struct v4l2_fract interval; 2378c2ecf20Sopenharmony_ci /* Maximum resolution for this interval */ 2388c2ecf20Sopenharmony_ci struct v4l2_frmsize_discrete size; 2398c2ecf20Sopenharmony_ci u8 clkrc_div; 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cienum gpio_id { 2438c2ecf20Sopenharmony_ci GPIO_PWDN, 2448c2ecf20Sopenharmony_ci GPIO_RST, 2458c2ecf20Sopenharmony_ci NUM_GPIOS, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistruct ov965x { 2498c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 2508c2ecf20Sopenharmony_ci struct media_pad pad; 2518c2ecf20Sopenharmony_ci enum v4l2_mbus_type bus_type; 2528c2ecf20Sopenharmony_ci struct gpio_desc *gpios[NUM_GPIOS]; 2538c2ecf20Sopenharmony_ci /* External master clock frequency */ 2548c2ecf20Sopenharmony_ci unsigned long mclk_frequency; 2558c2ecf20Sopenharmony_ci struct clk *clk; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Protects the struct fields below */ 2588c2ecf20Sopenharmony_ci struct mutex lock; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci struct regmap *regmap; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Exposure row interval in us */ 2638c2ecf20Sopenharmony_ci unsigned int exp_row_interval; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci unsigned short id; 2668c2ecf20Sopenharmony_ci const struct ov965x_framesize *frame_size; 2678c2ecf20Sopenharmony_ci /* YUYV sequence (pixel format) control register */ 2688c2ecf20Sopenharmony_ci u8 tslb_reg; 2698c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt format; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci struct ov965x_ctrls ctrls; 2728c2ecf20Sopenharmony_ci /* Pointer to frame rate control data structure */ 2738c2ecf20Sopenharmony_ci const struct ov965x_interval *fiv; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci int streaming; 2768c2ecf20Sopenharmony_ci int power; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci u8 apply_frame_fmt; 2798c2ecf20Sopenharmony_ci}; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistruct i2c_rv { 2828c2ecf20Sopenharmony_ci u8 addr; 2838c2ecf20Sopenharmony_ci u8 value; 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic const struct i2c_rv ov965x_init_regs[] = { 2878c2ecf20Sopenharmony_ci { REG_COM2, 0x10 }, /* Set soft sleep mode */ 2888c2ecf20Sopenharmony_ci { REG_COM5, 0x00 }, /* System clock options */ 2898c2ecf20Sopenharmony_ci { REG_COM2, 0x01 }, /* Output drive, soft sleep mode */ 2908c2ecf20Sopenharmony_ci { REG_COM10, 0x00 }, /* Slave mode, HREF vs HSYNC, signals negate */ 2918c2ecf20Sopenharmony_ci { REG_EDGE, 0xa6 }, /* Edge enhancement treshhold and factor */ 2928c2ecf20Sopenharmony_ci { REG_COM16, 0x02 }, /* Color matrix coeff double option */ 2938c2ecf20Sopenharmony_ci { REG_COM17, 0x08 }, /* Single frame out, banding filter */ 2948c2ecf20Sopenharmony_ci { 0x16, 0x06 }, 2958c2ecf20Sopenharmony_ci { REG_CHLF, 0xc0 }, /* Reserved */ 2968c2ecf20Sopenharmony_ci { 0x34, 0xbf }, 2978c2ecf20Sopenharmony_ci { 0xa8, 0x80 }, 2988c2ecf20Sopenharmony_ci { 0x96, 0x04 }, 2998c2ecf20Sopenharmony_ci { 0x8e, 0x00 }, 3008c2ecf20Sopenharmony_ci { REG_COM12, 0x77 }, /* HREF option, UV average */ 3018c2ecf20Sopenharmony_ci { 0x8b, 0x06 }, 3028c2ecf20Sopenharmony_ci { 0x35, 0x91 }, 3038c2ecf20Sopenharmony_ci { 0x94, 0x88 }, 3048c2ecf20Sopenharmony_ci { 0x95, 0x88 }, 3058c2ecf20Sopenharmony_ci { REG_COM15, 0xc1 }, /* Output range, RGB 555/565 */ 3068c2ecf20Sopenharmony_ci { REG_GRCOM, 0x2f }, /* Analog BLC & regulator */ 3078c2ecf20Sopenharmony_ci { REG_COM6, 0x43 }, /* HREF & ADBLC options */ 3088c2ecf20Sopenharmony_ci { REG_COM8, 0xe5 }, /* AGC/AEC options */ 3098c2ecf20Sopenharmony_ci { REG_COM13, 0x90 }, /* Gamma selection, colour matrix, UV delay */ 3108c2ecf20Sopenharmony_ci { REG_HV, 0x80 }, /* Manual banding filter MSB */ 3118c2ecf20Sopenharmony_ci { 0x5c, 0x96 }, /* Reserved up to 0xa5 */ 3128c2ecf20Sopenharmony_ci { 0x5d, 0x96 }, 3138c2ecf20Sopenharmony_ci { 0x5e, 0x10 }, 3148c2ecf20Sopenharmony_ci { 0x59, 0xeb }, 3158c2ecf20Sopenharmony_ci { 0x5a, 0x9c }, 3168c2ecf20Sopenharmony_ci { 0x5b, 0x55 }, 3178c2ecf20Sopenharmony_ci { 0x43, 0xf0 }, 3188c2ecf20Sopenharmony_ci { 0x44, 0x10 }, 3198c2ecf20Sopenharmony_ci { 0x45, 0x55 }, 3208c2ecf20Sopenharmony_ci { 0x46, 0x86 }, 3218c2ecf20Sopenharmony_ci { 0x47, 0x64 }, 3228c2ecf20Sopenharmony_ci { 0x48, 0x86 }, 3238c2ecf20Sopenharmony_ci { 0x5f, 0xe0 }, 3248c2ecf20Sopenharmony_ci { 0x60, 0x8c }, 3258c2ecf20Sopenharmony_ci { 0x61, 0x20 }, 3268c2ecf20Sopenharmony_ci { 0xa5, 0xd9 }, 3278c2ecf20Sopenharmony_ci { 0xa4, 0x74 }, /* reserved */ 3288c2ecf20Sopenharmony_ci { REG_COM23, 0x02 }, /* Color gain analog/_digital_ */ 3298c2ecf20Sopenharmony_ci { REG_COM8, 0xe7 }, /* Enable AEC, AWB, AEC */ 3308c2ecf20Sopenharmony_ci { REG_COM22, 0x23 }, /* Edge enhancement, denoising */ 3318c2ecf20Sopenharmony_ci { 0xa9, 0xb8 }, 3328c2ecf20Sopenharmony_ci { 0xaa, 0x92 }, 3338c2ecf20Sopenharmony_ci { 0xab, 0x0a }, 3348c2ecf20Sopenharmony_ci { REG_DBLC1, 0xdf }, /* Digital BLC */ 3358c2ecf20Sopenharmony_ci { REG_DBLC_B, 0x00 }, /* Digital BLC B chan offset */ 3368c2ecf20Sopenharmony_ci { REG_DBLC_R, 0x00 }, /* Digital BLC R chan offset */ 3378c2ecf20Sopenharmony_ci { REG_DBLC_GB, 0x00 }, /* Digital BLC GB chan offset */ 3388c2ecf20Sopenharmony_ci { REG_DBLC_GR, 0x00 }, 3398c2ecf20Sopenharmony_ci { REG_COM9, 0x3a }, /* Gain ceiling 16x */ 3408c2ecf20Sopenharmony_ci { REG_NULL, 0 } 3418c2ecf20Sopenharmony_ci}; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci#define NUM_FMT_REGS 14 3448c2ecf20Sopenharmony_ci/* 3458c2ecf20Sopenharmony_ci * COM7, COM3, COM4, HSTART, HSTOP, HREF, VSTART, VSTOP, VREF, 3468c2ecf20Sopenharmony_ci * EXHCH, EXHCL, ADC, OCOM, OFON 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_cistatic const u8 frame_size_reg_addr[NUM_FMT_REGS] = { 3498c2ecf20Sopenharmony_ci 0x12, 0x0c, 0x0d, 0x17, 0x18, 0x32, 0x19, 0x1a, 0x03, 3508c2ecf20Sopenharmony_ci 0x2a, 0x2b, 0x37, 0x38, 0x39, 3518c2ecf20Sopenharmony_ci}; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic const u8 ov965x_sxga_regs[NUM_FMT_REGS] = { 3548c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x1e, 0xbe, 0xbf, 0x01, 0x81, 0x12, 3558c2ecf20Sopenharmony_ci 0x10, 0x34, 0x81, 0x93, 0x51, 3568c2ecf20Sopenharmony_ci}; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic const u8 ov965x_vga_regs[NUM_FMT_REGS] = { 3598c2ecf20Sopenharmony_ci 0x40, 0x04, 0x80, 0x26, 0xc6, 0xed, 0x01, 0x3d, 0x00, 3608c2ecf20Sopenharmony_ci 0x10, 0x40, 0x91, 0x12, 0x43, 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/* Determined empirically. */ 3648c2ecf20Sopenharmony_cistatic const u8 ov965x_qvga_regs[NUM_FMT_REGS] = { 3658c2ecf20Sopenharmony_ci 0x10, 0x04, 0x80, 0x25, 0xc5, 0xbf, 0x00, 0x80, 0x12, 3668c2ecf20Sopenharmony_ci 0x10, 0x40, 0x91, 0x12, 0x43, 3678c2ecf20Sopenharmony_ci}; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic const struct ov965x_framesize ov965x_framesizes[] = { 3708c2ecf20Sopenharmony_ci { 3718c2ecf20Sopenharmony_ci .width = SXGA_WIDTH, 3728c2ecf20Sopenharmony_ci .height = SXGA_HEIGHT, 3738c2ecf20Sopenharmony_ci .regs = ov965x_sxga_regs, 3748c2ecf20Sopenharmony_ci .max_exp_lines = 1048, 3758c2ecf20Sopenharmony_ci }, { 3768c2ecf20Sopenharmony_ci .width = VGA_WIDTH, 3778c2ecf20Sopenharmony_ci .height = VGA_HEIGHT, 3788c2ecf20Sopenharmony_ci .regs = ov965x_vga_regs, 3798c2ecf20Sopenharmony_ci .max_exp_lines = 498, 3808c2ecf20Sopenharmony_ci }, { 3818c2ecf20Sopenharmony_ci .width = QVGA_WIDTH, 3828c2ecf20Sopenharmony_ci .height = QVGA_HEIGHT, 3838c2ecf20Sopenharmony_ci .regs = ov965x_qvga_regs, 3848c2ecf20Sopenharmony_ci .max_exp_lines = 248, 3858c2ecf20Sopenharmony_ci }, 3868c2ecf20Sopenharmony_ci}; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistruct ov965x_pixfmt { 3898c2ecf20Sopenharmony_ci u32 code; 3908c2ecf20Sopenharmony_ci u32 colorspace; 3918c2ecf20Sopenharmony_ci /* REG_TSLB value, only bits [3:2] may be set. */ 3928c2ecf20Sopenharmony_ci u8 tslb_reg; 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic const struct ov965x_pixfmt ov965x_formats[] = { 3968c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 0x00}, 3978c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG, 0x04}, 3988c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG, 0x0c}, 3998c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 0x08}, 4008c2ecf20Sopenharmony_ci}; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/* 4038c2ecf20Sopenharmony_ci * This table specifies possible frame resolution and interval 4048c2ecf20Sopenharmony_ci * combinations. Default CLKRC[5:0] divider values are valid 4058c2ecf20Sopenharmony_ci * only for 24 MHz external clock frequency. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_cistatic struct ov965x_interval ov965x_intervals[] = { 4088c2ecf20Sopenharmony_ci {{ 100, 625 }, { SXGA_WIDTH, SXGA_HEIGHT }, 0 }, /* 6.25 fps */ 4098c2ecf20Sopenharmony_ci {{ 10, 125 }, { VGA_WIDTH, VGA_HEIGHT }, 1 }, /* 12.5 fps */ 4108c2ecf20Sopenharmony_ci {{ 10, 125 }, { QVGA_WIDTH, QVGA_HEIGHT }, 3 }, /* 12.5 fps */ 4118c2ecf20Sopenharmony_ci {{ 1, 25 }, { VGA_WIDTH, VGA_HEIGHT }, 0 }, /* 25 fps */ 4128c2ecf20Sopenharmony_ci {{ 1, 25 }, { QVGA_WIDTH, QVGA_HEIGHT }, 1 }, /* 25 fps */ 4138c2ecf20Sopenharmony_ci}; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci return &container_of(ctrl->handler, struct ov965x, ctrls.handler)->sd; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic inline struct ov965x *to_ov965x(struct v4l2_subdev *sd) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci return container_of(sd, struct ov965x, sd); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int ov965x_read(struct ov965x *ov965x, u8 addr, u8 *val) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci int ret; 4288c2ecf20Sopenharmony_ci unsigned int buf; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci ret = regmap_read(ov965x->regmap, addr, &buf); 4318c2ecf20Sopenharmony_ci if (!ret) 4328c2ecf20Sopenharmony_ci *val = buf; 4338c2ecf20Sopenharmony_ci else 4348c2ecf20Sopenharmony_ci *val = -1; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, &ov965x->sd, "%s: 0x%02x @ 0x%02x. (%d)\n", 4378c2ecf20Sopenharmony_ci __func__, *val, addr, ret); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int ov965x_write(struct ov965x *ov965x, u8 addr, u8 val) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci int ret; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci ret = regmap_write(ov965x->regmap, addr, val); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci v4l2_dbg(2, debug, &ov965x->sd, "%s: 0x%02x @ 0x%02X (%d)\n", 4498c2ecf20Sopenharmony_ci __func__, val, addr, ret); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return ret; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic int ov965x_write_array(struct ov965x *ov965x, 4558c2ecf20Sopenharmony_ci const struct i2c_rv *regs) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci int i, ret = 0; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) 4608c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, regs[i].addr, regs[i].value); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci return ret; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int ov965x_set_default_gamma_curve(struct ov965x *ov965x) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci static const u8 gamma_curve[] = { 4688c2ecf20Sopenharmony_ci /* Values taken from OV application note. */ 4698c2ecf20Sopenharmony_ci 0x40, 0x30, 0x4b, 0x60, 0x70, 0x70, 0x70, 0x70, 4708c2ecf20Sopenharmony_ci 0x60, 0x60, 0x50, 0x48, 0x3a, 0x2e, 0x28, 0x22, 4718c2ecf20Sopenharmony_ci 0x04, 0x07, 0x10, 0x28, 0x36, 0x44, 0x52, 0x60, 4728c2ecf20Sopenharmony_ci 0x6c, 0x78, 0x8c, 0x9e, 0xbb, 0xd2, 0xe6 4738c2ecf20Sopenharmony_ci }; 4748c2ecf20Sopenharmony_ci u8 addr = REG_GSP; 4758c2ecf20Sopenharmony_ci unsigned int i; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gamma_curve); i++) { 4788c2ecf20Sopenharmony_ci int ret = ov965x_write(ov965x, addr, gamma_curve[i]); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (ret < 0) 4818c2ecf20Sopenharmony_ci return ret; 4828c2ecf20Sopenharmony_ci addr++; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci}; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic int ov965x_set_color_matrix(struct ov965x *ov965x) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci static const u8 mtx[] = { 4918c2ecf20Sopenharmony_ci /* MTX1..MTX9, MTXS */ 4928c2ecf20Sopenharmony_ci 0x3a, 0x3d, 0x03, 0x12, 0x26, 0x38, 0x40, 0x40, 0x40, 0x0d 4938c2ecf20Sopenharmony_ci }; 4948c2ecf20Sopenharmony_ci u8 addr = REG_MTX(1); 4958c2ecf20Sopenharmony_ci unsigned int i; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mtx); i++) { 4988c2ecf20Sopenharmony_ci int ret = ov965x_write(ov965x, addr, mtx[i]); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (ret < 0) 5018c2ecf20Sopenharmony_ci return ret; 5028c2ecf20Sopenharmony_ci addr++; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic int __ov965x_set_power(struct ov965x *ov965x, int on) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci if (on) { 5118c2ecf20Sopenharmony_ci int ret = clk_prepare_enable(ov965x->clk); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (ret) 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 0); 5178c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 0); 5188c2ecf20Sopenharmony_ci msleep(25); 5198c2ecf20Sopenharmony_ci } else { 5208c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 1); 5218c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 1); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci clk_disable_unprepare(ov965x->clk); 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci ov965x->streaming = 0; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int ov965x_s_power(struct v4l2_subdev *sd, int on) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct ov965x *ov965x = to_ov965x(sd); 5348c2ecf20Sopenharmony_ci int ret = 0; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 5398c2ecf20Sopenharmony_ci if (ov965x->power == !on) { 5408c2ecf20Sopenharmony_ci ret = __ov965x_set_power(ov965x, on); 5418c2ecf20Sopenharmony_ci if (!ret && on) { 5428c2ecf20Sopenharmony_ci ret = ov965x_write_array(ov965x, 5438c2ecf20Sopenharmony_ci ov965x_init_regs); 5448c2ecf20Sopenharmony_ci ov965x->apply_frame_fmt = 1; 5458c2ecf20Sopenharmony_ci ov965x->ctrls.update = 1; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci if (!ret) 5498c2ecf20Sopenharmony_ci ov965x->power += on ? 1 : -1; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci WARN_ON(ov965x->power < 0); 5528c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 5538c2ecf20Sopenharmony_ci return ret; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci/* 5578c2ecf20Sopenharmony_ci * V4L2 controls 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic void ov965x_update_exposure_ctrl(struct ov965x *ov965x) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct v4l2_ctrl *ctrl = ov965x->ctrls.exposure; 5638c2ecf20Sopenharmony_ci unsigned long fint, trow; 5648c2ecf20Sopenharmony_ci int min, max, def; 5658c2ecf20Sopenharmony_ci u8 clkrc; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 5688c2ecf20Sopenharmony_ci if (WARN_ON(!ctrl || !ov965x->frame_size)) { 5698c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 5708c2ecf20Sopenharmony_ci return; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci clkrc = DEF_CLKRC + ov965x->fiv->clkrc_div; 5738c2ecf20Sopenharmony_ci /* Calculate internal clock frequency */ 5748c2ecf20Sopenharmony_ci fint = ov965x->mclk_frequency * ((clkrc >> 7) + 1) / 5758c2ecf20Sopenharmony_ci ((2 * ((clkrc & 0x3f) + 1))); 5768c2ecf20Sopenharmony_ci /* and the row interval (in us). */ 5778c2ecf20Sopenharmony_ci trow = (2 * 1520 * 1000000UL) / fint; 5788c2ecf20Sopenharmony_ci max = ov965x->frame_size->max_exp_lines * trow; 5798c2ecf20Sopenharmony_ci ov965x->exp_row_interval = trow; 5808c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, &ov965x->sd, "clkrc: %#x, fi: %lu, tr: %lu, %d\n", 5838c2ecf20Sopenharmony_ci clkrc, fint, trow, max); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* Update exposure time range to match current frame format. */ 5868c2ecf20Sopenharmony_ci min = (trow + 100) / 100; 5878c2ecf20Sopenharmony_ci max = (max - 100) / 100; 5888c2ecf20Sopenharmony_ci def = min + (max - min) / 2; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (v4l2_ctrl_modify_range(ctrl, min, max, 1, def)) 5918c2ecf20Sopenharmony_ci v4l2_err(&ov965x->sd, "Exposure ctrl range update failed\n"); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic int ov965x_set_banding_filter(struct ov965x *ov965x, int value) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci unsigned long mbd, light_freq; 5978c2ecf20Sopenharmony_ci int ret; 5988c2ecf20Sopenharmony_ci u8 reg; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_COM8, ®); 6018c2ecf20Sopenharmony_ci if (!ret) { 6028c2ecf20Sopenharmony_ci if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED) 6038c2ecf20Sopenharmony_ci reg &= ~COM8_BFILT; 6048c2ecf20Sopenharmony_ci else 6058c2ecf20Sopenharmony_ci reg |= COM8_BFILT; 6068c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_COM8, reg); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED) 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci if (WARN_ON(!ov965x->fiv)) 6118c2ecf20Sopenharmony_ci return -EINVAL; 6128c2ecf20Sopenharmony_ci /* Set minimal exposure time for 50/60 HZ lighting */ 6138c2ecf20Sopenharmony_ci if (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) 6148c2ecf20Sopenharmony_ci light_freq = 50; 6158c2ecf20Sopenharmony_ci else 6168c2ecf20Sopenharmony_ci light_freq = 60; 6178c2ecf20Sopenharmony_ci mbd = (1000UL * ov965x->fiv->interval.denominator * 6188c2ecf20Sopenharmony_ci ov965x->frame_size->max_exp_lines) / 6198c2ecf20Sopenharmony_ci ov965x->fiv->interval.numerator; 6208c2ecf20Sopenharmony_ci mbd = ((mbd / (light_freq * 2)) + 500) / 1000UL; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return ov965x_write(ov965x, REG_MBD, mbd); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int ov965x_set_white_balance(struct ov965x *ov965x, int awb) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci int ret; 6288c2ecf20Sopenharmony_ci u8 reg; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_COM8, ®); 6318c2ecf20Sopenharmony_ci if (!ret) { 6328c2ecf20Sopenharmony_ci reg = awb ? reg | REG_COM8 : reg & ~REG_COM8; 6338c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_COM8, reg); 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci if (!ret && !awb) { 6368c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_BLUE, 6378c2ecf20Sopenharmony_ci ov965x->ctrls.blue_balance->val); 6388c2ecf20Sopenharmony_ci if (ret < 0) 6398c2ecf20Sopenharmony_ci return ret; 6408c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_RED, 6418c2ecf20Sopenharmony_ci ov965x->ctrls.red_balance->val); 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci return ret; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci#define NUM_BR_LEVELS 7 6478c2ecf20Sopenharmony_ci#define NUM_BR_REGS 3 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic int ov965x_set_brightness(struct ov965x *ov965x, int val) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci static const u8 regs[NUM_BR_LEVELS + 1][NUM_BR_REGS] = { 6528c2ecf20Sopenharmony_ci { REG_AEW, REG_AEB, REG_VPT }, 6538c2ecf20Sopenharmony_ci { 0x1c, 0x12, 0x50 }, /* -3 */ 6548c2ecf20Sopenharmony_ci { 0x3d, 0x30, 0x71 }, /* -2 */ 6558c2ecf20Sopenharmony_ci { 0x50, 0x44, 0x92 }, /* -1 */ 6568c2ecf20Sopenharmony_ci { 0x70, 0x64, 0xc3 }, /* 0 */ 6578c2ecf20Sopenharmony_ci { 0x90, 0x84, 0xd4 }, /* +1 */ 6588c2ecf20Sopenharmony_ci { 0xc4, 0xbf, 0xf9 }, /* +2 */ 6598c2ecf20Sopenharmony_ci { 0xd8, 0xd0, 0xfa }, /* +3 */ 6608c2ecf20Sopenharmony_ci }; 6618c2ecf20Sopenharmony_ci int i, ret = 0; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci val += (NUM_BR_LEVELS / 2 + 1); 6648c2ecf20Sopenharmony_ci if (val > NUM_BR_LEVELS) 6658c2ecf20Sopenharmony_ci return -EINVAL; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci for (i = 0; i < NUM_BR_REGS && !ret; i++) 6688c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, regs[0][i], 6698c2ecf20Sopenharmony_ci regs[val][i]); 6708c2ecf20Sopenharmony_ci return ret; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic int ov965x_set_gain(struct ov965x *ov965x, int auto_gain) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct ov965x_ctrls *ctrls = &ov965x->ctrls; 6768c2ecf20Sopenharmony_ci int ret = 0; 6778c2ecf20Sopenharmony_ci u8 reg; 6788c2ecf20Sopenharmony_ci /* 6798c2ecf20Sopenharmony_ci * For manual mode we need to disable AGC first, so 6808c2ecf20Sopenharmony_ci * gain value in REG_VREF, REG_GAIN is not overwritten. 6818c2ecf20Sopenharmony_ci */ 6828c2ecf20Sopenharmony_ci if (ctrls->auto_gain->is_new) { 6838c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_COM8, ®); 6848c2ecf20Sopenharmony_ci if (ret < 0) 6858c2ecf20Sopenharmony_ci return ret; 6868c2ecf20Sopenharmony_ci if (ctrls->auto_gain->val) 6878c2ecf20Sopenharmony_ci reg |= COM8_AGC; 6888c2ecf20Sopenharmony_ci else 6898c2ecf20Sopenharmony_ci reg &= ~COM8_AGC; 6908c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_COM8, reg); 6918c2ecf20Sopenharmony_ci if (ret < 0) 6928c2ecf20Sopenharmony_ci return ret; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (ctrls->gain->is_new && !auto_gain) { 6968c2ecf20Sopenharmony_ci unsigned int gain = ctrls->gain->val; 6978c2ecf20Sopenharmony_ci unsigned int rgain; 6988c2ecf20Sopenharmony_ci int m; 6998c2ecf20Sopenharmony_ci /* 7008c2ecf20Sopenharmony_ci * Convert gain control value to the sensor's gain 7018c2ecf20Sopenharmony_ci * registers (VREF[7:6], GAIN[7:0]) format. 7028c2ecf20Sopenharmony_ci */ 7038c2ecf20Sopenharmony_ci for (m = 6; m >= 0; m--) 7048c2ecf20Sopenharmony_ci if (gain >= (1 << m) * 16) 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci /* Sanity check: don't adjust the gain with a negative value */ 7088c2ecf20Sopenharmony_ci if (m < 0) 7098c2ecf20Sopenharmony_ci return -EINVAL; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci rgain = (gain - ((1 << m) * 16)) / (1 << m); 7128c2ecf20Sopenharmony_ci rgain |= (((1 << m) - 1) << 4); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_GAIN, rgain & 0xff); 7158c2ecf20Sopenharmony_ci if (ret < 0) 7168c2ecf20Sopenharmony_ci return ret; 7178c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_VREF, ®); 7188c2ecf20Sopenharmony_ci if (ret < 0) 7198c2ecf20Sopenharmony_ci return ret; 7208c2ecf20Sopenharmony_ci reg &= ~VREF_GAIN_MASK; 7218c2ecf20Sopenharmony_ci reg |= (((rgain >> 8) & 0x3) << 6); 7228c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_VREF, reg); 7238c2ecf20Sopenharmony_ci if (ret < 0) 7248c2ecf20Sopenharmony_ci return ret; 7258c2ecf20Sopenharmony_ci /* Return updated control's value to userspace */ 7268c2ecf20Sopenharmony_ci ctrls->gain->val = (1 << m) * (16 + (rgain & 0xf)); 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci return ret; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic int ov965x_set_sharpness(struct ov965x *ov965x, unsigned int value) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci u8 com14, edge; 7358c2ecf20Sopenharmony_ci int ret; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_COM14, &com14); 7388c2ecf20Sopenharmony_ci if (ret < 0) 7398c2ecf20Sopenharmony_ci return ret; 7408c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_EDGE, &edge); 7418c2ecf20Sopenharmony_ci if (ret < 0) 7428c2ecf20Sopenharmony_ci return ret; 7438c2ecf20Sopenharmony_ci com14 = value ? com14 | COM14_EDGE_EN : com14 & ~COM14_EDGE_EN; 7448c2ecf20Sopenharmony_ci value--; 7458c2ecf20Sopenharmony_ci if (value > 0x0f) { 7468c2ecf20Sopenharmony_ci com14 |= COM14_EEF_X2; 7478c2ecf20Sopenharmony_ci value >>= 1; 7488c2ecf20Sopenharmony_ci } else { 7498c2ecf20Sopenharmony_ci com14 &= ~COM14_EEF_X2; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_COM14, com14); 7528c2ecf20Sopenharmony_ci if (ret < 0) 7538c2ecf20Sopenharmony_ci return ret; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci edge &= ~EDGE_FACTOR_MASK; 7568c2ecf20Sopenharmony_ci edge |= ((u8)value & 0x0f); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci return ov965x_write(ov965x, REG_EDGE, edge); 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic int ov965x_set_exposure(struct ov965x *ov965x, int exp) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci struct ov965x_ctrls *ctrls = &ov965x->ctrls; 7648c2ecf20Sopenharmony_ci bool auto_exposure = (exp == V4L2_EXPOSURE_AUTO); 7658c2ecf20Sopenharmony_ci int ret; 7668c2ecf20Sopenharmony_ci u8 reg; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (ctrls->auto_exp->is_new) { 7698c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_COM8, ®); 7708c2ecf20Sopenharmony_ci if (ret < 0) 7718c2ecf20Sopenharmony_ci return ret; 7728c2ecf20Sopenharmony_ci if (auto_exposure) 7738c2ecf20Sopenharmony_ci reg |= (COM8_AEC | COM8_AGC); 7748c2ecf20Sopenharmony_ci else 7758c2ecf20Sopenharmony_ci reg &= ~(COM8_AEC | COM8_AGC); 7768c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_COM8, reg); 7778c2ecf20Sopenharmony_ci if (ret < 0) 7788c2ecf20Sopenharmony_ci return ret; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (!auto_exposure && ctrls->exposure->is_new) { 7828c2ecf20Sopenharmony_ci unsigned int exposure = (ctrls->exposure->val * 100) 7838c2ecf20Sopenharmony_ci / ov965x->exp_row_interval; 7848c2ecf20Sopenharmony_ci /* 7858c2ecf20Sopenharmony_ci * Manual exposure value 7868c2ecf20Sopenharmony_ci * [b15:b0] - AECHM (b15:b10), AECH (b9:b2), COM1 (b1:b0) 7878c2ecf20Sopenharmony_ci */ 7888c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_COM1, exposure & 0x3); 7898c2ecf20Sopenharmony_ci if (!ret) 7908c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_AECH, 7918c2ecf20Sopenharmony_ci (exposure >> 2) & 0xff); 7928c2ecf20Sopenharmony_ci if (!ret) 7938c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_AECHM, 7948c2ecf20Sopenharmony_ci (exposure >> 10) & 0x3f); 7958c2ecf20Sopenharmony_ci /* Update the value to minimize rounding errors */ 7968c2ecf20Sopenharmony_ci ctrls->exposure->val = ((exposure * ov965x->exp_row_interval) 7978c2ecf20Sopenharmony_ci + 50) / 100; 7988c2ecf20Sopenharmony_ci if (ret < 0) 7998c2ecf20Sopenharmony_ci return ret; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci v4l2_ctrl_activate(ov965x->ctrls.brightness, !exp); 8038c2ecf20Sopenharmony_ci return 0; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic int ov965x_set_flip(struct ov965x *ov965x) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci u8 mvfp = 0; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci if (ov965x->ctrls.hflip->val) 8118c2ecf20Sopenharmony_ci mvfp |= MVFP_MIRROR; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (ov965x->ctrls.vflip->val) 8148c2ecf20Sopenharmony_ci mvfp |= MVFP_FLIP; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci return ov965x_write(ov965x, REG_MVFP, mvfp); 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci#define NUM_SAT_LEVELS 5 8208c2ecf20Sopenharmony_ci#define NUM_SAT_REGS 6 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic int ov965x_set_saturation(struct ov965x *ov965x, int val) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci static const u8 regs[NUM_SAT_LEVELS][NUM_SAT_REGS] = { 8258c2ecf20Sopenharmony_ci /* MTX(1)...MTX(6) */ 8268c2ecf20Sopenharmony_ci { 0x1d, 0x1f, 0x02, 0x09, 0x13, 0x1c }, /* -2 */ 8278c2ecf20Sopenharmony_ci { 0x2e, 0x31, 0x02, 0x0e, 0x1e, 0x2d }, /* -1 */ 8288c2ecf20Sopenharmony_ci { 0x3a, 0x3d, 0x03, 0x12, 0x26, 0x38 }, /* 0 */ 8298c2ecf20Sopenharmony_ci { 0x46, 0x49, 0x04, 0x16, 0x2e, 0x43 }, /* +1 */ 8308c2ecf20Sopenharmony_ci { 0x57, 0x5c, 0x05, 0x1b, 0x39, 0x54 }, /* +2 */ 8318c2ecf20Sopenharmony_ci }; 8328c2ecf20Sopenharmony_ci u8 addr = REG_MTX(1); 8338c2ecf20Sopenharmony_ci int i, ret = 0; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci val += (NUM_SAT_LEVELS / 2); 8368c2ecf20Sopenharmony_ci if (val >= NUM_SAT_LEVELS) 8378c2ecf20Sopenharmony_ci return -EINVAL; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci for (i = 0; i < NUM_SAT_REGS && !ret; i++) 8408c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, addr + i, regs[val][i]); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci return ret; 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_cistatic int ov965x_set_test_pattern(struct ov965x *ov965x, int value) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci int ret; 8488c2ecf20Sopenharmony_ci u8 reg; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_COM23, ®); 8518c2ecf20Sopenharmony_ci if (ret < 0) 8528c2ecf20Sopenharmony_ci return ret; 8538c2ecf20Sopenharmony_ci reg = value ? reg | COM23_TEST_MODE : reg & ~COM23_TEST_MODE; 8548c2ecf20Sopenharmony_ci return ov965x_write(ov965x, REG_COM23, reg); 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic int __g_volatile_ctrl(struct ov965x *ov965x, struct v4l2_ctrl *ctrl) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci unsigned int exposure, gain, m; 8608c2ecf20Sopenharmony_ci u8 reg0, reg1, reg2; 8618c2ecf20Sopenharmony_ci int ret; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (!ov965x->power) 8648c2ecf20Sopenharmony_ci return 0; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci switch (ctrl->id) { 8678c2ecf20Sopenharmony_ci case V4L2_CID_AUTOGAIN: 8688c2ecf20Sopenharmony_ci if (!ctrl->val) 8698c2ecf20Sopenharmony_ci return 0; 8708c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_GAIN, ®0); 8718c2ecf20Sopenharmony_ci if (ret < 0) 8728c2ecf20Sopenharmony_ci return ret; 8738c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_VREF, ®1); 8748c2ecf20Sopenharmony_ci if (ret < 0) 8758c2ecf20Sopenharmony_ci return ret; 8768c2ecf20Sopenharmony_ci gain = ((reg1 >> 6) << 8) | reg0; 8778c2ecf20Sopenharmony_ci m = 0x01 << fls(gain >> 4); 8788c2ecf20Sopenharmony_ci ov965x->ctrls.gain->val = m * (16 + (gain & 0xf)); 8798c2ecf20Sopenharmony_ci break; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 8828c2ecf20Sopenharmony_ci if (ctrl->val == V4L2_EXPOSURE_MANUAL) 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_COM1, ®0); 8858c2ecf20Sopenharmony_ci if (ret < 0) 8868c2ecf20Sopenharmony_ci return ret; 8878c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_AECH, ®1); 8888c2ecf20Sopenharmony_ci if (ret < 0) 8898c2ecf20Sopenharmony_ci return ret; 8908c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_AECHM, ®2); 8918c2ecf20Sopenharmony_ci if (ret < 0) 8928c2ecf20Sopenharmony_ci return ret; 8938c2ecf20Sopenharmony_ci exposure = ((reg2 & 0x3f) << 10) | (reg1 << 2) | 8948c2ecf20Sopenharmony_ci (reg0 & 0x3); 8958c2ecf20Sopenharmony_ci ov965x->ctrls.exposure->val = ((exposure * 8968c2ecf20Sopenharmony_ci ov965x->exp_row_interval) + 50) / 100; 8978c2ecf20Sopenharmony_ci break; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci return 0; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic int ov965x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = ctrl_to_sd(ctrl); 9068c2ecf20Sopenharmony_ci struct ov965x *ov965x = to_ov965x(sd); 9078c2ecf20Sopenharmony_ci int ret; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "g_ctrl: %s\n", ctrl->name); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 9128c2ecf20Sopenharmony_ci ret = __g_volatile_ctrl(ov965x, ctrl); 9138c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 9148c2ecf20Sopenharmony_ci return ret; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cistatic int ov965x_s_ctrl(struct v4l2_ctrl *ctrl) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = ctrl_to_sd(ctrl); 9208c2ecf20Sopenharmony_ci struct ov965x *ov965x = to_ov965x(sd); 9218c2ecf20Sopenharmony_ci int ret = -EINVAL; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "s_ctrl: %s, value: %d. power: %d\n", 9248c2ecf20Sopenharmony_ci ctrl->name, ctrl->val, ov965x->power); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 9278c2ecf20Sopenharmony_ci /* 9288c2ecf20Sopenharmony_ci * If the device is not powered up now postpone applying control's 9298c2ecf20Sopenharmony_ci * value to the hardware, until it is ready to accept commands. 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci if (ov965x->power == 0) { 9328c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 9338c2ecf20Sopenharmony_ci return 0; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci switch (ctrl->id) { 9378c2ecf20Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 9388c2ecf20Sopenharmony_ci ret = ov965x_set_white_balance(ov965x, ctrl->val); 9398c2ecf20Sopenharmony_ci break; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 9428c2ecf20Sopenharmony_ci ret = ov965x_set_brightness(ov965x, ctrl->val); 9438c2ecf20Sopenharmony_ci break; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE_AUTO: 9468c2ecf20Sopenharmony_ci ret = ov965x_set_exposure(ov965x, ctrl->val); 9478c2ecf20Sopenharmony_ci break; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci case V4L2_CID_AUTOGAIN: 9508c2ecf20Sopenharmony_ci ret = ov965x_set_gain(ov965x, ctrl->val); 9518c2ecf20Sopenharmony_ci break; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 9548c2ecf20Sopenharmony_ci ret = ov965x_set_flip(ov965x); 9558c2ecf20Sopenharmony_ci break; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci case V4L2_CID_POWER_LINE_FREQUENCY: 9588c2ecf20Sopenharmony_ci ret = ov965x_set_banding_filter(ov965x, ctrl->val); 9598c2ecf20Sopenharmony_ci break; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci case V4L2_CID_SATURATION: 9628c2ecf20Sopenharmony_ci ret = ov965x_set_saturation(ov965x, ctrl->val); 9638c2ecf20Sopenharmony_ci break; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci case V4L2_CID_SHARPNESS: 9668c2ecf20Sopenharmony_ci ret = ov965x_set_sharpness(ov965x, ctrl->val); 9678c2ecf20Sopenharmony_ci break; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci case V4L2_CID_TEST_PATTERN: 9708c2ecf20Sopenharmony_ci ret = ov965x_set_test_pattern(ov965x, ctrl->val); 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 9758c2ecf20Sopenharmony_ci return ret; 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops ov965x_ctrl_ops = { 9798c2ecf20Sopenharmony_ci .g_volatile_ctrl = ov965x_g_volatile_ctrl, 9808c2ecf20Sopenharmony_ci .s_ctrl = ov965x_s_ctrl, 9818c2ecf20Sopenharmony_ci}; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic const char * const test_pattern_menu[] = { 9848c2ecf20Sopenharmony_ci "Disabled", 9858c2ecf20Sopenharmony_ci "Color bars", 9868c2ecf20Sopenharmony_ci}; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic int ov965x_initialize_controls(struct ov965x *ov965x) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci const struct v4l2_ctrl_ops *ops = &ov965x_ctrl_ops; 9918c2ecf20Sopenharmony_ci struct ov965x_ctrls *ctrls = &ov965x->ctrls; 9928c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &ctrls->handler; 9938c2ecf20Sopenharmony_ci int ret; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_init(hdl, 16); 9968c2ecf20Sopenharmony_ci if (ret < 0) 9978c2ecf20Sopenharmony_ci return ret; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* Auto/manual white balance */ 10008c2ecf20Sopenharmony_ci ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, 10018c2ecf20Sopenharmony_ci V4L2_CID_AUTO_WHITE_BALANCE, 10028c2ecf20Sopenharmony_ci 0, 1, 1, 1); 10038c2ecf20Sopenharmony_ci ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, 10048c2ecf20Sopenharmony_ci 0, 0xff, 1, 0x80); 10058c2ecf20Sopenharmony_ci ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, 10068c2ecf20Sopenharmony_ci 0, 0xff, 1, 0x80); 10078c2ecf20Sopenharmony_ci /* Auto/manual exposure */ 10088c2ecf20Sopenharmony_ci ctrls->auto_exp = 10098c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu(hdl, ops, 10108c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE_AUTO, 10118c2ecf20Sopenharmony_ci V4L2_EXPOSURE_MANUAL, 0, 10128c2ecf20Sopenharmony_ci V4L2_EXPOSURE_AUTO); 10138c2ecf20Sopenharmony_ci /* Exposure time, in 100 us units. min/max is updated dynamically. */ 10148c2ecf20Sopenharmony_ci ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, 10158c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE_ABSOLUTE, 10168c2ecf20Sopenharmony_ci 2, 1500, 1, 500); 10178c2ecf20Sopenharmony_ci /* Auto/manual gain */ 10188c2ecf20Sopenharmony_ci ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, 10198c2ecf20Sopenharmony_ci 0, 1, 1, 1); 10208c2ecf20Sopenharmony_ci ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 10218c2ecf20Sopenharmony_ci 16, 64 * (16 + 15), 1, 64 * 16); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, 10248c2ecf20Sopenharmony_ci -2, 2, 1, 0); 10258c2ecf20Sopenharmony_ci ctrls->brightness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, 10268c2ecf20Sopenharmony_ci -3, 3, 1, 0); 10278c2ecf20Sopenharmony_ci ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, 10288c2ecf20Sopenharmony_ci 0, 32, 1, 6); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); 10318c2ecf20Sopenharmony_ci ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci ctrls->light_freq = 10348c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu(hdl, ops, 10358c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY, 10368c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_60HZ, ~0x7, 10378c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_50HZ); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, 10408c2ecf20Sopenharmony_ci ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, 10418c2ecf20Sopenharmony_ci test_pattern_menu); 10428c2ecf20Sopenharmony_ci if (hdl->error) { 10438c2ecf20Sopenharmony_ci ret = hdl->error; 10448c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(hdl); 10458c2ecf20Sopenharmony_ci return ret; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; 10498c2ecf20Sopenharmony_ci ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); 10528c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); 10538c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); 10548c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(2, &ctrls->hflip); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci ov965x->sd.ctrl_handler = hdl; 10578c2ecf20Sopenharmony_ci return 0; 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci/* 10618c2ecf20Sopenharmony_ci * V4L2 subdev video and pad level operations 10628c2ecf20Sopenharmony_ci */ 10638c2ecf20Sopenharmony_cistatic void ov965x_get_default_format(struct v4l2_mbus_framefmt *mf) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci mf->width = ov965x_framesizes[0].width; 10668c2ecf20Sopenharmony_ci mf->height = ov965x_framesizes[0].height; 10678c2ecf20Sopenharmony_ci mf->colorspace = ov965x_formats[0].colorspace; 10688c2ecf20Sopenharmony_ci mf->code = ov965x_formats[0].code; 10698c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic int ov965x_enum_mbus_code(struct v4l2_subdev *sd, 10738c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 10748c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci if (code->index >= ARRAY_SIZE(ov965x_formats)) 10778c2ecf20Sopenharmony_ci return -EINVAL; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci code->code = ov965x_formats[code->index].code; 10808c2ecf20Sopenharmony_ci return 0; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic int ov965x_enum_frame_sizes(struct v4l2_subdev *sd, 10848c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 10858c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci int i = ARRAY_SIZE(ov965x_formats); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (fse->index >= ARRAY_SIZE(ov965x_framesizes)) 10908c2ecf20Sopenharmony_ci return -EINVAL; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci while (--i) 10938c2ecf20Sopenharmony_ci if (fse->code == ov965x_formats[i].code) 10948c2ecf20Sopenharmony_ci break; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci fse->code = ov965x_formats[i].code; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci fse->min_width = ov965x_framesizes[fse->index].width; 10998c2ecf20Sopenharmony_ci fse->max_width = fse->min_width; 11008c2ecf20Sopenharmony_ci fse->max_height = ov965x_framesizes[fse->index].height; 11018c2ecf20Sopenharmony_ci fse->min_height = fse->max_height; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci return 0; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic int ov965x_g_frame_interval(struct v4l2_subdev *sd, 11078c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *fi) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci struct ov965x *ov965x = to_ov965x(sd); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 11128c2ecf20Sopenharmony_ci fi->interval = ov965x->fiv->interval; 11138c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci return 0; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_cistatic int __ov965x_set_frame_interval(struct ov965x *ov965x, 11198c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *fi) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mbus_fmt = &ov965x->format; 11228c2ecf20Sopenharmony_ci const struct ov965x_interval *fiv = &ov965x_intervals[0]; 11238c2ecf20Sopenharmony_ci u64 req_int, err, min_err = ~0ULL; 11248c2ecf20Sopenharmony_ci unsigned int i; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (fi->interval.denominator == 0) 11278c2ecf20Sopenharmony_ci return -EINVAL; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci req_int = (u64)fi->interval.numerator * 10000; 11308c2ecf20Sopenharmony_ci do_div(req_int, fi->interval.denominator); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ov965x_intervals); i++) { 11338c2ecf20Sopenharmony_ci const struct ov965x_interval *iv = &ov965x_intervals[i]; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (mbus_fmt->width != iv->size.width || 11368c2ecf20Sopenharmony_ci mbus_fmt->height != iv->size.height) 11378c2ecf20Sopenharmony_ci continue; 11388c2ecf20Sopenharmony_ci err = abs((u64)(iv->interval.numerator * 10000) / 11398c2ecf20Sopenharmony_ci iv->interval.denominator - req_int); 11408c2ecf20Sopenharmony_ci if (err < min_err) { 11418c2ecf20Sopenharmony_ci fiv = iv; 11428c2ecf20Sopenharmony_ci min_err = err; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci ov965x->fiv = fiv; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, &ov965x->sd, "Changed frame interval to %u us\n", 11488c2ecf20Sopenharmony_ci fiv->interval.numerator * 1000000 / fiv->interval.denominator); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci return 0; 11518c2ecf20Sopenharmony_ci} 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_cistatic int ov965x_s_frame_interval(struct v4l2_subdev *sd, 11548c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *fi) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci struct ov965x *ov965x = to_ov965x(sd); 11578c2ecf20Sopenharmony_ci int ret; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", 11608c2ecf20Sopenharmony_ci fi->interval.numerator, fi->interval.denominator); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 11638c2ecf20Sopenharmony_ci ret = __ov965x_set_frame_interval(ov965x, fi); 11648c2ecf20Sopenharmony_ci ov965x->apply_frame_fmt = 1; 11658c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 11668c2ecf20Sopenharmony_ci return ret; 11678c2ecf20Sopenharmony_ci} 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_cistatic int ov965x_get_fmt(struct v4l2_subdev *sd, 11708c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 11718c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci struct ov965x *ov965x = to_ov965x(sd); 11748c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 11778c2ecf20Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, cfg, 0); 11788c2ecf20Sopenharmony_ci fmt->format = *mf; 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 11838c2ecf20Sopenharmony_ci fmt->format = ov965x->format; 11848c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci return 0; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic void __ov965x_try_frame_size(struct v4l2_mbus_framefmt *mf, 11908c2ecf20Sopenharmony_ci const struct ov965x_framesize **size) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci const struct ov965x_framesize *fsize = &ov965x_framesizes[0], 11938c2ecf20Sopenharmony_ci *match = NULL; 11948c2ecf20Sopenharmony_ci int i = ARRAY_SIZE(ov965x_framesizes); 11958c2ecf20Sopenharmony_ci unsigned int min_err = UINT_MAX; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci while (i--) { 11988c2ecf20Sopenharmony_ci int err = abs(fsize->width - mf->width) 11998c2ecf20Sopenharmony_ci + abs(fsize->height - mf->height); 12008c2ecf20Sopenharmony_ci if (err < min_err) { 12018c2ecf20Sopenharmony_ci min_err = err; 12028c2ecf20Sopenharmony_ci match = fsize; 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci fsize++; 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci if (!match) 12078c2ecf20Sopenharmony_ci match = &ov965x_framesizes[0]; 12088c2ecf20Sopenharmony_ci mf->width = match->width; 12098c2ecf20Sopenharmony_ci mf->height = match->height; 12108c2ecf20Sopenharmony_ci if (size) 12118c2ecf20Sopenharmony_ci *size = match; 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cistatic int ov965x_set_fmt(struct v4l2_subdev *sd, 12158c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12168c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci unsigned int index = ARRAY_SIZE(ov965x_formats); 12198c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &fmt->format; 12208c2ecf20Sopenharmony_ci struct ov965x *ov965x = to_ov965x(sd); 12218c2ecf20Sopenharmony_ci const struct ov965x_framesize *size = NULL; 12228c2ecf20Sopenharmony_ci int ret = 0; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci __ov965x_try_frame_size(mf, &size); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci while (--index) 12278c2ecf20Sopenharmony_ci if (ov965x_formats[index].code == mf->code) 12288c2ecf20Sopenharmony_ci break; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci mf->colorspace = V4L2_COLORSPACE_JPEG; 12318c2ecf20Sopenharmony_ci mf->code = ov965x_formats[index].code; 12328c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 12378c2ecf20Sopenharmony_ci if (cfg) { 12388c2ecf20Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 12398c2ecf20Sopenharmony_ci *mf = fmt->format; 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci } else { 12428c2ecf20Sopenharmony_ci if (ov965x->streaming) { 12438c2ecf20Sopenharmony_ci ret = -EBUSY; 12448c2ecf20Sopenharmony_ci } else { 12458c2ecf20Sopenharmony_ci ov965x->frame_size = size; 12468c2ecf20Sopenharmony_ci ov965x->format = fmt->format; 12478c2ecf20Sopenharmony_ci ov965x->tslb_reg = ov965x_formats[index].tslb_reg; 12488c2ecf20Sopenharmony_ci ov965x->apply_frame_fmt = 1; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (!ret && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 12538c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval fiv = { 12548c2ecf20Sopenharmony_ci .interval = { 0, 1 } 12558c2ecf20Sopenharmony_ci }; 12568c2ecf20Sopenharmony_ci /* Reset to minimum possible frame interval */ 12578c2ecf20Sopenharmony_ci __ov965x_set_frame_interval(ov965x, &fiv); 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (!ret) 12628c2ecf20Sopenharmony_ci ov965x_update_exposure_ctrl(ov965x); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci return ret; 12658c2ecf20Sopenharmony_ci} 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistatic int ov965x_set_frame_size(struct ov965x *ov965x) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci int i, ret = 0; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci for (i = 0; ret == 0 && i < NUM_FMT_REGS; i++) 12728c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, frame_size_reg_addr[i], 12738c2ecf20Sopenharmony_ci ov965x->frame_size->regs[i]); 12748c2ecf20Sopenharmony_ci return ret; 12758c2ecf20Sopenharmony_ci} 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic int __ov965x_set_params(struct ov965x *ov965x) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci struct ov965x_ctrls *ctrls = &ov965x->ctrls; 12808c2ecf20Sopenharmony_ci int ret = 0; 12818c2ecf20Sopenharmony_ci u8 reg; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci if (ov965x->apply_frame_fmt) { 12848c2ecf20Sopenharmony_ci reg = DEF_CLKRC + ov965x->fiv->clkrc_div; 12858c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_CLKRC, reg); 12868c2ecf20Sopenharmony_ci if (ret < 0) 12878c2ecf20Sopenharmony_ci return ret; 12888c2ecf20Sopenharmony_ci ret = ov965x_set_frame_size(ov965x); 12898c2ecf20Sopenharmony_ci if (ret < 0) 12908c2ecf20Sopenharmony_ci return ret; 12918c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_TSLB, ®); 12928c2ecf20Sopenharmony_ci if (ret < 0) 12938c2ecf20Sopenharmony_ci return ret; 12948c2ecf20Sopenharmony_ci reg &= ~TSLB_YUYV_MASK; 12958c2ecf20Sopenharmony_ci reg |= ov965x->tslb_reg; 12968c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_TSLB, reg); 12978c2ecf20Sopenharmony_ci if (ret < 0) 12988c2ecf20Sopenharmony_ci return ret; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci ret = ov965x_set_default_gamma_curve(ov965x); 13018c2ecf20Sopenharmony_ci if (ret < 0) 13028c2ecf20Sopenharmony_ci return ret; 13038c2ecf20Sopenharmony_ci ret = ov965x_set_color_matrix(ov965x); 13048c2ecf20Sopenharmony_ci if (ret < 0) 13058c2ecf20Sopenharmony_ci return ret; 13068c2ecf20Sopenharmony_ci /* 13078c2ecf20Sopenharmony_ci * Select manual banding filter, the filter will 13088c2ecf20Sopenharmony_ci * be enabled further if required. 13098c2ecf20Sopenharmony_ci */ 13108c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_COM11, ®); 13118c2ecf20Sopenharmony_ci if (!ret) 13128c2ecf20Sopenharmony_ci reg |= COM11_BANDING; 13138c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_COM11, reg); 13148c2ecf20Sopenharmony_ci if (ret < 0) 13158c2ecf20Sopenharmony_ci return ret; 13168c2ecf20Sopenharmony_ci /* 13178c2ecf20Sopenharmony_ci * Banding filter (REG_MBD value) needs to match selected 13188c2ecf20Sopenharmony_ci * resolution and frame rate, so it's always updated here. 13198c2ecf20Sopenharmony_ci */ 13208c2ecf20Sopenharmony_ci return ov965x_set_banding_filter(ov965x, ctrls->light_freq->val); 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_cistatic int ov965x_s_stream(struct v4l2_subdev *sd, int on) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci struct ov965x *ov965x = to_ov965x(sd); 13268c2ecf20Sopenharmony_ci struct ov965x_ctrls *ctrls = &ov965x->ctrls; 13278c2ecf20Sopenharmony_ci int ret = 0; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 13328c2ecf20Sopenharmony_ci if (ov965x->streaming == !on) { 13338c2ecf20Sopenharmony_ci if (on) 13348c2ecf20Sopenharmony_ci ret = __ov965x_set_params(ov965x); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci if (!ret && ctrls->update) { 13378c2ecf20Sopenharmony_ci /* 13388c2ecf20Sopenharmony_ci * ov965x_s_ctrl callback takes the mutex 13398c2ecf20Sopenharmony_ci * so it needs to be released here. 13408c2ecf20Sopenharmony_ci */ 13418c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 13428c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&ctrls->handler); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 13458c2ecf20Sopenharmony_ci if (!ret) 13468c2ecf20Sopenharmony_ci ctrls->update = 0; 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci if (!ret) 13498c2ecf20Sopenharmony_ci ret = ov965x_write(ov965x, REG_COM2, 13508c2ecf20Sopenharmony_ci on ? 0x01 : 0x11); 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci if (!ret) 13538c2ecf20Sopenharmony_ci ov965x->streaming += on ? 1 : -1; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci WARN_ON(ov965x->streaming < 0); 13568c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci return ret; 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci/* 13628c2ecf20Sopenharmony_ci * V4L2 subdev internal operations 13638c2ecf20Sopenharmony_ci */ 13648c2ecf20Sopenharmony_cistatic int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 13658c2ecf20Sopenharmony_ci{ 13668c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = 13678c2ecf20Sopenharmony_ci v4l2_subdev_get_try_format(sd, fh->pad, 0); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci ov965x_get_default_format(mf); 13708c2ecf20Sopenharmony_ci return 0; 13718c2ecf20Sopenharmony_ci} 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops ov965x_pad_ops = { 13748c2ecf20Sopenharmony_ci .enum_mbus_code = ov965x_enum_mbus_code, 13758c2ecf20Sopenharmony_ci .enum_frame_size = ov965x_enum_frame_sizes, 13768c2ecf20Sopenharmony_ci .get_fmt = ov965x_get_fmt, 13778c2ecf20Sopenharmony_ci .set_fmt = ov965x_set_fmt, 13788c2ecf20Sopenharmony_ci}; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops ov965x_video_ops = { 13818c2ecf20Sopenharmony_ci .s_stream = ov965x_s_stream, 13828c2ecf20Sopenharmony_ci .g_frame_interval = ov965x_g_frame_interval, 13838c2ecf20Sopenharmony_ci .s_frame_interval = ov965x_s_frame_interval, 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci}; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops ov965x_sd_internal_ops = { 13888c2ecf20Sopenharmony_ci .open = ov965x_open, 13898c2ecf20Sopenharmony_ci}; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops ov965x_core_ops = { 13928c2ecf20Sopenharmony_ci .s_power = ov965x_s_power, 13938c2ecf20Sopenharmony_ci .log_status = v4l2_ctrl_subdev_log_status, 13948c2ecf20Sopenharmony_ci .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 13958c2ecf20Sopenharmony_ci .unsubscribe_event = v4l2_event_subdev_unsubscribe, 13968c2ecf20Sopenharmony_ci}; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops ov965x_subdev_ops = { 13998c2ecf20Sopenharmony_ci .core = &ov965x_core_ops, 14008c2ecf20Sopenharmony_ci .pad = &ov965x_pad_ops, 14018c2ecf20Sopenharmony_ci .video = &ov965x_video_ops, 14028c2ecf20Sopenharmony_ci}; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci/* 14058c2ecf20Sopenharmony_ci * Reset and power down GPIOs configuration 14068c2ecf20Sopenharmony_ci */ 14078c2ecf20Sopenharmony_cistatic int ov965x_configure_gpios_pdata(struct ov965x *ov965x, 14088c2ecf20Sopenharmony_ci const struct ov9650_platform_data *pdata) 14098c2ecf20Sopenharmony_ci{ 14108c2ecf20Sopenharmony_ci int ret, i; 14118c2ecf20Sopenharmony_ci int gpios[NUM_GPIOS]; 14128c2ecf20Sopenharmony_ci struct device *dev = regmap_get_device(ov965x->regmap); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci gpios[GPIO_PWDN] = pdata->gpio_pwdn; 14158c2ecf20Sopenharmony_ci gpios[GPIO_RST] = pdata->gpio_reset; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ov965x->gpios); i++) { 14188c2ecf20Sopenharmony_ci int gpio = gpios[i]; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci if (!gpio_is_valid(gpio)) 14218c2ecf20Sopenharmony_ci continue; 14228c2ecf20Sopenharmony_ci ret = devm_gpio_request_one(dev, gpio, 14238c2ecf20Sopenharmony_ci GPIOF_OUT_INIT_HIGH, "OV965X"); 14248c2ecf20Sopenharmony_ci if (ret < 0) 14258c2ecf20Sopenharmony_ci return ret; 14268c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci gpio_set_value_cansleep(gpio, 1); 14298c2ecf20Sopenharmony_ci gpio_export(gpio, 0); 14308c2ecf20Sopenharmony_ci ov965x->gpios[i] = gpio_to_desc(gpio); 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci return 0; 14348c2ecf20Sopenharmony_ci} 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_cistatic int ov965x_configure_gpios(struct ov965x *ov965x) 14378c2ecf20Sopenharmony_ci{ 14388c2ecf20Sopenharmony_ci struct device *dev = regmap_get_device(ov965x->regmap); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci ov965x->gpios[GPIO_PWDN] = devm_gpiod_get_optional(dev, "powerdown", 14418c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 14428c2ecf20Sopenharmony_ci if (IS_ERR(ov965x->gpios[GPIO_PWDN])) { 14438c2ecf20Sopenharmony_ci dev_info(dev, "can't get %s GPIO\n", "powerdown"); 14448c2ecf20Sopenharmony_ci return PTR_ERR(ov965x->gpios[GPIO_PWDN]); 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci ov965x->gpios[GPIO_RST] = devm_gpiod_get_optional(dev, "reset", 14488c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 14498c2ecf20Sopenharmony_ci if (IS_ERR(ov965x->gpios[GPIO_RST])) { 14508c2ecf20Sopenharmony_ci dev_info(dev, "can't get %s GPIO\n", "reset"); 14518c2ecf20Sopenharmony_ci return PTR_ERR(ov965x->gpios[GPIO_RST]); 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci return 0; 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistatic int ov965x_detect_sensor(struct v4l2_subdev *sd) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci struct ov965x *ov965x = to_ov965x(sd); 14608c2ecf20Sopenharmony_ci u8 pid, ver; 14618c2ecf20Sopenharmony_ci int ret; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci mutex_lock(&ov965x->lock); 14648c2ecf20Sopenharmony_ci ret = __ov965x_set_power(ov965x, 1); 14658c2ecf20Sopenharmony_ci if (ret) 14668c2ecf20Sopenharmony_ci goto out; 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci msleep(25); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci /* Check sensor revision */ 14718c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_PID, &pid); 14728c2ecf20Sopenharmony_ci if (!ret) 14738c2ecf20Sopenharmony_ci ret = ov965x_read(ov965x, REG_VER, &ver); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci __ov965x_set_power(ov965x, 0); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if (!ret) { 14788c2ecf20Sopenharmony_ci ov965x->id = OV965X_ID(pid, ver); 14798c2ecf20Sopenharmony_ci if (ov965x->id == OV9650_ID || ov965x->id == OV9652_ID) { 14808c2ecf20Sopenharmony_ci v4l2_info(sd, "Found OV%04X sensor\n", ov965x->id); 14818c2ecf20Sopenharmony_ci } else { 14828c2ecf20Sopenharmony_ci v4l2_err(sd, "Sensor detection failed (%04X, %d)\n", 14838c2ecf20Sopenharmony_ci ov965x->id, ret); 14848c2ecf20Sopenharmony_ci ret = -ENODEV; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ciout: 14888c2ecf20Sopenharmony_ci mutex_unlock(&ov965x->lock); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci return ret; 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_cistatic int ov965x_probe(struct i2c_client *client) 14948c2ecf20Sopenharmony_ci{ 14958c2ecf20Sopenharmony_ci const struct ov9650_platform_data *pdata = client->dev.platform_data; 14968c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 14978c2ecf20Sopenharmony_ci struct ov965x *ov965x; 14988c2ecf20Sopenharmony_ci int ret; 14998c2ecf20Sopenharmony_ci static const struct regmap_config ov965x_regmap_config = { 15008c2ecf20Sopenharmony_ci .reg_bits = 8, 15018c2ecf20Sopenharmony_ci .val_bits = 8, 15028c2ecf20Sopenharmony_ci .max_register = 0xab, 15038c2ecf20Sopenharmony_ci }; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci ov965x = devm_kzalloc(&client->dev, sizeof(*ov965x), GFP_KERNEL); 15068c2ecf20Sopenharmony_ci if (!ov965x) 15078c2ecf20Sopenharmony_ci return -ENOMEM; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci ov965x->regmap = devm_regmap_init_sccb(client, &ov965x_regmap_config); 15108c2ecf20Sopenharmony_ci if (IS_ERR(ov965x->regmap)) { 15118c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to allocate register map\n"); 15128c2ecf20Sopenharmony_ci return PTR_ERR(ov965x->regmap); 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (pdata) { 15168c2ecf20Sopenharmony_ci if (pdata->mclk_frequency == 0) { 15178c2ecf20Sopenharmony_ci dev_err(&client->dev, "MCLK frequency not specified\n"); 15188c2ecf20Sopenharmony_ci return -EINVAL; 15198c2ecf20Sopenharmony_ci } 15208c2ecf20Sopenharmony_ci ov965x->mclk_frequency = pdata->mclk_frequency; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci ret = ov965x_configure_gpios_pdata(ov965x, pdata); 15238c2ecf20Sopenharmony_ci if (ret < 0) 15248c2ecf20Sopenharmony_ci return ret; 15258c2ecf20Sopenharmony_ci } else if (dev_fwnode(&client->dev)) { 15268c2ecf20Sopenharmony_ci ov965x->clk = devm_clk_get(&client->dev, NULL); 15278c2ecf20Sopenharmony_ci if (IS_ERR(ov965x->clk)) 15288c2ecf20Sopenharmony_ci return PTR_ERR(ov965x->clk); 15298c2ecf20Sopenharmony_ci ov965x->mclk_frequency = clk_get_rate(ov965x->clk); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci ret = ov965x_configure_gpios(ov965x); 15328c2ecf20Sopenharmony_ci if (ret < 0) 15338c2ecf20Sopenharmony_ci return ret; 15348c2ecf20Sopenharmony_ci } else { 15358c2ecf20Sopenharmony_ci dev_err(&client->dev, 15368c2ecf20Sopenharmony_ci "Neither platform data nor device property specified\n"); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci return -EINVAL; 15398c2ecf20Sopenharmony_ci } 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci mutex_init(&ov965x->lock); 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci sd = &ov965x->sd; 15448c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops); 15458c2ecf20Sopenharmony_ci strscpy(sd->name, DRIVER_NAME, sizeof(sd->name)); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci sd->internal_ops = &ov965x_sd_internal_ops; 15488c2ecf20Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 15498c2ecf20Sopenharmony_ci V4L2_SUBDEV_FL_HAS_EVENTS; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci ov965x->pad.flags = MEDIA_PAD_FL_SOURCE; 15528c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; 15538c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad); 15548c2ecf20Sopenharmony_ci if (ret < 0) 15558c2ecf20Sopenharmony_ci goto err_mutex; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci ret = ov965x_initialize_controls(ov965x); 15588c2ecf20Sopenharmony_ci if (ret < 0) 15598c2ecf20Sopenharmony_ci goto err_me; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci ov965x_get_default_format(&ov965x->format); 15628c2ecf20Sopenharmony_ci ov965x->frame_size = &ov965x_framesizes[0]; 15638c2ecf20Sopenharmony_ci ov965x->fiv = &ov965x_intervals[0]; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci ret = ov965x_detect_sensor(sd); 15668c2ecf20Sopenharmony_ci if (ret < 0) 15678c2ecf20Sopenharmony_ci goto err_ctrls; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci /* Update exposure time min/max to match frame format */ 15708c2ecf20Sopenharmony_ci ov965x_update_exposure_ctrl(ov965x); 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(sd); 15738c2ecf20Sopenharmony_ci if (ret < 0) 15748c2ecf20Sopenharmony_ci goto err_ctrls; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci return 0; 15778c2ecf20Sopenharmony_cierr_ctrls: 15788c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(sd->ctrl_handler); 15798c2ecf20Sopenharmony_cierr_me: 15808c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 15818c2ecf20Sopenharmony_cierr_mutex: 15828c2ecf20Sopenharmony_ci mutex_destroy(&ov965x->lock); 15838c2ecf20Sopenharmony_ci return ret; 15848c2ecf20Sopenharmony_ci} 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_cistatic int ov965x_remove(struct i2c_client *client) 15878c2ecf20Sopenharmony_ci{ 15888c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 15898c2ecf20Sopenharmony_ci struct ov965x *ov965x = to_ov965x(sd); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(sd); 15928c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(sd->ctrl_handler); 15938c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 15948c2ecf20Sopenharmony_ci mutex_destroy(&ov965x->lock); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci return 0; 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_cistatic const struct i2c_device_id ov965x_id[] = { 16008c2ecf20Sopenharmony_ci { "OV9650", 0 }, 16018c2ecf20Sopenharmony_ci { "OV9652", 0 }, 16028c2ecf20Sopenharmony_ci { /* sentinel */ } 16038c2ecf20Sopenharmony_ci}; 16048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ov965x_id); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF) 16078c2ecf20Sopenharmony_cistatic const struct of_device_id ov965x_of_match[] = { 16088c2ecf20Sopenharmony_ci { .compatible = "ovti,ov9650", }, 16098c2ecf20Sopenharmony_ci { .compatible = "ovti,ov9652", }, 16108c2ecf20Sopenharmony_ci { /* sentinel */ } 16118c2ecf20Sopenharmony_ci}; 16128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ov965x_of_match); 16138c2ecf20Sopenharmony_ci#endif 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_cistatic struct i2c_driver ov965x_i2c_driver = { 16168c2ecf20Sopenharmony_ci .driver = { 16178c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 16188c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ov965x_of_match), 16198c2ecf20Sopenharmony_ci }, 16208c2ecf20Sopenharmony_ci .probe_new = ov965x_probe, 16218c2ecf20Sopenharmony_ci .remove = ov965x_remove, 16228c2ecf20Sopenharmony_ci .id_table = ov965x_id, 16238c2ecf20Sopenharmony_ci}; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_cimodule_i2c_driver(ov965x_i2c_driver); 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sylwester Nawrocki <sylvester.nawrocki@gmail.com>"); 16288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OV9650/OV9652 CMOS Image Sensor driver"); 16298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1630