18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ov772x Camera Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Jacopo Mondi <jacopo+renesas@jmondi.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Renesas Solutions Corp. 88c2ecf20Sopenharmony_ci * Kuninori Morimoto <morimoto.kuninori@renesas.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Based on ov7670 and soc_camera_platform driver, 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> 138c2ecf20Sopenharmony_ci * Copyright (C) 2008 Magnus Damm 148c2ecf20Sopenharmony_ci * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/clk.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 208c2ecf20Sopenharmony_ci#include <linux/i2c.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/regmap.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/v4l2-mediabus.h> 278c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <media/i2c/ov772x.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 328c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 338c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 348c2ecf20Sopenharmony_ci#include <media/v4l2-image-sizes.h> 358c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * register offset 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci#define GAIN 0x00 /* AGC - Gain control gain setting */ 418c2ecf20Sopenharmony_ci#define BLUE 0x01 /* AWB - Blue channel gain setting */ 428c2ecf20Sopenharmony_ci#define RED 0x02 /* AWB - Red channel gain setting */ 438c2ecf20Sopenharmony_ci#define GREEN 0x03 /* AWB - Green channel gain setting */ 448c2ecf20Sopenharmony_ci#define COM1 0x04 /* Common control 1 */ 458c2ecf20Sopenharmony_ci#define BAVG 0x05 /* U/B Average Level */ 468c2ecf20Sopenharmony_ci#define GAVG 0x06 /* Y/Gb Average Level */ 478c2ecf20Sopenharmony_ci#define RAVG 0x07 /* V/R Average Level */ 488c2ecf20Sopenharmony_ci#define AECH 0x08 /* Exposure Value - AEC MSBs */ 498c2ecf20Sopenharmony_ci#define COM2 0x09 /* Common control 2 */ 508c2ecf20Sopenharmony_ci#define PID 0x0A /* Product ID Number MSB */ 518c2ecf20Sopenharmony_ci#define VER 0x0B /* Product ID Number LSB */ 528c2ecf20Sopenharmony_ci#define COM3 0x0C /* Common control 3 */ 538c2ecf20Sopenharmony_ci#define COM4 0x0D /* Common control 4 */ 548c2ecf20Sopenharmony_ci#define COM5 0x0E /* Common control 5 */ 558c2ecf20Sopenharmony_ci#define COM6 0x0F /* Common control 6 */ 568c2ecf20Sopenharmony_ci#define AEC 0x10 /* Exposure Value */ 578c2ecf20Sopenharmony_ci#define CLKRC 0x11 /* Internal clock */ 588c2ecf20Sopenharmony_ci#define COM7 0x12 /* Common control 7 */ 598c2ecf20Sopenharmony_ci#define COM8 0x13 /* Common control 8 */ 608c2ecf20Sopenharmony_ci#define COM9 0x14 /* Common control 9 */ 618c2ecf20Sopenharmony_ci#define COM10 0x15 /* Common control 10 */ 628c2ecf20Sopenharmony_ci#define REG16 0x16 /* Register 16 */ 638c2ecf20Sopenharmony_ci#define HSTART 0x17 /* Horizontal sensor size */ 648c2ecf20Sopenharmony_ci#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */ 658c2ecf20Sopenharmony_ci#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */ 668c2ecf20Sopenharmony_ci#define VSIZE 0x1A /* Vertical sensor size */ 678c2ecf20Sopenharmony_ci#define PSHFT 0x1B /* Data format - pixel delay select */ 688c2ecf20Sopenharmony_ci#define MIDH 0x1C /* Manufacturer ID byte - high */ 698c2ecf20Sopenharmony_ci#define MIDL 0x1D /* Manufacturer ID byte - low */ 708c2ecf20Sopenharmony_ci#define LAEC 0x1F /* Fine AEC value */ 718c2ecf20Sopenharmony_ci#define COM11 0x20 /* Common control 11 */ 728c2ecf20Sopenharmony_ci#define BDBASE 0x22 /* Banding filter Minimum AEC value */ 738c2ecf20Sopenharmony_ci#define DBSTEP 0x23 /* Banding filter Maximum Setp */ 748c2ecf20Sopenharmony_ci#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ 758c2ecf20Sopenharmony_ci#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ 768c2ecf20Sopenharmony_ci#define VPT 0x26 /* AGC/AEC Fast mode operating region */ 778c2ecf20Sopenharmony_ci#define REG28 0x28 /* Register 28 */ 788c2ecf20Sopenharmony_ci#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */ 798c2ecf20Sopenharmony_ci#define EXHCH 0x2A /* Dummy pixel insert MSB */ 808c2ecf20Sopenharmony_ci#define EXHCL 0x2B /* Dummy pixel insert LSB */ 818c2ecf20Sopenharmony_ci#define VOUTSIZE 0x2C /* Vertical data output size MSBs */ 828c2ecf20Sopenharmony_ci#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ 838c2ecf20Sopenharmony_ci#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */ 848c2ecf20Sopenharmony_ci#define YAVE 0x2F /* Y/G Channel Average value */ 858c2ecf20Sopenharmony_ci#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */ 868c2ecf20Sopenharmony_ci#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */ 878c2ecf20Sopenharmony_ci#define HREF 0x32 /* Image start and size control */ 888c2ecf20Sopenharmony_ci#define DM_LNL 0x33 /* Dummy line low 8 bits */ 898c2ecf20Sopenharmony_ci#define DM_LNH 0x34 /* Dummy line high 8 bits */ 908c2ecf20Sopenharmony_ci#define ADOFF_B 0x35 /* AD offset compensation value for B channel */ 918c2ecf20Sopenharmony_ci#define ADOFF_R 0x36 /* AD offset compensation value for R channel */ 928c2ecf20Sopenharmony_ci#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */ 938c2ecf20Sopenharmony_ci#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */ 948c2ecf20Sopenharmony_ci#define OFF_B 0x39 /* Analog process B channel offset value */ 958c2ecf20Sopenharmony_ci#define OFF_R 0x3A /* Analog process R channel offset value */ 968c2ecf20Sopenharmony_ci#define OFF_GB 0x3B /* Analog process Gb channel offset value */ 978c2ecf20Sopenharmony_ci#define OFF_GR 0x3C /* Analog process Gr channel offset value */ 988c2ecf20Sopenharmony_ci#define COM12 0x3D /* Common control 12 */ 998c2ecf20Sopenharmony_ci#define COM13 0x3E /* Common control 13 */ 1008c2ecf20Sopenharmony_ci#define COM14 0x3F /* Common control 14 */ 1018c2ecf20Sopenharmony_ci#define COM15 0x40 /* Common control 15*/ 1028c2ecf20Sopenharmony_ci#define COM16 0x41 /* Common control 16 */ 1038c2ecf20Sopenharmony_ci#define TGT_B 0x42 /* BLC blue channel target value */ 1048c2ecf20Sopenharmony_ci#define TGT_R 0x43 /* BLC red channel target value */ 1058c2ecf20Sopenharmony_ci#define TGT_GB 0x44 /* BLC Gb channel target value */ 1068c2ecf20Sopenharmony_ci#define TGT_GR 0x45 /* BLC Gr channel target value */ 1078c2ecf20Sopenharmony_ci/* for ov7720 */ 1088c2ecf20Sopenharmony_ci#define LCC0 0x46 /* Lens correction control 0 */ 1098c2ecf20Sopenharmony_ci#define LCC1 0x47 /* Lens correction option 1 - X coordinate */ 1108c2ecf20Sopenharmony_ci#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */ 1118c2ecf20Sopenharmony_ci#define LCC3 0x49 /* Lens correction option 3 */ 1128c2ecf20Sopenharmony_ci#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */ 1138c2ecf20Sopenharmony_ci#define LCC5 0x4B /* Lens correction option 5 */ 1148c2ecf20Sopenharmony_ci#define LCC6 0x4C /* Lens correction option 6 */ 1158c2ecf20Sopenharmony_ci/* for ov7725 */ 1168c2ecf20Sopenharmony_ci#define LC_CTR 0x46 /* Lens correction control */ 1178c2ecf20Sopenharmony_ci#define LC_XC 0x47 /* X coordinate of lens correction center relative */ 1188c2ecf20Sopenharmony_ci#define LC_YC 0x48 /* Y coordinate of lens correction center relative */ 1198c2ecf20Sopenharmony_ci#define LC_COEF 0x49 /* Lens correction coefficient */ 1208c2ecf20Sopenharmony_ci#define LC_RADI 0x4A /* Lens correction radius */ 1218c2ecf20Sopenharmony_ci#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */ 1228c2ecf20Sopenharmony_ci#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */ 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define FIXGAIN 0x4D /* Analog fix gain amplifer */ 1258c2ecf20Sopenharmony_ci#define AREF0 0x4E /* Sensor reference control */ 1268c2ecf20Sopenharmony_ci#define AREF1 0x4F /* Sensor reference current control */ 1278c2ecf20Sopenharmony_ci#define AREF2 0x50 /* Analog reference control */ 1288c2ecf20Sopenharmony_ci#define AREF3 0x51 /* ADC reference control */ 1298c2ecf20Sopenharmony_ci#define AREF4 0x52 /* ADC reference control */ 1308c2ecf20Sopenharmony_ci#define AREF5 0x53 /* ADC reference control */ 1318c2ecf20Sopenharmony_ci#define AREF6 0x54 /* Analog reference control */ 1328c2ecf20Sopenharmony_ci#define AREF7 0x55 /* Analog reference control */ 1338c2ecf20Sopenharmony_ci#define UFIX 0x60 /* U channel fixed value output */ 1348c2ecf20Sopenharmony_ci#define VFIX 0x61 /* V channel fixed value output */ 1358c2ecf20Sopenharmony_ci#define AWBB_BLK 0x62 /* AWB option for advanced AWB */ 1368c2ecf20Sopenharmony_ci#define AWB_CTRL0 0x63 /* AWB control byte 0 */ 1378c2ecf20Sopenharmony_ci#define DSP_CTRL1 0x64 /* DSP control byte 1 */ 1388c2ecf20Sopenharmony_ci#define DSP_CTRL2 0x65 /* DSP control byte 2 */ 1398c2ecf20Sopenharmony_ci#define DSP_CTRL3 0x66 /* DSP control byte 3 */ 1408c2ecf20Sopenharmony_ci#define DSP_CTRL4 0x67 /* DSP control byte 4 */ 1418c2ecf20Sopenharmony_ci#define AWB_BIAS 0x68 /* AWB BLC level clip */ 1428c2ecf20Sopenharmony_ci#define AWB_CTRL1 0x69 /* AWB control 1 */ 1438c2ecf20Sopenharmony_ci#define AWB_CTRL2 0x6A /* AWB control 2 */ 1448c2ecf20Sopenharmony_ci#define AWB_CTRL3 0x6B /* AWB control 3 */ 1458c2ecf20Sopenharmony_ci#define AWB_CTRL4 0x6C /* AWB control 4 */ 1468c2ecf20Sopenharmony_ci#define AWB_CTRL5 0x6D /* AWB control 5 */ 1478c2ecf20Sopenharmony_ci#define AWB_CTRL6 0x6E /* AWB control 6 */ 1488c2ecf20Sopenharmony_ci#define AWB_CTRL7 0x6F /* AWB control 7 */ 1498c2ecf20Sopenharmony_ci#define AWB_CTRL8 0x70 /* AWB control 8 */ 1508c2ecf20Sopenharmony_ci#define AWB_CTRL9 0x71 /* AWB control 9 */ 1518c2ecf20Sopenharmony_ci#define AWB_CTRL10 0x72 /* AWB control 10 */ 1528c2ecf20Sopenharmony_ci#define AWB_CTRL11 0x73 /* AWB control 11 */ 1538c2ecf20Sopenharmony_ci#define AWB_CTRL12 0x74 /* AWB control 12 */ 1548c2ecf20Sopenharmony_ci#define AWB_CTRL13 0x75 /* AWB control 13 */ 1558c2ecf20Sopenharmony_ci#define AWB_CTRL14 0x76 /* AWB control 14 */ 1568c2ecf20Sopenharmony_ci#define AWB_CTRL15 0x77 /* AWB control 15 */ 1578c2ecf20Sopenharmony_ci#define AWB_CTRL16 0x78 /* AWB control 16 */ 1588c2ecf20Sopenharmony_ci#define AWB_CTRL17 0x79 /* AWB control 17 */ 1598c2ecf20Sopenharmony_ci#define AWB_CTRL18 0x7A /* AWB control 18 */ 1608c2ecf20Sopenharmony_ci#define AWB_CTRL19 0x7B /* AWB control 19 */ 1618c2ecf20Sopenharmony_ci#define AWB_CTRL20 0x7C /* AWB control 20 */ 1628c2ecf20Sopenharmony_ci#define AWB_CTRL21 0x7D /* AWB control 21 */ 1638c2ecf20Sopenharmony_ci#define GAM1 0x7E /* Gamma Curve 1st segment input end point */ 1648c2ecf20Sopenharmony_ci#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */ 1658c2ecf20Sopenharmony_ci#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */ 1668c2ecf20Sopenharmony_ci#define GAM4 0x81 /* Gamma Curve 4th segment input end point */ 1678c2ecf20Sopenharmony_ci#define GAM5 0x82 /* Gamma Curve 5th segment input end point */ 1688c2ecf20Sopenharmony_ci#define GAM6 0x83 /* Gamma Curve 6th segment input end point */ 1698c2ecf20Sopenharmony_ci#define GAM7 0x84 /* Gamma Curve 7th segment input end point */ 1708c2ecf20Sopenharmony_ci#define GAM8 0x85 /* Gamma Curve 8th segment input end point */ 1718c2ecf20Sopenharmony_ci#define GAM9 0x86 /* Gamma Curve 9th segment input end point */ 1728c2ecf20Sopenharmony_ci#define GAM10 0x87 /* Gamma Curve 10th segment input end point */ 1738c2ecf20Sopenharmony_ci#define GAM11 0x88 /* Gamma Curve 11th segment input end point */ 1748c2ecf20Sopenharmony_ci#define GAM12 0x89 /* Gamma Curve 12th segment input end point */ 1758c2ecf20Sopenharmony_ci#define GAM13 0x8A /* Gamma Curve 13th segment input end point */ 1768c2ecf20Sopenharmony_ci#define GAM14 0x8B /* Gamma Curve 14th segment input end point */ 1778c2ecf20Sopenharmony_ci#define GAM15 0x8C /* Gamma Curve 15th segment input end point */ 1788c2ecf20Sopenharmony_ci#define SLOP 0x8D /* Gamma curve highest segment slope */ 1798c2ecf20Sopenharmony_ci#define DNSTH 0x8E /* De-noise threshold */ 1808c2ecf20Sopenharmony_ci#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */ 1818c2ecf20Sopenharmony_ci#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */ 1828c2ecf20Sopenharmony_ci#define DNSOFF 0x91 /* Auto De-noise threshold control */ 1838c2ecf20Sopenharmony_ci#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */ 1848c2ecf20Sopenharmony_ci#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */ 1858c2ecf20Sopenharmony_ci#define MTX1 0x94 /* Matrix coefficient 1 */ 1868c2ecf20Sopenharmony_ci#define MTX2 0x95 /* Matrix coefficient 2 */ 1878c2ecf20Sopenharmony_ci#define MTX3 0x96 /* Matrix coefficient 3 */ 1888c2ecf20Sopenharmony_ci#define MTX4 0x97 /* Matrix coefficient 4 */ 1898c2ecf20Sopenharmony_ci#define MTX5 0x98 /* Matrix coefficient 5 */ 1908c2ecf20Sopenharmony_ci#define MTX6 0x99 /* Matrix coefficient 6 */ 1918c2ecf20Sopenharmony_ci#define MTX_CTRL 0x9A /* Matrix control */ 1928c2ecf20Sopenharmony_ci#define BRIGHT 0x9B /* Brightness control */ 1938c2ecf20Sopenharmony_ci#define CNTRST 0x9C /* Contrast contrast */ 1948c2ecf20Sopenharmony_ci#define CNTRST_CTRL 0x9D /* Contrast contrast center */ 1958c2ecf20Sopenharmony_ci#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */ 1968c2ecf20Sopenharmony_ci#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */ 1978c2ecf20Sopenharmony_ci#define SCAL0 0xA0 /* Scaling control 0 */ 1988c2ecf20Sopenharmony_ci#define SCAL1 0xA1 /* Scaling control 1 */ 1998c2ecf20Sopenharmony_ci#define SCAL2 0xA2 /* Scaling control 2 */ 2008c2ecf20Sopenharmony_ci#define FIFODLYM 0xA3 /* FIFO manual mode delay control */ 2018c2ecf20Sopenharmony_ci#define FIFODLYA 0xA4 /* FIFO auto mode delay control */ 2028c2ecf20Sopenharmony_ci#define SDE 0xA6 /* Special digital effect control */ 2038c2ecf20Sopenharmony_ci#define USAT 0xA7 /* U component saturation control */ 2048c2ecf20Sopenharmony_ci#define VSAT 0xA8 /* V component saturation control */ 2058c2ecf20Sopenharmony_ci/* for ov7720 */ 2068c2ecf20Sopenharmony_ci#define HUE0 0xA9 /* Hue control 0 */ 2078c2ecf20Sopenharmony_ci#define HUE1 0xAA /* Hue control 1 */ 2088c2ecf20Sopenharmony_ci/* for ov7725 */ 2098c2ecf20Sopenharmony_ci#define HUECOS 0xA9 /* Cosine value */ 2108c2ecf20Sopenharmony_ci#define HUESIN 0xAA /* Sine value */ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci#define SIGN 0xAB /* Sign bit for Hue and contrast */ 2138c2ecf20Sopenharmony_ci#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */ 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* 2168c2ecf20Sopenharmony_ci * register detail 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* COM2 */ 2208c2ecf20Sopenharmony_ci#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ 2218c2ecf20Sopenharmony_ci /* Output drive capability */ 2228c2ecf20Sopenharmony_ci#define OCAP_1x 0x00 /* 1x */ 2238c2ecf20Sopenharmony_ci#define OCAP_2x 0x01 /* 2x */ 2248c2ecf20Sopenharmony_ci#define OCAP_3x 0x02 /* 3x */ 2258c2ecf20Sopenharmony_ci#define OCAP_4x 0x03 /* 4x */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* COM3 */ 2288c2ecf20Sopenharmony_ci#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML) 2298c2ecf20Sopenharmony_ci#define IMG_MASK (VFLIP_IMG | HFLIP_IMG) 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */ 2328c2ecf20Sopenharmony_ci#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */ 2338c2ecf20Sopenharmony_ci#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */ 2348c2ecf20Sopenharmony_ci#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */ 2358c2ecf20Sopenharmony_ci#define SWAP_ML 0x08 /* Swap output MSB/LSB */ 2368c2ecf20Sopenharmony_ci /* Tri-state option for output clock */ 2378c2ecf20Sopenharmony_ci#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */ 2388c2ecf20Sopenharmony_ci /* 1: No tri-state at this period */ 2398c2ecf20Sopenharmony_ci /* Tri-state option for output data */ 2408c2ecf20Sopenharmony_ci#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */ 2418c2ecf20Sopenharmony_ci /* 1: No tri-state at this period */ 2428c2ecf20Sopenharmony_ci#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */ 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* COM4 */ 2458c2ecf20Sopenharmony_ci /* PLL frequency control */ 2468c2ecf20Sopenharmony_ci#define PLL_BYPASS 0x00 /* 00: Bypass PLL */ 2478c2ecf20Sopenharmony_ci#define PLL_4x 0x40 /* 01: PLL 4x */ 2488c2ecf20Sopenharmony_ci#define PLL_6x 0x80 /* 10: PLL 6x */ 2498c2ecf20Sopenharmony_ci#define PLL_8x 0xc0 /* 11: PLL 8x */ 2508c2ecf20Sopenharmony_ci /* AEC evaluate window */ 2518c2ecf20Sopenharmony_ci#define AEC_FULL 0x00 /* 00: Full window */ 2528c2ecf20Sopenharmony_ci#define AEC_1p2 0x10 /* 01: 1/2 window */ 2538c2ecf20Sopenharmony_ci#define AEC_1p4 0x20 /* 10: 1/4 window */ 2548c2ecf20Sopenharmony_ci#define AEC_2p3 0x30 /* 11: Low 2/3 window */ 2558c2ecf20Sopenharmony_ci#define COM4_RESERVED 0x01 /* Reserved bit */ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* COM5 */ 2588c2ecf20Sopenharmony_ci#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */ 2598c2ecf20Sopenharmony_ci#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */ 2608c2ecf20Sopenharmony_ci /* Auto frame rate max rate control */ 2618c2ecf20Sopenharmony_ci#define AFR_NO_RATE 0x00 /* No reduction of frame rate */ 2628c2ecf20Sopenharmony_ci#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */ 2638c2ecf20Sopenharmony_ci#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */ 2648c2ecf20Sopenharmony_ci#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */ 2658c2ecf20Sopenharmony_ci /* Auto frame rate active point control */ 2668c2ecf20Sopenharmony_ci#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */ 2678c2ecf20Sopenharmony_ci#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */ 2688c2ecf20Sopenharmony_ci#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */ 2698c2ecf20Sopenharmony_ci#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */ 2708c2ecf20Sopenharmony_ci /* AEC max step control */ 2718c2ecf20Sopenharmony_ci#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */ 2728c2ecf20Sopenharmony_ci /* 1 : No limit to AEC increase step */ 2738c2ecf20Sopenharmony_ci/* CLKRC */ 2748c2ecf20Sopenharmony_ci /* Input clock divider register */ 2758c2ecf20Sopenharmony_ci#define CLKRC_RESERVED 0x80 /* Reserved bit */ 2768c2ecf20Sopenharmony_ci#define CLKRC_DIV(n) ((n) - 1) 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/* COM7 */ 2798c2ecf20Sopenharmony_ci /* SCCB Register Reset */ 2808c2ecf20Sopenharmony_ci#define SCCB_RESET 0x80 /* 0 : No change */ 2818c2ecf20Sopenharmony_ci /* 1 : Resets all registers to default */ 2828c2ecf20Sopenharmony_ci /* Resolution selection */ 2838c2ecf20Sopenharmony_ci#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */ 2848c2ecf20Sopenharmony_ci#define SLCT_VGA 0x00 /* 0 : VGA */ 2858c2ecf20Sopenharmony_ci#define SLCT_QVGA 0x40 /* 1 : QVGA */ 2868c2ecf20Sopenharmony_ci#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */ 2878c2ecf20Sopenharmony_ci#define SENSOR_RAW 0x10 /* Sensor RAW */ 2888c2ecf20Sopenharmony_ci /* RGB output format control */ 2898c2ecf20Sopenharmony_ci#define FMT_MASK 0x0c /* Mask of color format */ 2908c2ecf20Sopenharmony_ci#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */ 2918c2ecf20Sopenharmony_ci#define FMT_RGB565 0x04 /* 01 : RGB 565 */ 2928c2ecf20Sopenharmony_ci#define FMT_RGB555 0x08 /* 10 : RGB 555 */ 2938c2ecf20Sopenharmony_ci#define FMT_RGB444 0x0c /* 11 : RGB 444 */ 2948c2ecf20Sopenharmony_ci /* Output format control */ 2958c2ecf20Sopenharmony_ci#define OFMT_MASK 0x03 /* Mask of output format */ 2968c2ecf20Sopenharmony_ci#define OFMT_YUV 0x00 /* 00 : YUV */ 2978c2ecf20Sopenharmony_ci#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */ 2988c2ecf20Sopenharmony_ci#define OFMT_RGB 0x02 /* 10 : RGB */ 2998c2ecf20Sopenharmony_ci#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */ 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* COM8 */ 3028c2ecf20Sopenharmony_ci#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */ 3038c2ecf20Sopenharmony_ci /* AEC Setp size limit */ 3048c2ecf20Sopenharmony_ci#define UNLMT_STEP 0x40 /* 0 : Step size is limited */ 3058c2ecf20Sopenharmony_ci /* 1 : Unlimited step size */ 3068c2ecf20Sopenharmony_ci#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */ 3078c2ecf20Sopenharmony_ci#define AEC_BND 0x10 /* Enable AEC below banding value */ 3088c2ecf20Sopenharmony_ci#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */ 3098c2ecf20Sopenharmony_ci#define AGC_ON 0x04 /* AGC Enable */ 3108c2ecf20Sopenharmony_ci#define AWB_ON 0x02 /* AWB Enable */ 3118c2ecf20Sopenharmony_ci#define AEC_ON 0x01 /* AEC Enable */ 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* COM9 */ 3148c2ecf20Sopenharmony_ci#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */ 3158c2ecf20Sopenharmony_ci /* Automatic gain ceiling - maximum AGC value */ 3168c2ecf20Sopenharmony_ci#define GAIN_2x 0x00 /* 000 : 2x */ 3178c2ecf20Sopenharmony_ci#define GAIN_4x 0x10 /* 001 : 4x */ 3188c2ecf20Sopenharmony_ci#define GAIN_8x 0x20 /* 010 : 8x */ 3198c2ecf20Sopenharmony_ci#define GAIN_16x 0x30 /* 011 : 16x */ 3208c2ecf20Sopenharmony_ci#define GAIN_32x 0x40 /* 100 : 32x */ 3218c2ecf20Sopenharmony_ci#define GAIN_64x 0x50 /* 101 : 64x */ 3228c2ecf20Sopenharmony_ci#define GAIN_128x 0x60 /* 110 : 128x */ 3238c2ecf20Sopenharmony_ci#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ 3248c2ecf20Sopenharmony_ci#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* COM11 */ 3278c2ecf20Sopenharmony_ci#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */ 3288c2ecf20Sopenharmony_ci#define SGLF_TRIG 0x01 /* Single frame transfer trigger */ 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* HREF */ 3318c2ecf20Sopenharmony_ci#define HREF_VSTART_SHIFT 6 /* VSTART LSB */ 3328c2ecf20Sopenharmony_ci#define HREF_HSTART_SHIFT 4 /* HSTART 2 LSBs */ 3338c2ecf20Sopenharmony_ci#define HREF_VSIZE_SHIFT 2 /* VSIZE LSB */ 3348c2ecf20Sopenharmony_ci#define HREF_HSIZE_SHIFT 0 /* HSIZE 2 LSBs */ 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* EXHCH */ 3378c2ecf20Sopenharmony_ci#define EXHCH_VSIZE_SHIFT 2 /* VOUTSIZE LSB */ 3388c2ecf20Sopenharmony_ci#define EXHCH_HSIZE_SHIFT 0 /* HOUTSIZE 2 LSBs */ 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/* DSP_CTRL1 */ 3418c2ecf20Sopenharmony_ci#define FIFO_ON 0x80 /* FIFO enable/disable selection */ 3428c2ecf20Sopenharmony_ci#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */ 3438c2ecf20Sopenharmony_ci#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */ 3448c2ecf20Sopenharmony_ci#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */ 3458c2ecf20Sopenharmony_ci#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */ 3468c2ecf20Sopenharmony_ci#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */ 3478c2ecf20Sopenharmony_ci#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */ 3488c2ecf20Sopenharmony_ci#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */ 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/* DSP_CTRL3 */ 3518c2ecf20Sopenharmony_ci#define UV_MASK 0x80 /* UV output sequence option */ 3528c2ecf20Sopenharmony_ci#define UV_ON 0x80 /* ON */ 3538c2ecf20Sopenharmony_ci#define UV_OFF 0x00 /* OFF */ 3548c2ecf20Sopenharmony_ci#define CBAR_MASK 0x20 /* DSP Color bar mask */ 3558c2ecf20Sopenharmony_ci#define CBAR_ON 0x20 /* ON */ 3568c2ecf20Sopenharmony_ci#define CBAR_OFF 0x00 /* OFF */ 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/* DSP_CTRL4 */ 3598c2ecf20Sopenharmony_ci#define DSP_OFMT_YUV 0x00 3608c2ecf20Sopenharmony_ci#define DSP_OFMT_RGB 0x00 3618c2ecf20Sopenharmony_ci#define DSP_OFMT_RAW8 0x02 3628c2ecf20Sopenharmony_ci#define DSP_OFMT_RAW10 0x03 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* DSPAUTO (DSP Auto Function ON/OFF Control) */ 3658c2ecf20Sopenharmony_ci#define AWB_ACTRL 0x80 /* AWB auto threshold control */ 3668c2ecf20Sopenharmony_ci#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */ 3678c2ecf20Sopenharmony_ci#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */ 3688c2ecf20Sopenharmony_ci#define UV_ACTRL 0x10 /* UV adjust auto slope control */ 3698c2ecf20Sopenharmony_ci#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */ 3708c2ecf20Sopenharmony_ci#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */ 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci#define OV772X_MAX_WIDTH VGA_WIDTH 3738c2ecf20Sopenharmony_ci#define OV772X_MAX_HEIGHT VGA_HEIGHT 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/* 3768c2ecf20Sopenharmony_ci * ID 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci#define OV7720 0x7720 3798c2ecf20Sopenharmony_ci#define OV7725 0x7721 3808c2ecf20Sopenharmony_ci#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/* 3838c2ecf20Sopenharmony_ci * PLL multipliers 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_cistatic struct { 3868c2ecf20Sopenharmony_ci unsigned int mult; 3878c2ecf20Sopenharmony_ci u8 com4; 3888c2ecf20Sopenharmony_ci} ov772x_pll[] = { 3898c2ecf20Sopenharmony_ci { 1, PLL_BYPASS, }, 3908c2ecf20Sopenharmony_ci { 4, PLL_4x, }, 3918c2ecf20Sopenharmony_ci { 6, PLL_6x, }, 3928c2ecf20Sopenharmony_ci { 8, PLL_8x, }, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* 3968c2ecf20Sopenharmony_ci * struct 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistruct ov772x_color_format { 4008c2ecf20Sopenharmony_ci u32 code; 4018c2ecf20Sopenharmony_ci enum v4l2_colorspace colorspace; 4028c2ecf20Sopenharmony_ci u8 dsp3; 4038c2ecf20Sopenharmony_ci u8 dsp4; 4048c2ecf20Sopenharmony_ci u8 com3; 4058c2ecf20Sopenharmony_ci u8 com7; 4068c2ecf20Sopenharmony_ci}; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistruct ov772x_win_size { 4098c2ecf20Sopenharmony_ci char *name; 4108c2ecf20Sopenharmony_ci unsigned char com7_bit; 4118c2ecf20Sopenharmony_ci unsigned int sizeimage; 4128c2ecf20Sopenharmony_ci struct v4l2_rect rect; 4138c2ecf20Sopenharmony_ci}; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistruct ov772x_priv { 4168c2ecf20Sopenharmony_ci struct v4l2_subdev subdev; 4178c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 4188c2ecf20Sopenharmony_ci struct clk *clk; 4198c2ecf20Sopenharmony_ci struct regmap *regmap; 4208c2ecf20Sopenharmony_ci struct ov772x_camera_info *info; 4218c2ecf20Sopenharmony_ci struct gpio_desc *pwdn_gpio; 4228c2ecf20Sopenharmony_ci struct gpio_desc *rstb_gpio; 4238c2ecf20Sopenharmony_ci const struct ov772x_color_format *cfmt; 4248c2ecf20Sopenharmony_ci const struct ov772x_win_size *win; 4258c2ecf20Sopenharmony_ci struct v4l2_ctrl *vflip_ctrl; 4268c2ecf20Sopenharmony_ci struct v4l2_ctrl *hflip_ctrl; 4278c2ecf20Sopenharmony_ci /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ 4288c2ecf20Sopenharmony_ci struct v4l2_ctrl *band_filter_ctrl; 4298c2ecf20Sopenharmony_ci unsigned int fps; 4308c2ecf20Sopenharmony_ci /* lock to protect power_count and streaming */ 4318c2ecf20Sopenharmony_ci struct mutex lock; 4328c2ecf20Sopenharmony_ci int power_count; 4338c2ecf20Sopenharmony_ci int streaming; 4348c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 4358c2ecf20Sopenharmony_ci struct media_pad pad; 4368c2ecf20Sopenharmony_ci#endif 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci/* 4408c2ecf20Sopenharmony_ci * supported color format list 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_cistatic const struct ov772x_color_format ov772x_cfmts[] = { 4438c2ecf20Sopenharmony_ci { 4448c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_YUYV8_2X8, 4458c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 4468c2ecf20Sopenharmony_ci .dsp3 = 0x0, 4478c2ecf20Sopenharmony_ci .dsp4 = DSP_OFMT_YUV, 4488c2ecf20Sopenharmony_ci .com3 = SWAP_YUV, 4498c2ecf20Sopenharmony_ci .com7 = OFMT_YUV, 4508c2ecf20Sopenharmony_ci }, 4518c2ecf20Sopenharmony_ci { 4528c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_YVYU8_2X8, 4538c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 4548c2ecf20Sopenharmony_ci .dsp3 = UV_ON, 4558c2ecf20Sopenharmony_ci .dsp4 = DSP_OFMT_YUV, 4568c2ecf20Sopenharmony_ci .com3 = SWAP_YUV, 4578c2ecf20Sopenharmony_ci .com7 = OFMT_YUV, 4588c2ecf20Sopenharmony_ci }, 4598c2ecf20Sopenharmony_ci { 4608c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_UYVY8_2X8, 4618c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 4628c2ecf20Sopenharmony_ci .dsp3 = 0x0, 4638c2ecf20Sopenharmony_ci .dsp4 = DSP_OFMT_YUV, 4648c2ecf20Sopenharmony_ci .com3 = 0x0, 4658c2ecf20Sopenharmony_ci .com7 = OFMT_YUV, 4668c2ecf20Sopenharmony_ci }, 4678c2ecf20Sopenharmony_ci { 4688c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, 4698c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 4708c2ecf20Sopenharmony_ci .dsp3 = 0x0, 4718c2ecf20Sopenharmony_ci .dsp4 = DSP_OFMT_YUV, 4728c2ecf20Sopenharmony_ci .com3 = SWAP_RGB, 4738c2ecf20Sopenharmony_ci .com7 = FMT_RGB555 | OFMT_RGB, 4748c2ecf20Sopenharmony_ci }, 4758c2ecf20Sopenharmony_ci { 4768c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, 4778c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 4788c2ecf20Sopenharmony_ci .dsp3 = 0x0, 4798c2ecf20Sopenharmony_ci .dsp4 = DSP_OFMT_YUV, 4808c2ecf20Sopenharmony_ci .com3 = 0x0, 4818c2ecf20Sopenharmony_ci .com7 = FMT_RGB555 | OFMT_RGB, 4828c2ecf20Sopenharmony_ci }, 4838c2ecf20Sopenharmony_ci { 4848c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB565_2X8_LE, 4858c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 4868c2ecf20Sopenharmony_ci .dsp3 = 0x0, 4878c2ecf20Sopenharmony_ci .dsp4 = DSP_OFMT_YUV, 4888c2ecf20Sopenharmony_ci .com3 = SWAP_RGB, 4898c2ecf20Sopenharmony_ci .com7 = FMT_RGB565 | OFMT_RGB, 4908c2ecf20Sopenharmony_ci }, 4918c2ecf20Sopenharmony_ci { 4928c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB565_2X8_BE, 4938c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 4948c2ecf20Sopenharmony_ci .dsp3 = 0x0, 4958c2ecf20Sopenharmony_ci .dsp4 = DSP_OFMT_YUV, 4968c2ecf20Sopenharmony_ci .com3 = 0x0, 4978c2ecf20Sopenharmony_ci .com7 = FMT_RGB565 | OFMT_RGB, 4988c2ecf20Sopenharmony_ci }, 4998c2ecf20Sopenharmony_ci { 5008c2ecf20Sopenharmony_ci /* Setting DSP4 to DSP_OFMT_RAW8 still gives 10-bit output, 5018c2ecf20Sopenharmony_ci * regardless of the COM7 value. We can thus only support 10-bit 5028c2ecf20Sopenharmony_ci * Bayer until someone figures it out. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SBGGR10_1X10, 5058c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 5068c2ecf20Sopenharmony_ci .dsp3 = 0x0, 5078c2ecf20Sopenharmony_ci .dsp4 = DSP_OFMT_RAW10, 5088c2ecf20Sopenharmony_ci .com3 = 0x0, 5098c2ecf20Sopenharmony_ci .com7 = SENSOR_RAW | OFMT_BRAW, 5108c2ecf20Sopenharmony_ci }, 5118c2ecf20Sopenharmony_ci}; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/* 5148c2ecf20Sopenharmony_ci * window size list 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic const struct ov772x_win_size ov772x_win_sizes[] = { 5188c2ecf20Sopenharmony_ci { 5198c2ecf20Sopenharmony_ci .name = "VGA", 5208c2ecf20Sopenharmony_ci .com7_bit = SLCT_VGA, 5218c2ecf20Sopenharmony_ci .sizeimage = 510 * 748, 5228c2ecf20Sopenharmony_ci .rect = { 5238c2ecf20Sopenharmony_ci .left = 140, 5248c2ecf20Sopenharmony_ci .top = 14, 5258c2ecf20Sopenharmony_ci .width = VGA_WIDTH, 5268c2ecf20Sopenharmony_ci .height = VGA_HEIGHT, 5278c2ecf20Sopenharmony_ci }, 5288c2ecf20Sopenharmony_ci }, { 5298c2ecf20Sopenharmony_ci .name = "QVGA", 5308c2ecf20Sopenharmony_ci .com7_bit = SLCT_QVGA, 5318c2ecf20Sopenharmony_ci .sizeimage = 278 * 576, 5328c2ecf20Sopenharmony_ci .rect = { 5338c2ecf20Sopenharmony_ci .left = 252, 5348c2ecf20Sopenharmony_ci .top = 6, 5358c2ecf20Sopenharmony_ci .width = QVGA_WIDTH, 5368c2ecf20Sopenharmony_ci .height = QVGA_HEIGHT, 5378c2ecf20Sopenharmony_ci }, 5388c2ecf20Sopenharmony_ci }, 5398c2ecf20Sopenharmony_ci}; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci/* 5428c2ecf20Sopenharmony_ci * frame rate settings lists 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_cistatic const unsigned int ov772x_frame_intervals[] = { 5, 10, 15, 20, 30, 60 }; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/* 5478c2ecf20Sopenharmony_ci * general function 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci return container_of(sd, struct ov772x_priv, subdev); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int ov772x_reset(struct ov772x_priv *priv) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci int ret; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, COM7, SCCB_RESET); 5608c2ecf20Sopenharmony_ci if (ret < 0) 5618c2ecf20Sopenharmony_ci return ret; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci usleep_range(1000, 5000); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return regmap_update_bits(priv->regmap, COM2, SOFT_SLEEP_MODE, 5668c2ecf20Sopenharmony_ci SOFT_SLEEP_MODE); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci/* 5708c2ecf20Sopenharmony_ci * subdev ops 5718c2ecf20Sopenharmony_ci */ 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int ov772x_s_stream(struct v4l2_subdev *sd, int enable) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 5768c2ecf20Sopenharmony_ci struct ov772x_priv *priv = to_ov772x(sd); 5778c2ecf20Sopenharmony_ci int ret = 0; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci mutex_lock(&priv->lock); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (priv->streaming == enable) 5828c2ecf20Sopenharmony_ci goto done; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci ret = regmap_update_bits(priv->regmap, COM2, SOFT_SLEEP_MODE, 5858c2ecf20Sopenharmony_ci enable ? 0 : SOFT_SLEEP_MODE); 5868c2ecf20Sopenharmony_ci if (ret) 5878c2ecf20Sopenharmony_ci goto done; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (enable) { 5908c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "format %d, win %s\n", 5918c2ecf20Sopenharmony_ci priv->cfmt->code, priv->win->name); 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci priv->streaming = enable; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cidone: 5968c2ecf20Sopenharmony_ci mutex_unlock(&priv->lock); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return ret; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic unsigned int ov772x_select_fps(struct ov772x_priv *priv, 6028c2ecf20Sopenharmony_ci struct v4l2_fract *tpf) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci unsigned int fps = tpf->numerator ? 6058c2ecf20Sopenharmony_ci tpf->denominator / tpf->numerator : 6068c2ecf20Sopenharmony_ci tpf->denominator; 6078c2ecf20Sopenharmony_ci unsigned int best_diff; 6088c2ecf20Sopenharmony_ci unsigned int diff; 6098c2ecf20Sopenharmony_ci unsigned int idx; 6108c2ecf20Sopenharmony_ci unsigned int i; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* Approximate to the closest supported frame interval. */ 6138c2ecf20Sopenharmony_ci best_diff = ~0L; 6148c2ecf20Sopenharmony_ci for (i = 0, idx = 0; i < ARRAY_SIZE(ov772x_frame_intervals); i++) { 6158c2ecf20Sopenharmony_ci diff = abs(fps - ov772x_frame_intervals[i]); 6168c2ecf20Sopenharmony_ci if (diff < best_diff) { 6178c2ecf20Sopenharmony_ci idx = i; 6188c2ecf20Sopenharmony_ci best_diff = diff; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return ov772x_frame_intervals[idx]; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int ov772x_set_frame_rate(struct ov772x_priv *priv, 6268c2ecf20Sopenharmony_ci unsigned int fps, 6278c2ecf20Sopenharmony_ci const struct ov772x_color_format *cfmt, 6288c2ecf20Sopenharmony_ci const struct ov772x_win_size *win) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci unsigned long fin = clk_get_rate(priv->clk); 6318c2ecf20Sopenharmony_ci unsigned int best_diff; 6328c2ecf20Sopenharmony_ci unsigned int fsize; 6338c2ecf20Sopenharmony_ci unsigned int pclk; 6348c2ecf20Sopenharmony_ci unsigned int diff; 6358c2ecf20Sopenharmony_ci unsigned int i; 6368c2ecf20Sopenharmony_ci u8 clkrc = 0; 6378c2ecf20Sopenharmony_ci u8 com4 = 0; 6388c2ecf20Sopenharmony_ci int ret; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* Use image size (with blankings) to calculate desired pixel clock. */ 6418c2ecf20Sopenharmony_ci switch (cfmt->com7 & OFMT_MASK) { 6428c2ecf20Sopenharmony_ci case OFMT_BRAW: 6438c2ecf20Sopenharmony_ci fsize = win->sizeimage; 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci case OFMT_RGB: 6468c2ecf20Sopenharmony_ci case OFMT_YUV: 6478c2ecf20Sopenharmony_ci default: 6488c2ecf20Sopenharmony_ci fsize = win->sizeimage * 2; 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci pclk = fps * fsize; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * Pixel clock generation circuit is pretty simple: 6568c2ecf20Sopenharmony_ci * 6578c2ecf20Sopenharmony_ci * Fin -> [ / CLKRC_div] -> [ * PLL_mult] -> pclk 6588c2ecf20Sopenharmony_ci * 6598c2ecf20Sopenharmony_ci * Try to approximate the desired pixel clock testing all available 6608c2ecf20Sopenharmony_ci * PLL multipliers (1x, 4x, 6x, 8x) and calculate corresponding 6618c2ecf20Sopenharmony_ci * divisor with: 6628c2ecf20Sopenharmony_ci * 6638c2ecf20Sopenharmony_ci * div = PLL_mult * Fin / pclk 6648c2ecf20Sopenharmony_ci * 6658c2ecf20Sopenharmony_ci * and re-calculate the pixel clock using it: 6668c2ecf20Sopenharmony_ci * 6678c2ecf20Sopenharmony_ci * pclk = Fin * PLL_mult / CLKRC_div 6688c2ecf20Sopenharmony_ci * 6698c2ecf20Sopenharmony_ci * Choose the PLL_mult and CLKRC_div pair that gives a pixel clock 6708c2ecf20Sopenharmony_ci * closer to the desired one. 6718c2ecf20Sopenharmony_ci * 6728c2ecf20Sopenharmony_ci * The desired pixel clock is calculated using a known frame size 6738c2ecf20Sopenharmony_ci * (blanking included) and FPS. 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_ci best_diff = ~0L; 6768c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ov772x_pll); i++) { 6778c2ecf20Sopenharmony_ci unsigned int pll_mult = ov772x_pll[i].mult; 6788c2ecf20Sopenharmony_ci unsigned int pll_out = pll_mult * fin; 6798c2ecf20Sopenharmony_ci unsigned int t_pclk; 6808c2ecf20Sopenharmony_ci unsigned int div; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (pll_out < pclk) 6838c2ecf20Sopenharmony_ci continue; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST(pll_out, pclk); 6868c2ecf20Sopenharmony_ci t_pclk = DIV_ROUND_CLOSEST(fin * pll_mult, div); 6878c2ecf20Sopenharmony_ci diff = abs(pclk - t_pclk); 6888c2ecf20Sopenharmony_ci if (diff < best_diff) { 6898c2ecf20Sopenharmony_ci best_diff = diff; 6908c2ecf20Sopenharmony_ci clkrc = CLKRC_DIV(div); 6918c2ecf20Sopenharmony_ci com4 = ov772x_pll[i].com4; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, COM4, com4 | COM4_RESERVED); 6968c2ecf20Sopenharmony_ci if (ret < 0) 6978c2ecf20Sopenharmony_ci return ret; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, CLKRC, clkrc | CLKRC_RESERVED); 7008c2ecf20Sopenharmony_ci if (ret < 0) 7018c2ecf20Sopenharmony_ci return ret; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return 0; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic int ov772x_g_frame_interval(struct v4l2_subdev *sd, 7078c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *ival) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci struct ov772x_priv *priv = to_ov772x(sd); 7108c2ecf20Sopenharmony_ci struct v4l2_fract *tpf = &ival->interval; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci tpf->numerator = 1; 7138c2ecf20Sopenharmony_ci tpf->denominator = priv->fps; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci return 0; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic int ov772x_s_frame_interval(struct v4l2_subdev *sd, 7198c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval *ival) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct ov772x_priv *priv = to_ov772x(sd); 7228c2ecf20Sopenharmony_ci struct v4l2_fract *tpf = &ival->interval; 7238c2ecf20Sopenharmony_ci unsigned int fps; 7248c2ecf20Sopenharmony_ci int ret = 0; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci mutex_lock(&priv->lock); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (priv->streaming) { 7298c2ecf20Sopenharmony_ci ret = -EBUSY; 7308c2ecf20Sopenharmony_ci goto error; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci fps = ov772x_select_fps(priv, tpf); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* 7368c2ecf20Sopenharmony_ci * If the device is not powered up by the host driver do 7378c2ecf20Sopenharmony_ci * not apply any changes to H/W at this time. Instead 7388c2ecf20Sopenharmony_ci * the frame rate will be restored right after power-up. 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_ci if (priv->power_count > 0) { 7418c2ecf20Sopenharmony_ci ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win); 7428c2ecf20Sopenharmony_ci if (ret) 7438c2ecf20Sopenharmony_ci goto error; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci tpf->numerator = 1; 7478c2ecf20Sopenharmony_ci tpf->denominator = fps; 7488c2ecf20Sopenharmony_ci priv->fps = fps; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cierror: 7518c2ecf20Sopenharmony_ci mutex_unlock(&priv->lock); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return ret; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct ov772x_priv *priv = container_of(ctrl->handler, 7598c2ecf20Sopenharmony_ci struct ov772x_priv, hdl); 7608c2ecf20Sopenharmony_ci struct regmap *regmap = priv->regmap; 7618c2ecf20Sopenharmony_ci int ret = 0; 7628c2ecf20Sopenharmony_ci u8 val; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* v4l2_ctrl_lock() locks our own mutex */ 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci /* 7678c2ecf20Sopenharmony_ci * If the device is not powered up by the host driver do 7688c2ecf20Sopenharmony_ci * not apply any controls to H/W at this time. Instead 7698c2ecf20Sopenharmony_ci * the controls will be restored right after power-up. 7708c2ecf20Sopenharmony_ci */ 7718c2ecf20Sopenharmony_ci if (priv->power_count == 0) 7728c2ecf20Sopenharmony_ci return 0; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci switch (ctrl->id) { 7758c2ecf20Sopenharmony_ci case V4L2_CID_VFLIP: 7768c2ecf20Sopenharmony_ci val = ctrl->val ? VFLIP_IMG : 0x00; 7778c2ecf20Sopenharmony_ci if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) 7788c2ecf20Sopenharmony_ci val ^= VFLIP_IMG; 7798c2ecf20Sopenharmony_ci return regmap_update_bits(regmap, COM3, VFLIP_IMG, val); 7808c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 7818c2ecf20Sopenharmony_ci val = ctrl->val ? HFLIP_IMG : 0x00; 7828c2ecf20Sopenharmony_ci if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) 7838c2ecf20Sopenharmony_ci val ^= HFLIP_IMG; 7848c2ecf20Sopenharmony_ci return regmap_update_bits(regmap, COM3, HFLIP_IMG, val); 7858c2ecf20Sopenharmony_ci case V4L2_CID_BAND_STOP_FILTER: 7868c2ecf20Sopenharmony_ci if (!ctrl->val) { 7878c2ecf20Sopenharmony_ci /* Switch the filter off, it is on now */ 7888c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, BDBASE, 0xff, 0xff); 7898c2ecf20Sopenharmony_ci if (!ret) 7908c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, COM8, 7918c2ecf20Sopenharmony_ci BNDF_ON_OFF, 0); 7928c2ecf20Sopenharmony_ci } else { 7938c2ecf20Sopenharmony_ci /* Switch the filter on, set AEC low limit */ 7948c2ecf20Sopenharmony_ci val = 256 - ctrl->val; 7958c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, COM8, 7968c2ecf20Sopenharmony_ci BNDF_ON_OFF, BNDF_ON_OFF); 7978c2ecf20Sopenharmony_ci if (!ret) 7988c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, BDBASE, 7998c2ecf20Sopenharmony_ci 0xff, val); 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci return ret; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci return -EINVAL; 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 8098c2ecf20Sopenharmony_cistatic int ov772x_g_register(struct v4l2_subdev *sd, 8108c2ecf20Sopenharmony_ci struct v4l2_dbg_register *reg) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct ov772x_priv *priv = to_ov772x(sd); 8138c2ecf20Sopenharmony_ci int ret; 8148c2ecf20Sopenharmony_ci unsigned int val; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci reg->size = 1; 8178c2ecf20Sopenharmony_ci if (reg->reg > 0xff) 8188c2ecf20Sopenharmony_ci return -EINVAL; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap, reg->reg, &val); 8218c2ecf20Sopenharmony_ci if (ret < 0) 8228c2ecf20Sopenharmony_ci return ret; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci reg->val = (__u64)val; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci return 0; 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic int ov772x_s_register(struct v4l2_subdev *sd, 8308c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct ov772x_priv *priv = to_ov772x(sd); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (reg->reg > 0xff || 8358c2ecf20Sopenharmony_ci reg->val > 0xff) 8368c2ecf20Sopenharmony_ci return -EINVAL; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci return regmap_write(priv->regmap, reg->reg, reg->val); 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci#endif 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_cistatic int ov772x_power_on(struct ov772x_priv *priv) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); 8458c2ecf20Sopenharmony_ci int ret; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci if (priv->clk) { 8488c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->clk); 8498c2ecf20Sopenharmony_ci if (ret) 8508c2ecf20Sopenharmony_ci return ret; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (priv->pwdn_gpio) { 8548c2ecf20Sopenharmony_ci gpiod_set_value(priv->pwdn_gpio, 1); 8558c2ecf20Sopenharmony_ci usleep_range(500, 1000); 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* 8598c2ecf20Sopenharmony_ci * FIXME: The reset signal is connected to a shared GPIO on some 8608c2ecf20Sopenharmony_ci * platforms (namely the SuperH Migo-R). Until a framework becomes 8618c2ecf20Sopenharmony_ci * available to handle this cleanly, request the GPIO temporarily 8628c2ecf20Sopenharmony_ci * to avoid conflicts. 8638c2ecf20Sopenharmony_ci */ 8648c2ecf20Sopenharmony_ci priv->rstb_gpio = gpiod_get_optional(&client->dev, "reset", 8658c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 8668c2ecf20Sopenharmony_ci if (IS_ERR(priv->rstb_gpio)) { 8678c2ecf20Sopenharmony_ci dev_info(&client->dev, "Unable to get GPIO \"reset\""); 8688c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 8698c2ecf20Sopenharmony_ci return PTR_ERR(priv->rstb_gpio); 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (priv->rstb_gpio) { 8738c2ecf20Sopenharmony_ci gpiod_set_value(priv->rstb_gpio, 1); 8748c2ecf20Sopenharmony_ci usleep_range(500, 1000); 8758c2ecf20Sopenharmony_ci gpiod_set_value(priv->rstb_gpio, 0); 8768c2ecf20Sopenharmony_ci usleep_range(500, 1000); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci gpiod_put(priv->rstb_gpio); 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci return 0; 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic int ov772x_power_off(struct ov772x_priv *priv) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (priv->pwdn_gpio) { 8898c2ecf20Sopenharmony_ci gpiod_set_value(priv->pwdn_gpio, 0); 8908c2ecf20Sopenharmony_ci usleep_range(500, 1000); 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci return 0; 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_cistatic int ov772x_set_params(struct ov772x_priv *priv, 8978c2ecf20Sopenharmony_ci const struct ov772x_color_format *cfmt, 8988c2ecf20Sopenharmony_ci const struct ov772x_win_size *win); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic int ov772x_s_power(struct v4l2_subdev *sd, int on) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci struct ov772x_priv *priv = to_ov772x(sd); 9038c2ecf20Sopenharmony_ci int ret = 0; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci mutex_lock(&priv->lock); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* If the power count is modified from 0 to != 0 or from != 0 to 0, 9088c2ecf20Sopenharmony_ci * update the power state. 9098c2ecf20Sopenharmony_ci */ 9108c2ecf20Sopenharmony_ci if (priv->power_count == !on) { 9118c2ecf20Sopenharmony_ci if (on) { 9128c2ecf20Sopenharmony_ci ret = ov772x_power_on(priv); 9138c2ecf20Sopenharmony_ci /* 9148c2ecf20Sopenharmony_ci * Restore the format, the frame rate, and 9158c2ecf20Sopenharmony_ci * the controls 9168c2ecf20Sopenharmony_ci */ 9178c2ecf20Sopenharmony_ci if (!ret) 9188c2ecf20Sopenharmony_ci ret = ov772x_set_params(priv, priv->cfmt, 9198c2ecf20Sopenharmony_ci priv->win); 9208c2ecf20Sopenharmony_ci } else { 9218c2ecf20Sopenharmony_ci ret = ov772x_power_off(priv); 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (!ret) { 9268c2ecf20Sopenharmony_ci /* Update the power count. */ 9278c2ecf20Sopenharmony_ci priv->power_count += on ? 1 : -1; 9288c2ecf20Sopenharmony_ci WARN(priv->power_count < 0, "Unbalanced power count\n"); 9298c2ecf20Sopenharmony_ci WARN(priv->power_count > 1, "Duplicated s_power call\n"); 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci mutex_unlock(&priv->lock); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci return ret; 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci const struct ov772x_win_size *win = &ov772x_win_sizes[0]; 9408c2ecf20Sopenharmony_ci u32 best_diff = UINT_MAX; 9418c2ecf20Sopenharmony_ci unsigned int i; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ov772x_win_sizes); ++i) { 9448c2ecf20Sopenharmony_ci u32 diff = abs(width - ov772x_win_sizes[i].rect.width) 9458c2ecf20Sopenharmony_ci + abs(height - ov772x_win_sizes[i].rect.height); 9468c2ecf20Sopenharmony_ci if (diff < best_diff) { 9478c2ecf20Sopenharmony_ci best_diff = diff; 9488c2ecf20Sopenharmony_ci win = &ov772x_win_sizes[i]; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return win; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic void ov772x_select_params(const struct v4l2_mbus_framefmt *mf, 9568c2ecf20Sopenharmony_ci const struct ov772x_color_format **cfmt, 9578c2ecf20Sopenharmony_ci const struct ov772x_win_size **win) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci unsigned int i; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* Select a format. */ 9628c2ecf20Sopenharmony_ci *cfmt = &ov772x_cfmts[0]; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { 9658c2ecf20Sopenharmony_ci if (mf->code == ov772x_cfmts[i].code) { 9668c2ecf20Sopenharmony_ci *cfmt = &ov772x_cfmts[i]; 9678c2ecf20Sopenharmony_ci break; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* Select a window size. */ 9728c2ecf20Sopenharmony_ci *win = ov772x_select_win(mf->width, mf->height); 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic int ov772x_edgectrl(struct ov772x_priv *priv) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci struct regmap *regmap = priv->regmap; 9788c2ecf20Sopenharmony_ci int ret; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (!priv->info) 9818c2ecf20Sopenharmony_ci return 0; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { 9848c2ecf20Sopenharmony_ci /* 9858c2ecf20Sopenharmony_ci * Manual Edge Control Mode. 9868c2ecf20Sopenharmony_ci * 9878c2ecf20Sopenharmony_ci * Edge auto strength bit is set by default. 9888c2ecf20Sopenharmony_ci * Remove it when manual mode. 9898c2ecf20Sopenharmony_ci */ 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, DSPAUTO, EDGE_ACTRL, 0x00); 9928c2ecf20Sopenharmony_ci if (ret < 0) 9938c2ecf20Sopenharmony_ci return ret; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, EDGE_TRSHLD, 9968c2ecf20Sopenharmony_ci OV772X_EDGE_THRESHOLD_MASK, 9978c2ecf20Sopenharmony_ci priv->info->edgectrl.threshold); 9988c2ecf20Sopenharmony_ci if (ret < 0) 9998c2ecf20Sopenharmony_ci return ret; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, EDGE_STRNGT, 10028c2ecf20Sopenharmony_ci OV772X_EDGE_STRENGTH_MASK, 10038c2ecf20Sopenharmony_ci priv->info->edgectrl.strength); 10048c2ecf20Sopenharmony_ci if (ret < 0) 10058c2ecf20Sopenharmony_ci return ret; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { 10088c2ecf20Sopenharmony_ci /* 10098c2ecf20Sopenharmony_ci * Auto Edge Control Mode. 10108c2ecf20Sopenharmony_ci * 10118c2ecf20Sopenharmony_ci * Set upper and lower limit. 10128c2ecf20Sopenharmony_ci */ 10138c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, EDGE_UPPER, 10148c2ecf20Sopenharmony_ci OV772X_EDGE_UPPER_MASK, 10158c2ecf20Sopenharmony_ci priv->info->edgectrl.upper); 10168c2ecf20Sopenharmony_ci if (ret < 0) 10178c2ecf20Sopenharmony_ci return ret; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, EDGE_LOWER, 10208c2ecf20Sopenharmony_ci OV772X_EDGE_LOWER_MASK, 10218c2ecf20Sopenharmony_ci priv->info->edgectrl.lower); 10228c2ecf20Sopenharmony_ci if (ret < 0) 10238c2ecf20Sopenharmony_ci return ret; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci return 0; 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic int ov772x_set_params(struct ov772x_priv *priv, 10308c2ecf20Sopenharmony_ci const struct ov772x_color_format *cfmt, 10318c2ecf20Sopenharmony_ci const struct ov772x_win_size *win) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci int ret; 10348c2ecf20Sopenharmony_ci u8 val; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* Reset hardware. */ 10378c2ecf20Sopenharmony_ci ov772x_reset(priv); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* Edge Ctrl. */ 10408c2ecf20Sopenharmony_ci ret = ov772x_edgectrl(priv); 10418c2ecf20Sopenharmony_ci if (ret < 0) 10428c2ecf20Sopenharmony_ci return ret; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci /* Format and window size. */ 10458c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, HSTART, win->rect.left >> 2); 10468c2ecf20Sopenharmony_ci if (ret < 0) 10478c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 10488c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, HSIZE, win->rect.width >> 2); 10498c2ecf20Sopenharmony_ci if (ret < 0) 10508c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 10518c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, VSTART, win->rect.top >> 1); 10528c2ecf20Sopenharmony_ci if (ret < 0) 10538c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 10548c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, VSIZE, win->rect.height >> 1); 10558c2ecf20Sopenharmony_ci if (ret < 0) 10568c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 10578c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, HOUTSIZE, win->rect.width >> 2); 10588c2ecf20Sopenharmony_ci if (ret < 0) 10598c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 10608c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, VOUTSIZE, win->rect.height >> 1); 10618c2ecf20Sopenharmony_ci if (ret < 0) 10628c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 10638c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, HREF, 10648c2ecf20Sopenharmony_ci ((win->rect.top & 1) << HREF_VSTART_SHIFT) | 10658c2ecf20Sopenharmony_ci ((win->rect.left & 3) << HREF_HSTART_SHIFT) | 10668c2ecf20Sopenharmony_ci ((win->rect.height & 1) << HREF_VSIZE_SHIFT) | 10678c2ecf20Sopenharmony_ci ((win->rect.width & 3) << HREF_HSIZE_SHIFT)); 10688c2ecf20Sopenharmony_ci if (ret < 0) 10698c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 10708c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, EXHCH, 10718c2ecf20Sopenharmony_ci ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) | 10728c2ecf20Sopenharmony_ci ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT)); 10738c2ecf20Sopenharmony_ci if (ret < 0) 10748c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* Set DSP_CTRL3. */ 10778c2ecf20Sopenharmony_ci val = cfmt->dsp3; 10788c2ecf20Sopenharmony_ci if (val) { 10798c2ecf20Sopenharmony_ci ret = regmap_update_bits(priv->regmap, DSP_CTRL3, UV_MASK, val); 10808c2ecf20Sopenharmony_ci if (ret < 0) 10818c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci /* DSP_CTRL4: AEC reference point and DSP output format. */ 10858c2ecf20Sopenharmony_ci if (cfmt->dsp4) { 10868c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, DSP_CTRL4, cfmt->dsp4); 10878c2ecf20Sopenharmony_ci if (ret < 0) 10888c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* Set COM3. */ 10928c2ecf20Sopenharmony_ci val = cfmt->com3; 10938c2ecf20Sopenharmony_ci if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) 10948c2ecf20Sopenharmony_ci val |= VFLIP_IMG; 10958c2ecf20Sopenharmony_ci if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) 10968c2ecf20Sopenharmony_ci val |= HFLIP_IMG; 10978c2ecf20Sopenharmony_ci if (priv->vflip_ctrl->val) 10988c2ecf20Sopenharmony_ci val ^= VFLIP_IMG; 10998c2ecf20Sopenharmony_ci if (priv->hflip_ctrl->val) 11008c2ecf20Sopenharmony_ci val ^= HFLIP_IMG; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci ret = regmap_update_bits(priv->regmap, COM3, SWAP_MASK | IMG_MASK, val); 11038c2ecf20Sopenharmony_ci if (ret < 0) 11048c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci /* COM7: Sensor resolution and output format control. */ 11078c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, COM7, win->com7_bit | cfmt->com7); 11088c2ecf20Sopenharmony_ci if (ret < 0) 11098c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci /* COM4, CLKRC: Set pixel clock and framerate. */ 11128c2ecf20Sopenharmony_ci ret = ov772x_set_frame_rate(priv, priv->fps, cfmt, win); 11138c2ecf20Sopenharmony_ci if (ret < 0) 11148c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci /* Set COM8. */ 11178c2ecf20Sopenharmony_ci if (priv->band_filter_ctrl->val) { 11188c2ecf20Sopenharmony_ci unsigned short band_filter = priv->band_filter_ctrl->val; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci ret = regmap_update_bits(priv->regmap, COM8, 11218c2ecf20Sopenharmony_ci BNDF_ON_OFF, BNDF_ON_OFF); 11228c2ecf20Sopenharmony_ci if (!ret) 11238c2ecf20Sopenharmony_ci ret = regmap_update_bits(priv->regmap, BDBASE, 11248c2ecf20Sopenharmony_ci 0xff, 256 - band_filter); 11258c2ecf20Sopenharmony_ci if (ret < 0) 11268c2ecf20Sopenharmony_ci goto ov772x_set_fmt_error; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci return ret; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ciov772x_set_fmt_error: 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci ov772x_reset(priv); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci return ret; 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_cistatic int ov772x_get_selection(struct v4l2_subdev *sd, 11398c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 11408c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci struct ov772x_priv *priv = to_ov772x(sd); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) 11458c2ecf20Sopenharmony_ci return -EINVAL; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci sel->r.left = 0; 11488c2ecf20Sopenharmony_ci sel->r.top = 0; 11498c2ecf20Sopenharmony_ci switch (sel->target) { 11508c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 11518c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 11528c2ecf20Sopenharmony_ci sel->r.width = priv->win->rect.width; 11538c2ecf20Sopenharmony_ci sel->r.height = priv->win->rect.height; 11548c2ecf20Sopenharmony_ci return 0; 11558c2ecf20Sopenharmony_ci default: 11568c2ecf20Sopenharmony_ci return -EINVAL; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci} 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic int ov772x_get_fmt(struct v4l2_subdev *sd, 11618c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 11628c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &format->format; 11658c2ecf20Sopenharmony_ci struct ov772x_priv *priv = to_ov772x(sd); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci if (format->pad) 11688c2ecf20Sopenharmony_ci return -EINVAL; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci mf->width = priv->win->rect.width; 11718c2ecf20Sopenharmony_ci mf->height = priv->win->rect.height; 11728c2ecf20Sopenharmony_ci mf->code = priv->cfmt->code; 11738c2ecf20Sopenharmony_ci mf->colorspace = priv->cfmt->colorspace; 11748c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci return 0; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistatic int ov772x_set_fmt(struct v4l2_subdev *sd, 11808c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 11818c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 11828c2ecf20Sopenharmony_ci{ 11838c2ecf20Sopenharmony_ci struct ov772x_priv *priv = to_ov772x(sd); 11848c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &format->format; 11858c2ecf20Sopenharmony_ci const struct ov772x_color_format *cfmt; 11868c2ecf20Sopenharmony_ci const struct ov772x_win_size *win; 11878c2ecf20Sopenharmony_ci int ret = 0; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (format->pad) 11908c2ecf20Sopenharmony_ci return -EINVAL; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci ov772x_select_params(mf, &cfmt, &win); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci mf->code = cfmt->code; 11958c2ecf20Sopenharmony_ci mf->width = win->rect.width; 11968c2ecf20Sopenharmony_ci mf->height = win->rect.height; 11978c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 11988c2ecf20Sopenharmony_ci mf->colorspace = cfmt->colorspace; 11998c2ecf20Sopenharmony_ci mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 12008c2ecf20Sopenharmony_ci mf->quantization = V4L2_QUANTIZATION_DEFAULT; 12018c2ecf20Sopenharmony_ci mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 12048c2ecf20Sopenharmony_ci cfg->try_fmt = *mf; 12058c2ecf20Sopenharmony_ci return 0; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci mutex_lock(&priv->lock); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (priv->streaming) { 12118c2ecf20Sopenharmony_ci ret = -EBUSY; 12128c2ecf20Sopenharmony_ci goto error; 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* 12168c2ecf20Sopenharmony_ci * If the device is not powered up by the host driver do 12178c2ecf20Sopenharmony_ci * not apply any changes to H/W at this time. Instead 12188c2ecf20Sopenharmony_ci * the format will be restored right after power-up. 12198c2ecf20Sopenharmony_ci */ 12208c2ecf20Sopenharmony_ci if (priv->power_count > 0) { 12218c2ecf20Sopenharmony_ci ret = ov772x_set_params(priv, cfmt, win); 12228c2ecf20Sopenharmony_ci if (ret < 0) 12238c2ecf20Sopenharmony_ci goto error; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci priv->win = win; 12268c2ecf20Sopenharmony_ci priv->cfmt = cfmt; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_cierror: 12298c2ecf20Sopenharmony_ci mutex_unlock(&priv->lock); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci return ret; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic int ov772x_video_probe(struct ov772x_priv *priv) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); 12378c2ecf20Sopenharmony_ci int pid, ver, midh, midl; 12388c2ecf20Sopenharmony_ci const char *devname; 12398c2ecf20Sopenharmony_ci int ret; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci ret = ov772x_power_on(priv); 12428c2ecf20Sopenharmony_ci if (ret < 0) 12438c2ecf20Sopenharmony_ci return ret; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci /* Check and show product ID and manufacturer ID. */ 12468c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap, PID, &pid); 12478c2ecf20Sopenharmony_ci if (ret < 0) 12488c2ecf20Sopenharmony_ci return ret; 12498c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap, VER, &ver); 12508c2ecf20Sopenharmony_ci if (ret < 0) 12518c2ecf20Sopenharmony_ci return ret; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci switch (VERSION(pid, ver)) { 12548c2ecf20Sopenharmony_ci case OV7720: 12558c2ecf20Sopenharmony_ci devname = "ov7720"; 12568c2ecf20Sopenharmony_ci break; 12578c2ecf20Sopenharmony_ci case OV7725: 12588c2ecf20Sopenharmony_ci devname = "ov7725"; 12598c2ecf20Sopenharmony_ci break; 12608c2ecf20Sopenharmony_ci default: 12618c2ecf20Sopenharmony_ci dev_err(&client->dev, 12628c2ecf20Sopenharmony_ci "Product ID error %x:%x\n", pid, ver); 12638c2ecf20Sopenharmony_ci ret = -ENODEV; 12648c2ecf20Sopenharmony_ci goto done; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap, MIDH, &midh); 12688c2ecf20Sopenharmony_ci if (ret < 0) 12698c2ecf20Sopenharmony_ci return ret; 12708c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap, MIDL, &midl); 12718c2ecf20Sopenharmony_ci if (ret < 0) 12728c2ecf20Sopenharmony_ci return ret; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci dev_info(&client->dev, 12758c2ecf20Sopenharmony_ci "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", 12768c2ecf20Sopenharmony_ci devname, pid, ver, midh, midl); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_setup(&priv->hdl); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_cidone: 12818c2ecf20Sopenharmony_ci ov772x_power_off(priv); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci return ret; 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops ov772x_ctrl_ops = { 12878c2ecf20Sopenharmony_ci .s_ctrl = ov772x_s_ctrl, 12888c2ecf20Sopenharmony_ci}; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { 12918c2ecf20Sopenharmony_ci .log_status = v4l2_ctrl_subdev_log_status, 12928c2ecf20Sopenharmony_ci .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 12938c2ecf20Sopenharmony_ci .unsubscribe_event = v4l2_event_subdev_unsubscribe, 12948c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 12958c2ecf20Sopenharmony_ci .g_register = ov772x_g_register, 12968c2ecf20Sopenharmony_ci .s_register = ov772x_s_register, 12978c2ecf20Sopenharmony_ci#endif 12988c2ecf20Sopenharmony_ci .s_power = ov772x_s_power, 12998c2ecf20Sopenharmony_ci}; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_cistatic int ov772x_enum_frame_interval(struct v4l2_subdev *sd, 13028c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 13038c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval_enum *fie) 13048c2ecf20Sopenharmony_ci{ 13058c2ecf20Sopenharmony_ci if (fie->pad || fie->index >= ARRAY_SIZE(ov772x_frame_intervals)) 13068c2ecf20Sopenharmony_ci return -EINVAL; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci if (fie->width != VGA_WIDTH && fie->width != QVGA_WIDTH) 13098c2ecf20Sopenharmony_ci return -EINVAL; 13108c2ecf20Sopenharmony_ci if (fie->height != VGA_HEIGHT && fie->height != QVGA_HEIGHT) 13118c2ecf20Sopenharmony_ci return -EINVAL; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci fie->interval.numerator = 1; 13148c2ecf20Sopenharmony_ci fie->interval.denominator = ov772x_frame_intervals[fie->index]; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci return 0; 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic int ov772x_enum_mbus_code(struct v4l2_subdev *sd, 13208c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 13218c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts)) 13248c2ecf20Sopenharmony_ci return -EINVAL; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci code->code = ov772x_cfmts[code->index].code; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci return 0; 13298c2ecf20Sopenharmony_ci} 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { 13328c2ecf20Sopenharmony_ci .s_stream = ov772x_s_stream, 13338c2ecf20Sopenharmony_ci .s_frame_interval = ov772x_s_frame_interval, 13348c2ecf20Sopenharmony_ci .g_frame_interval = ov772x_g_frame_interval, 13358c2ecf20Sopenharmony_ci}; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { 13388c2ecf20Sopenharmony_ci .enum_frame_interval = ov772x_enum_frame_interval, 13398c2ecf20Sopenharmony_ci .enum_mbus_code = ov772x_enum_mbus_code, 13408c2ecf20Sopenharmony_ci .get_selection = ov772x_get_selection, 13418c2ecf20Sopenharmony_ci .get_fmt = ov772x_get_fmt, 13428c2ecf20Sopenharmony_ci .set_fmt = ov772x_set_fmt, 13438c2ecf20Sopenharmony_ci}; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops ov772x_subdev_ops = { 13468c2ecf20Sopenharmony_ci .core = &ov772x_subdev_core_ops, 13478c2ecf20Sopenharmony_ci .video = &ov772x_subdev_video_ops, 13488c2ecf20Sopenharmony_ci .pad = &ov772x_subdev_pad_ops, 13498c2ecf20Sopenharmony_ci}; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci/* 13528c2ecf20Sopenharmony_ci * i2c_driver function 13538c2ecf20Sopenharmony_ci */ 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_cistatic int ov772x_probe(struct i2c_client *client) 13568c2ecf20Sopenharmony_ci{ 13578c2ecf20Sopenharmony_ci struct ov772x_priv *priv; 13588c2ecf20Sopenharmony_ci int ret; 13598c2ecf20Sopenharmony_ci static const struct regmap_config ov772x_regmap_config = { 13608c2ecf20Sopenharmony_ci .reg_bits = 8, 13618c2ecf20Sopenharmony_ci .val_bits = 8, 13628c2ecf20Sopenharmony_ci .max_register = DSPAUTO, 13638c2ecf20Sopenharmony_ci }; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci if (!client->dev.of_node && !client->dev.platform_data) { 13668c2ecf20Sopenharmony_ci dev_err(&client->dev, 13678c2ecf20Sopenharmony_ci "Missing ov772x platform data for non-DT device\n"); 13688c2ecf20Sopenharmony_ci return -EINVAL; 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); 13728c2ecf20Sopenharmony_ci if (!priv) 13738c2ecf20Sopenharmony_ci return -ENOMEM; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci priv->regmap = devm_regmap_init_sccb(client, &ov772x_regmap_config); 13768c2ecf20Sopenharmony_ci if (IS_ERR(priv->regmap)) { 13778c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to allocate register map\n"); 13788c2ecf20Sopenharmony_ci return PTR_ERR(priv->regmap); 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci priv->info = client->dev.platform_data; 13828c2ecf20Sopenharmony_ci mutex_init(&priv->lock); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); 13858c2ecf20Sopenharmony_ci priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 13868c2ecf20Sopenharmony_ci V4L2_SUBDEV_FL_HAS_EVENTS; 13878c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&priv->hdl, 3); 13888c2ecf20Sopenharmony_ci /* Use our mutex for the controls */ 13898c2ecf20Sopenharmony_ci priv->hdl.lock = &priv->lock; 13908c2ecf20Sopenharmony_ci priv->vflip_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, 13918c2ecf20Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 13928c2ecf20Sopenharmony_ci priv->hflip_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, 13938c2ecf20Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 13948c2ecf20Sopenharmony_ci priv->band_filter_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, 13958c2ecf20Sopenharmony_ci V4L2_CID_BAND_STOP_FILTER, 13968c2ecf20Sopenharmony_ci 0, 256, 1, 0); 13978c2ecf20Sopenharmony_ci priv->subdev.ctrl_handler = &priv->hdl; 13988c2ecf20Sopenharmony_ci if (priv->hdl.error) { 13998c2ecf20Sopenharmony_ci ret = priv->hdl.error; 14008c2ecf20Sopenharmony_ci goto error_ctrl_free; 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci priv->clk = clk_get(&client->dev, NULL); 14048c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) { 14058c2ecf20Sopenharmony_ci dev_err(&client->dev, "Unable to get xclk clock\n"); 14068c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->clk); 14078c2ecf20Sopenharmony_ci goto error_ctrl_free; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci priv->pwdn_gpio = gpiod_get_optional(&client->dev, "powerdown", 14118c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 14128c2ecf20Sopenharmony_ci if (IS_ERR(priv->pwdn_gpio)) { 14138c2ecf20Sopenharmony_ci dev_info(&client->dev, "Unable to get GPIO \"powerdown\""); 14148c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->pwdn_gpio); 14158c2ecf20Sopenharmony_ci goto error_clk_put; 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci ret = ov772x_video_probe(priv); 14198c2ecf20Sopenharmony_ci if (ret < 0) 14208c2ecf20Sopenharmony_ci goto error_gpio_put; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 14238c2ecf20Sopenharmony_ci priv->pad.flags = MEDIA_PAD_FL_SOURCE; 14248c2ecf20Sopenharmony_ci priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; 14258c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); 14268c2ecf20Sopenharmony_ci if (ret < 0) 14278c2ecf20Sopenharmony_ci goto error_gpio_put; 14288c2ecf20Sopenharmony_ci#endif 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci priv->cfmt = &ov772x_cfmts[0]; 14318c2ecf20Sopenharmony_ci priv->win = &ov772x_win_sizes[0]; 14328c2ecf20Sopenharmony_ci priv->fps = 15; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(&priv->subdev); 14358c2ecf20Sopenharmony_ci if (ret) 14368c2ecf20Sopenharmony_ci goto error_entity_cleanup; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci return 0; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_cierror_entity_cleanup: 14418c2ecf20Sopenharmony_ci media_entity_cleanup(&priv->subdev.entity); 14428c2ecf20Sopenharmony_cierror_gpio_put: 14438c2ecf20Sopenharmony_ci if (priv->pwdn_gpio) 14448c2ecf20Sopenharmony_ci gpiod_put(priv->pwdn_gpio); 14458c2ecf20Sopenharmony_cierror_clk_put: 14468c2ecf20Sopenharmony_ci clk_put(priv->clk); 14478c2ecf20Sopenharmony_cierror_ctrl_free: 14488c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&priv->hdl); 14498c2ecf20Sopenharmony_ci mutex_destroy(&priv->lock); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci return ret; 14528c2ecf20Sopenharmony_ci} 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_cistatic int ov772x_remove(struct i2c_client *client) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client)); 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci media_entity_cleanup(&priv->subdev.entity); 14598c2ecf20Sopenharmony_ci clk_put(priv->clk); 14608c2ecf20Sopenharmony_ci if (priv->pwdn_gpio) 14618c2ecf20Sopenharmony_ci gpiod_put(priv->pwdn_gpio); 14628c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(&priv->subdev); 14638c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&priv->hdl); 14648c2ecf20Sopenharmony_ci mutex_destroy(&priv->lock); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci return 0; 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_cistatic const struct i2c_device_id ov772x_id[] = { 14708c2ecf20Sopenharmony_ci { "ov772x", 0 }, 14718c2ecf20Sopenharmony_ci { } 14728c2ecf20Sopenharmony_ci}; 14738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ov772x_id); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_cistatic const struct of_device_id ov772x_of_match[] = { 14768c2ecf20Sopenharmony_ci { .compatible = "ovti,ov7725", }, 14778c2ecf20Sopenharmony_ci { .compatible = "ovti,ov7720", }, 14788c2ecf20Sopenharmony_ci { /* sentinel */ }, 14798c2ecf20Sopenharmony_ci}; 14808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ov772x_of_match); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_cistatic struct i2c_driver ov772x_i2c_driver = { 14838c2ecf20Sopenharmony_ci .driver = { 14848c2ecf20Sopenharmony_ci .name = "ov772x", 14858c2ecf20Sopenharmony_ci .of_match_table = ov772x_of_match, 14868c2ecf20Sopenharmony_ci }, 14878c2ecf20Sopenharmony_ci .probe_new = ov772x_probe, 14888c2ecf20Sopenharmony_ci .remove = ov772x_remove, 14898c2ecf20Sopenharmony_ci .id_table = ov772x_id, 14908c2ecf20Sopenharmony_ci}; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_cimodule_i2c_driver(ov772x_i2c_driver); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("V4L2 driver for OV772x image sensor"); 14958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto"); 14968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1497