1762c2812SJacopo Mondi // SPDX-License-Identifier: GPL-2.0 21112babdSJacopo Mondi /* 31112babdSJacopo Mondi * ov772x Camera Driver 41112babdSJacopo Mondi * 5762c2812SJacopo Mondi * Copyright (C) 2017 Jacopo Mondi <jacopo+renesas@jmondi.org> 6762c2812SJacopo Mondi * 71112babdSJacopo Mondi * Copyright (C) 2008 Renesas Solutions Corp. 81112babdSJacopo Mondi * Kuninori Morimoto <morimoto.kuninori@renesas.com> 91112babdSJacopo Mondi * 101112babdSJacopo Mondi * Based on ov7670 and soc_camera_platform driver, 111112babdSJacopo Mondi * 121112babdSJacopo Mondi * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> 131112babdSJacopo Mondi * Copyright (C) 2008 Magnus Damm 141112babdSJacopo Mondi * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> 151112babdSJacopo Mondi */ 161112babdSJacopo Mondi 17762c2812SJacopo Mondi #include <linux/clk.h> 18762c2812SJacopo Mondi #include <linux/delay.h> 19762c2812SJacopo Mondi #include <linux/gpio/consumer.h> 20762c2812SJacopo Mondi #include <linux/i2c.h> 211112babdSJacopo Mondi #include <linux/init.h> 221112babdSJacopo Mondi #include <linux/kernel.h> 231112babdSJacopo Mondi #include <linux/module.h> 241112babdSJacopo Mondi #include <linux/slab.h> 251112babdSJacopo Mondi #include <linux/v4l2-mediabus.h> 261112babdSJacopo Mondi #include <linux/videodev2.h> 271112babdSJacopo Mondi 281112babdSJacopo Mondi #include <media/i2c/ov772x.h> 29762c2812SJacopo Mondi 301112babdSJacopo Mondi #include <media/v4l2-ctrls.h> 31762c2812SJacopo Mondi #include <media/v4l2-device.h> 321112babdSJacopo Mondi #include <media/v4l2-image-sizes.h> 33762c2812SJacopo Mondi #include <media/v4l2-subdev.h> 341112babdSJacopo Mondi 351112babdSJacopo Mondi /* 361112babdSJacopo Mondi * register offset 371112babdSJacopo Mondi */ 381112babdSJacopo Mondi #define GAIN 0x00 /* AGC - Gain control gain setting */ 391112babdSJacopo Mondi #define BLUE 0x01 /* AWB - Blue channel gain setting */ 401112babdSJacopo Mondi #define RED 0x02 /* AWB - Red channel gain setting */ 411112babdSJacopo Mondi #define GREEN 0x03 /* AWB - Green channel gain setting */ 421112babdSJacopo Mondi #define COM1 0x04 /* Common control 1 */ 431112babdSJacopo Mondi #define BAVG 0x05 /* U/B Average Level */ 441112babdSJacopo Mondi #define GAVG 0x06 /* Y/Gb Average Level */ 451112babdSJacopo Mondi #define RAVG 0x07 /* V/R Average Level */ 461112babdSJacopo Mondi #define AECH 0x08 /* Exposure Value - AEC MSBs */ 471112babdSJacopo Mondi #define COM2 0x09 /* Common control 2 */ 481112babdSJacopo Mondi #define PID 0x0A /* Product ID Number MSB */ 491112babdSJacopo Mondi #define VER 0x0B /* Product ID Number LSB */ 501112babdSJacopo Mondi #define COM3 0x0C /* Common control 3 */ 511112babdSJacopo Mondi #define COM4 0x0D /* Common control 4 */ 521112babdSJacopo Mondi #define COM5 0x0E /* Common control 5 */ 531112babdSJacopo Mondi #define COM6 0x0F /* Common control 6 */ 541112babdSJacopo Mondi #define AEC 0x10 /* Exposure Value */ 551112babdSJacopo Mondi #define CLKRC 0x11 /* Internal clock */ 561112babdSJacopo Mondi #define COM7 0x12 /* Common control 7 */ 571112babdSJacopo Mondi #define COM8 0x13 /* Common control 8 */ 581112babdSJacopo Mondi #define COM9 0x14 /* Common control 9 */ 591112babdSJacopo Mondi #define COM10 0x15 /* Common control 10 */ 601112babdSJacopo Mondi #define REG16 0x16 /* Register 16 */ 611112babdSJacopo Mondi #define HSTART 0x17 /* Horizontal sensor size */ 621112babdSJacopo Mondi #define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */ 631112babdSJacopo Mondi #define VSTART 0x19 /* Vertical frame (row) start high 8-bit */ 641112babdSJacopo Mondi #define VSIZE 0x1A /* Vertical sensor size */ 651112babdSJacopo Mondi #define PSHFT 0x1B /* Data format - pixel delay select */ 661112babdSJacopo Mondi #define MIDH 0x1C /* Manufacturer ID byte - high */ 671112babdSJacopo Mondi #define MIDL 0x1D /* Manufacturer ID byte - low */ 681112babdSJacopo Mondi #define LAEC 0x1F /* Fine AEC value */ 691112babdSJacopo Mondi #define COM11 0x20 /* Common control 11 */ 701112babdSJacopo Mondi #define BDBASE 0x22 /* Banding filter Minimum AEC value */ 711112babdSJacopo Mondi #define DBSTEP 0x23 /* Banding filter Maximum Setp */ 721112babdSJacopo Mondi #define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ 731112babdSJacopo Mondi #define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ 741112babdSJacopo Mondi #define VPT 0x26 /* AGC/AEC Fast mode operating region */ 751112babdSJacopo Mondi #define REG28 0x28 /* Register 28 */ 761112babdSJacopo Mondi #define HOUTSIZE 0x29 /* Horizontal data output size MSBs */ 771112babdSJacopo Mondi #define EXHCH 0x2A /* Dummy pixel insert MSB */ 781112babdSJacopo Mondi #define EXHCL 0x2B /* Dummy pixel insert LSB */ 791112babdSJacopo Mondi #define VOUTSIZE 0x2C /* Vertical data output size MSBs */ 801112babdSJacopo Mondi #define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ 811112babdSJacopo Mondi #define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */ 821112babdSJacopo Mondi #define YAVE 0x2F /* Y/G Channel Average value */ 831112babdSJacopo Mondi #define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */ 841112babdSJacopo Mondi #define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */ 851112babdSJacopo Mondi #define HREF 0x32 /* Image start and size control */ 861112babdSJacopo Mondi #define DM_LNL 0x33 /* Dummy line low 8 bits */ 871112babdSJacopo Mondi #define DM_LNH 0x34 /* Dummy line high 8 bits */ 881112babdSJacopo Mondi #define ADOFF_B 0x35 /* AD offset compensation value for B channel */ 891112babdSJacopo Mondi #define ADOFF_R 0x36 /* AD offset compensation value for R channel */ 901112babdSJacopo Mondi #define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */ 911112babdSJacopo Mondi #define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */ 921112babdSJacopo Mondi #define OFF_B 0x39 /* Analog process B channel offset value */ 931112babdSJacopo Mondi #define OFF_R 0x3A /* Analog process R channel offset value */ 941112babdSJacopo Mondi #define OFF_GB 0x3B /* Analog process Gb channel offset value */ 951112babdSJacopo Mondi #define OFF_GR 0x3C /* Analog process Gr channel offset value */ 961112babdSJacopo Mondi #define COM12 0x3D /* Common control 12 */ 971112babdSJacopo Mondi #define COM13 0x3E /* Common control 13 */ 981112babdSJacopo Mondi #define COM14 0x3F /* Common control 14 */ 991112babdSJacopo Mondi #define COM15 0x40 /* Common control 15*/ 1001112babdSJacopo Mondi #define COM16 0x41 /* Common control 16 */ 1011112babdSJacopo Mondi #define TGT_B 0x42 /* BLC blue channel target value */ 1021112babdSJacopo Mondi #define TGT_R 0x43 /* BLC red channel target value */ 1031112babdSJacopo Mondi #define TGT_GB 0x44 /* BLC Gb channel target value */ 1041112babdSJacopo Mondi #define TGT_GR 0x45 /* BLC Gr channel target value */ 1051112babdSJacopo Mondi /* for ov7720 */ 1061112babdSJacopo Mondi #define LCC0 0x46 /* Lens correction control 0 */ 1071112babdSJacopo Mondi #define LCC1 0x47 /* Lens correction option 1 - X coordinate */ 1081112babdSJacopo Mondi #define LCC2 0x48 /* Lens correction option 2 - Y coordinate */ 1091112babdSJacopo Mondi #define LCC3 0x49 /* Lens correction option 3 */ 1101112babdSJacopo Mondi #define LCC4 0x4A /* Lens correction option 4 - radius of the circular */ 1111112babdSJacopo Mondi #define LCC5 0x4B /* Lens correction option 5 */ 1121112babdSJacopo Mondi #define LCC6 0x4C /* Lens correction option 6 */ 1131112babdSJacopo Mondi /* for ov7725 */ 1141112babdSJacopo Mondi #define LC_CTR 0x46 /* Lens correction control */ 1151112babdSJacopo Mondi #define LC_XC 0x47 /* X coordinate of lens correction center relative */ 1161112babdSJacopo Mondi #define LC_YC 0x48 /* Y coordinate of lens correction center relative */ 1171112babdSJacopo Mondi #define LC_COEF 0x49 /* Lens correction coefficient */ 1181112babdSJacopo Mondi #define LC_RADI 0x4A /* Lens correction radius */ 1191112babdSJacopo Mondi #define LC_COEFB 0x4B /* Lens B channel compensation coefficient */ 1201112babdSJacopo Mondi #define LC_COEFR 0x4C /* Lens R channel compensation coefficient */ 1211112babdSJacopo Mondi 1221112babdSJacopo Mondi #define FIXGAIN 0x4D /* Analog fix gain amplifer */ 1231112babdSJacopo Mondi #define AREF0 0x4E /* Sensor reference control */ 1241112babdSJacopo Mondi #define AREF1 0x4F /* Sensor reference current control */ 1251112babdSJacopo Mondi #define AREF2 0x50 /* Analog reference control */ 1261112babdSJacopo Mondi #define AREF3 0x51 /* ADC reference control */ 1271112babdSJacopo Mondi #define AREF4 0x52 /* ADC reference control */ 1281112babdSJacopo Mondi #define AREF5 0x53 /* ADC reference control */ 1291112babdSJacopo Mondi #define AREF6 0x54 /* Analog reference control */ 1301112babdSJacopo Mondi #define AREF7 0x55 /* Analog reference control */ 1311112babdSJacopo Mondi #define UFIX 0x60 /* U channel fixed value output */ 1321112babdSJacopo Mondi #define VFIX 0x61 /* V channel fixed value output */ 1331112babdSJacopo Mondi #define AWBB_BLK 0x62 /* AWB option for advanced AWB */ 1341112babdSJacopo Mondi #define AWB_CTRL0 0x63 /* AWB control byte 0 */ 1351112babdSJacopo Mondi #define DSP_CTRL1 0x64 /* DSP control byte 1 */ 1361112babdSJacopo Mondi #define DSP_CTRL2 0x65 /* DSP control byte 2 */ 1371112babdSJacopo Mondi #define DSP_CTRL3 0x66 /* DSP control byte 3 */ 1381112babdSJacopo Mondi #define DSP_CTRL4 0x67 /* DSP control byte 4 */ 1391112babdSJacopo Mondi #define AWB_BIAS 0x68 /* AWB BLC level clip */ 1401112babdSJacopo Mondi #define AWB_CTRL1 0x69 /* AWB control 1 */ 1411112babdSJacopo Mondi #define AWB_CTRL2 0x6A /* AWB control 2 */ 1421112babdSJacopo Mondi #define AWB_CTRL3 0x6B /* AWB control 3 */ 1431112babdSJacopo Mondi #define AWB_CTRL4 0x6C /* AWB control 4 */ 1441112babdSJacopo Mondi #define AWB_CTRL5 0x6D /* AWB control 5 */ 1451112babdSJacopo Mondi #define AWB_CTRL6 0x6E /* AWB control 6 */ 1461112babdSJacopo Mondi #define AWB_CTRL7 0x6F /* AWB control 7 */ 1471112babdSJacopo Mondi #define AWB_CTRL8 0x70 /* AWB control 8 */ 1481112babdSJacopo Mondi #define AWB_CTRL9 0x71 /* AWB control 9 */ 1491112babdSJacopo Mondi #define AWB_CTRL10 0x72 /* AWB control 10 */ 1501112babdSJacopo Mondi #define AWB_CTRL11 0x73 /* AWB control 11 */ 1511112babdSJacopo Mondi #define AWB_CTRL12 0x74 /* AWB control 12 */ 1521112babdSJacopo Mondi #define AWB_CTRL13 0x75 /* AWB control 13 */ 1531112babdSJacopo Mondi #define AWB_CTRL14 0x76 /* AWB control 14 */ 1541112babdSJacopo Mondi #define AWB_CTRL15 0x77 /* AWB control 15 */ 1551112babdSJacopo Mondi #define AWB_CTRL16 0x78 /* AWB control 16 */ 1561112babdSJacopo Mondi #define AWB_CTRL17 0x79 /* AWB control 17 */ 1571112babdSJacopo Mondi #define AWB_CTRL18 0x7A /* AWB control 18 */ 1581112babdSJacopo Mondi #define AWB_CTRL19 0x7B /* AWB control 19 */ 1591112babdSJacopo Mondi #define AWB_CTRL20 0x7C /* AWB control 20 */ 1601112babdSJacopo Mondi #define AWB_CTRL21 0x7D /* AWB control 21 */ 1611112babdSJacopo Mondi #define GAM1 0x7E /* Gamma Curve 1st segment input end point */ 1621112babdSJacopo Mondi #define GAM2 0x7F /* Gamma Curve 2nd segment input end point */ 1631112babdSJacopo Mondi #define GAM3 0x80 /* Gamma Curve 3rd segment input end point */ 1641112babdSJacopo Mondi #define GAM4 0x81 /* Gamma Curve 4th segment input end point */ 1651112babdSJacopo Mondi #define GAM5 0x82 /* Gamma Curve 5th segment input end point */ 1661112babdSJacopo Mondi #define GAM6 0x83 /* Gamma Curve 6th segment input end point */ 1671112babdSJacopo Mondi #define GAM7 0x84 /* Gamma Curve 7th segment input end point */ 1681112babdSJacopo Mondi #define GAM8 0x85 /* Gamma Curve 8th segment input end point */ 1691112babdSJacopo Mondi #define GAM9 0x86 /* Gamma Curve 9th segment input end point */ 1701112babdSJacopo Mondi #define GAM10 0x87 /* Gamma Curve 10th segment input end point */ 1711112babdSJacopo Mondi #define GAM11 0x88 /* Gamma Curve 11th segment input end point */ 1721112babdSJacopo Mondi #define GAM12 0x89 /* Gamma Curve 12th segment input end point */ 1731112babdSJacopo Mondi #define GAM13 0x8A /* Gamma Curve 13th segment input end point */ 1741112babdSJacopo Mondi #define GAM14 0x8B /* Gamma Curve 14th segment input end point */ 1751112babdSJacopo Mondi #define GAM15 0x8C /* Gamma Curve 15th segment input end point */ 1761112babdSJacopo Mondi #define SLOP 0x8D /* Gamma curve highest segment slope */ 1771112babdSJacopo Mondi #define DNSTH 0x8E /* De-noise threshold */ 1781112babdSJacopo Mondi #define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */ 1791112babdSJacopo Mondi #define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */ 1801112babdSJacopo Mondi #define DNSOFF 0x91 /* Auto De-noise threshold control */ 1811112babdSJacopo Mondi #define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */ 1821112babdSJacopo Mondi #define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */ 1831112babdSJacopo Mondi #define MTX1 0x94 /* Matrix coefficient 1 */ 1841112babdSJacopo Mondi #define MTX2 0x95 /* Matrix coefficient 2 */ 1851112babdSJacopo Mondi #define MTX3 0x96 /* Matrix coefficient 3 */ 1861112babdSJacopo Mondi #define MTX4 0x97 /* Matrix coefficient 4 */ 1871112babdSJacopo Mondi #define MTX5 0x98 /* Matrix coefficient 5 */ 1881112babdSJacopo Mondi #define MTX6 0x99 /* Matrix coefficient 6 */ 1891112babdSJacopo Mondi #define MTX_CTRL 0x9A /* Matrix control */ 1901112babdSJacopo Mondi #define BRIGHT 0x9B /* Brightness control */ 1911112babdSJacopo Mondi #define CNTRST 0x9C /* Contrast contrast */ 1921112babdSJacopo Mondi #define CNTRST_CTRL 0x9D /* Contrast contrast center */ 1931112babdSJacopo Mondi #define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */ 1941112babdSJacopo Mondi #define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */ 1951112babdSJacopo Mondi #define SCAL0 0xA0 /* Scaling control 0 */ 1961112babdSJacopo Mondi #define SCAL1 0xA1 /* Scaling control 1 */ 1971112babdSJacopo Mondi #define SCAL2 0xA2 /* Scaling control 2 */ 1981112babdSJacopo Mondi #define FIFODLYM 0xA3 /* FIFO manual mode delay control */ 1991112babdSJacopo Mondi #define FIFODLYA 0xA4 /* FIFO auto mode delay control */ 2001112babdSJacopo Mondi #define SDE 0xA6 /* Special digital effect control */ 2011112babdSJacopo Mondi #define USAT 0xA7 /* U component saturation control */ 2021112babdSJacopo Mondi #define VSAT 0xA8 /* V component saturation control */ 2031112babdSJacopo Mondi /* for ov7720 */ 2041112babdSJacopo Mondi #define HUE0 0xA9 /* Hue control 0 */ 2051112babdSJacopo Mondi #define HUE1 0xAA /* Hue control 1 */ 2061112babdSJacopo Mondi /* for ov7725 */ 2071112babdSJacopo Mondi #define HUECOS 0xA9 /* Cosine value */ 2081112babdSJacopo Mondi #define HUESIN 0xAA /* Sine value */ 2091112babdSJacopo Mondi 2101112babdSJacopo Mondi #define SIGN 0xAB /* Sign bit for Hue and contrast */ 2111112babdSJacopo Mondi #define DSPAUTO 0xAC /* DSP auto function ON/OFF control */ 2121112babdSJacopo Mondi 2131112babdSJacopo Mondi /* 2141112babdSJacopo Mondi * register detail 2151112babdSJacopo Mondi */ 2161112babdSJacopo Mondi 2171112babdSJacopo Mondi /* COM2 */ 2181112babdSJacopo Mondi #define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ 2191112babdSJacopo Mondi /* Output drive capability */ 2201112babdSJacopo Mondi #define OCAP_1x 0x00 /* 1x */ 2211112babdSJacopo Mondi #define OCAP_2x 0x01 /* 2x */ 2221112babdSJacopo Mondi #define OCAP_3x 0x02 /* 3x */ 2231112babdSJacopo Mondi #define OCAP_4x 0x03 /* 4x */ 2241112babdSJacopo Mondi 2251112babdSJacopo Mondi /* COM3 */ 2261112babdSJacopo Mondi #define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML) 2271112babdSJacopo Mondi #define IMG_MASK (VFLIP_IMG | HFLIP_IMG) 2281112babdSJacopo Mondi 2291112babdSJacopo Mondi #define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */ 2301112babdSJacopo Mondi #define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */ 2311112babdSJacopo Mondi #define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */ 2321112babdSJacopo Mondi #define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */ 2331112babdSJacopo Mondi #define SWAP_ML 0x08 /* Swap output MSB/LSB */ 2341112babdSJacopo Mondi /* Tri-state option for output clock */ 2351112babdSJacopo Mondi #define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */ 2361112babdSJacopo Mondi /* 1: No tri-state at this period */ 2371112babdSJacopo Mondi /* Tri-state option for output data */ 2381112babdSJacopo Mondi #define NOTRI_DATA 0x02 /* 0: Tri-state at this period */ 2391112babdSJacopo Mondi /* 1: No tri-state at this period */ 2401112babdSJacopo Mondi #define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */ 2411112babdSJacopo Mondi 2421112babdSJacopo Mondi /* COM4 */ 2431112babdSJacopo Mondi /* PLL frequency control */ 2441112babdSJacopo Mondi #define PLL_BYPASS 0x00 /* 00: Bypass PLL */ 2451112babdSJacopo Mondi #define PLL_4x 0x40 /* 01: PLL 4x */ 2461112babdSJacopo Mondi #define PLL_6x 0x80 /* 10: PLL 6x */ 2471112babdSJacopo Mondi #define PLL_8x 0xc0 /* 11: PLL 8x */ 2481112babdSJacopo Mondi /* AEC evaluate window */ 2491112babdSJacopo Mondi #define AEC_FULL 0x00 /* 00: Full window */ 2501112babdSJacopo Mondi #define AEC_1p2 0x10 /* 01: 1/2 window */ 2511112babdSJacopo Mondi #define AEC_1p4 0x20 /* 10: 1/4 window */ 2521112babdSJacopo Mondi #define AEC_2p3 0x30 /* 11: Low 2/3 window */ 25335089491SJacopo Mondi #define COM4_RESERVED 0x01 /* Reserved bit */ 2541112babdSJacopo Mondi 2551112babdSJacopo Mondi /* COM5 */ 2561112babdSJacopo Mondi #define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */ 2571112babdSJacopo Mondi #define AFR_SPPED 0x40 /* Auto frame rate control speed selection */ 2581112babdSJacopo Mondi /* Auto frame rate max rate control */ 2591112babdSJacopo Mondi #define AFR_NO_RATE 0x00 /* No reduction of frame rate */ 2601112babdSJacopo Mondi #define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */ 2611112babdSJacopo Mondi #define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */ 2621112babdSJacopo Mondi #define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */ 2631112babdSJacopo Mondi /* Auto frame rate active point control */ 2641112babdSJacopo Mondi #define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */ 2651112babdSJacopo Mondi #define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */ 2661112babdSJacopo Mondi #define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */ 2671112babdSJacopo Mondi #define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */ 2681112babdSJacopo Mondi /* AEC max step control */ 2691112babdSJacopo Mondi #define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */ 2701112babdSJacopo Mondi /* 1 : No limit to AEC increase step */ 27135089491SJacopo Mondi /* CLKRC */ 27235089491SJacopo Mondi /* Input clock divider register */ 27335089491SJacopo Mondi #define CLKRC_RESERVED 0x80 /* Reserved bit */ 27435089491SJacopo Mondi #define CLKRC_DIV(n) ((n) - 1) 2751112babdSJacopo Mondi 2761112babdSJacopo Mondi /* COM7 */ 2771112babdSJacopo Mondi /* SCCB Register Reset */ 2781112babdSJacopo Mondi #define SCCB_RESET 0x80 /* 0 : No change */ 2791112babdSJacopo Mondi /* 1 : Resets all registers to default */ 2801112babdSJacopo Mondi /* Resolution selection */ 2811112babdSJacopo Mondi #define SLCT_MASK 0x40 /* Mask of VGA or QVGA */ 2821112babdSJacopo Mondi #define SLCT_VGA 0x00 /* 0 : VGA */ 2831112babdSJacopo Mondi #define SLCT_QVGA 0x40 /* 1 : QVGA */ 2841112babdSJacopo Mondi #define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */ 2851112babdSJacopo Mondi #define SENSOR_RAW 0x10 /* Sensor RAW */ 2861112babdSJacopo Mondi /* RGB output format control */ 2871112babdSJacopo Mondi #define FMT_MASK 0x0c /* Mask of color format */ 2881112babdSJacopo Mondi #define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */ 2891112babdSJacopo Mondi #define FMT_RGB565 0x04 /* 01 : RGB 565 */ 2901112babdSJacopo Mondi #define FMT_RGB555 0x08 /* 10 : RGB 555 */ 2911112babdSJacopo Mondi #define FMT_RGB444 0x0c /* 11 : RGB 444 */ 2921112babdSJacopo Mondi /* Output format control */ 2931112babdSJacopo Mondi #define OFMT_MASK 0x03 /* Mask of output format */ 2941112babdSJacopo Mondi #define OFMT_YUV 0x00 /* 00 : YUV */ 2951112babdSJacopo Mondi #define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */ 2961112babdSJacopo Mondi #define OFMT_RGB 0x02 /* 10 : RGB */ 2971112babdSJacopo Mondi #define OFMT_BRAW 0x03 /* 11 : Bayer RAW */ 2981112babdSJacopo Mondi 2991112babdSJacopo Mondi /* COM8 */ 3001112babdSJacopo Mondi #define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */ 3011112babdSJacopo Mondi /* AEC Setp size limit */ 3021112babdSJacopo Mondi #define UNLMT_STEP 0x40 /* 0 : Step size is limited */ 3031112babdSJacopo Mondi /* 1 : Unlimited step size */ 3041112babdSJacopo Mondi #define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */ 3051112babdSJacopo Mondi #define AEC_BND 0x10 /* Enable AEC below banding value */ 3061112babdSJacopo Mondi #define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */ 3071112babdSJacopo Mondi #define AGC_ON 0x04 /* AGC Enable */ 3081112babdSJacopo Mondi #define AWB_ON 0x02 /* AWB Enable */ 3091112babdSJacopo Mondi #define AEC_ON 0x01 /* AEC Enable */ 3101112babdSJacopo Mondi 3111112babdSJacopo Mondi /* COM9 */ 3121112babdSJacopo Mondi #define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */ 3131112babdSJacopo Mondi /* Automatic gain ceiling - maximum AGC value */ 3141112babdSJacopo Mondi #define GAIN_2x 0x00 /* 000 : 2x */ 3151112babdSJacopo Mondi #define GAIN_4x 0x10 /* 001 : 4x */ 3161112babdSJacopo Mondi #define GAIN_8x 0x20 /* 010 : 8x */ 3171112babdSJacopo Mondi #define GAIN_16x 0x30 /* 011 : 16x */ 3181112babdSJacopo Mondi #define GAIN_32x 0x40 /* 100 : 32x */ 3191112babdSJacopo Mondi #define GAIN_64x 0x50 /* 101 : 64x */ 3201112babdSJacopo Mondi #define GAIN_128x 0x60 /* 110 : 128x */ 3211112babdSJacopo Mondi #define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ 3221112babdSJacopo Mondi #define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ 3231112babdSJacopo Mondi 3241112babdSJacopo Mondi /* COM11 */ 3251112babdSJacopo Mondi #define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */ 3261112babdSJacopo Mondi #define SGLF_TRIG 0x01 /* Single frame transfer trigger */ 3271112babdSJacopo Mondi 3281112babdSJacopo Mondi /* HREF */ 3291112babdSJacopo Mondi #define HREF_VSTART_SHIFT 6 /* VSTART LSB */ 3301112babdSJacopo Mondi #define HREF_HSTART_SHIFT 4 /* HSTART 2 LSBs */ 3311112babdSJacopo Mondi #define HREF_VSIZE_SHIFT 2 /* VSIZE LSB */ 3321112babdSJacopo Mondi #define HREF_HSIZE_SHIFT 0 /* HSIZE 2 LSBs */ 3331112babdSJacopo Mondi 3341112babdSJacopo Mondi /* EXHCH */ 3351112babdSJacopo Mondi #define EXHCH_VSIZE_SHIFT 2 /* VOUTSIZE LSB */ 3361112babdSJacopo Mondi #define EXHCH_HSIZE_SHIFT 0 /* HOUTSIZE 2 LSBs */ 3371112babdSJacopo Mondi 3381112babdSJacopo Mondi /* DSP_CTRL1 */ 3391112babdSJacopo Mondi #define FIFO_ON 0x80 /* FIFO enable/disable selection */ 3401112babdSJacopo Mondi #define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */ 3411112babdSJacopo Mondi #define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */ 3421112babdSJacopo Mondi #define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */ 3431112babdSJacopo Mondi #define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */ 3441112babdSJacopo Mondi #define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */ 3451112babdSJacopo Mondi #define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */ 3461112babdSJacopo Mondi #define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */ 3471112babdSJacopo Mondi 3481112babdSJacopo Mondi /* DSP_CTRL3 */ 3491112babdSJacopo Mondi #define UV_MASK 0x80 /* UV output sequence option */ 3501112babdSJacopo Mondi #define UV_ON 0x80 /* ON */ 3511112babdSJacopo Mondi #define UV_OFF 0x00 /* OFF */ 3521112babdSJacopo Mondi #define CBAR_MASK 0x20 /* DSP Color bar mask */ 3531112babdSJacopo Mondi #define CBAR_ON 0x20 /* ON */ 3541112babdSJacopo Mondi #define CBAR_OFF 0x00 /* OFF */ 3551112babdSJacopo Mondi 3561112babdSJacopo Mondi /* DSP_CTRL4 */ 3571112babdSJacopo Mondi #define DSP_OFMT_YUV 0x00 3581112babdSJacopo Mondi #define DSP_OFMT_RGB 0x00 3591112babdSJacopo Mondi #define DSP_OFMT_RAW8 0x02 3601112babdSJacopo Mondi #define DSP_OFMT_RAW10 0x03 3611112babdSJacopo Mondi 3621112babdSJacopo Mondi /* DSPAUTO (DSP Auto Function ON/OFF Control) */ 3631112babdSJacopo Mondi #define AWB_ACTRL 0x80 /* AWB auto threshold control */ 3641112babdSJacopo Mondi #define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */ 3651112babdSJacopo Mondi #define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */ 3661112babdSJacopo Mondi #define UV_ACTRL 0x10 /* UV adjust auto slope control */ 3671112babdSJacopo Mondi #define SCAL0_ACTRL 0x08 /* Auto scaling factor control */ 3681112babdSJacopo Mondi #define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */ 3691112babdSJacopo Mondi 3701112babdSJacopo Mondi #define OV772X_MAX_WIDTH VGA_WIDTH 3711112babdSJacopo Mondi #define OV772X_MAX_HEIGHT VGA_HEIGHT 3721112babdSJacopo Mondi 3731112babdSJacopo Mondi /* 3741112babdSJacopo Mondi * ID 3751112babdSJacopo Mondi */ 3761112babdSJacopo Mondi #define OV7720 0x7720 3771112babdSJacopo Mondi #define OV7725 0x7721 3781112babdSJacopo Mondi #define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) 3791112babdSJacopo Mondi 3801112babdSJacopo Mondi /* 38135089491SJacopo Mondi * PLL multipliers 38235089491SJacopo Mondi */ 38335089491SJacopo Mondi static struct { 38435089491SJacopo Mondi unsigned int mult; 38535089491SJacopo Mondi u8 com4; 38635089491SJacopo Mondi } ov772x_pll[] = { 38735089491SJacopo Mondi { 1, PLL_BYPASS, }, 38835089491SJacopo Mondi { 4, PLL_4x, }, 38935089491SJacopo Mondi { 6, PLL_6x, }, 39035089491SJacopo Mondi { 8, PLL_8x, }, 39135089491SJacopo Mondi }; 39235089491SJacopo Mondi 39335089491SJacopo Mondi /* 3941112babdSJacopo Mondi * struct 3951112babdSJacopo Mondi */ 3961112babdSJacopo Mondi 3971112babdSJacopo Mondi struct ov772x_color_format { 3981112babdSJacopo Mondi u32 code; 3991112babdSJacopo Mondi enum v4l2_colorspace colorspace; 4001112babdSJacopo Mondi u8 dsp3; 4011112babdSJacopo Mondi u8 dsp4; 4021112babdSJacopo Mondi u8 com3; 4031112babdSJacopo Mondi u8 com7; 4041112babdSJacopo Mondi }; 4051112babdSJacopo Mondi 4061112babdSJacopo Mondi struct ov772x_win_size { 4071112babdSJacopo Mondi char *name; 4081112babdSJacopo Mondi unsigned char com7_bit; 40935089491SJacopo Mondi unsigned int sizeimage; 4101112babdSJacopo Mondi struct v4l2_rect rect; 4111112babdSJacopo Mondi }; 4121112babdSJacopo Mondi 4131112babdSJacopo Mondi struct ov772x_priv { 4141112babdSJacopo Mondi struct v4l2_subdev subdev; 4151112babdSJacopo Mondi struct v4l2_ctrl_handler hdl; 416762c2812SJacopo Mondi struct clk *clk; 4171112babdSJacopo Mondi struct ov772x_camera_info *info; 418762c2812SJacopo Mondi struct gpio_desc *pwdn_gpio; 419762c2812SJacopo Mondi struct gpio_desc *rstb_gpio; 4201112babdSJacopo Mondi const struct ov772x_color_format *cfmt; 4211112babdSJacopo Mondi const struct ov772x_win_size *win; 42209e620c6SAkinobu Mita struct v4l2_ctrl *vflip_ctrl; 42309e620c6SAkinobu Mita struct v4l2_ctrl *hflip_ctrl; 4241112babdSJacopo Mondi /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ 42509e620c6SAkinobu Mita struct v4l2_ctrl *band_filter_ctrl; 42635089491SJacopo Mondi unsigned int fps; 4277b9998c9SAkinobu Mita /* lock to protect power_count and streaming */ 42834af7d92SAkinobu Mita struct mutex lock; 42934af7d92SAkinobu Mita int power_count; 4307b9998c9SAkinobu Mita int streaming; 4314b610d6dSAkinobu Mita #ifdef CONFIG_MEDIA_CONTROLLER 4324b610d6dSAkinobu Mita struct media_pad pad; 4334b610d6dSAkinobu Mita #endif 4341112babdSJacopo Mondi }; 4351112babdSJacopo Mondi 4361112babdSJacopo Mondi /* 4371112babdSJacopo Mondi * supported color format list 4381112babdSJacopo Mondi */ 4391112babdSJacopo Mondi static const struct ov772x_color_format ov772x_cfmts[] = { 4401112babdSJacopo Mondi { 4411112babdSJacopo Mondi .code = MEDIA_BUS_FMT_YUYV8_2X8, 442762c2812SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 4431112babdSJacopo Mondi .dsp3 = 0x0, 4441112babdSJacopo Mondi .dsp4 = DSP_OFMT_YUV, 4451112babdSJacopo Mondi .com3 = SWAP_YUV, 4461112babdSJacopo Mondi .com7 = OFMT_YUV, 4471112babdSJacopo Mondi }, 4481112babdSJacopo Mondi { 4491112babdSJacopo Mondi .code = MEDIA_BUS_FMT_YVYU8_2X8, 450762c2812SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 4511112babdSJacopo Mondi .dsp3 = UV_ON, 4521112babdSJacopo Mondi .dsp4 = DSP_OFMT_YUV, 4531112babdSJacopo Mondi .com3 = SWAP_YUV, 4541112babdSJacopo Mondi .com7 = OFMT_YUV, 4551112babdSJacopo Mondi }, 4561112babdSJacopo Mondi { 4571112babdSJacopo Mondi .code = MEDIA_BUS_FMT_UYVY8_2X8, 458762c2812SJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 4591112babdSJacopo Mondi .dsp3 = 0x0, 4601112babdSJacopo Mondi .dsp4 = DSP_OFMT_YUV, 4611112babdSJacopo Mondi .com3 = 0x0, 4621112babdSJacopo Mondi .com7 = OFMT_YUV, 4631112babdSJacopo Mondi }, 4641112babdSJacopo Mondi { 4651112babdSJacopo Mondi .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, 4661112babdSJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 4671112babdSJacopo Mondi .dsp3 = 0x0, 4681112babdSJacopo Mondi .dsp4 = DSP_OFMT_YUV, 4691112babdSJacopo Mondi .com3 = SWAP_RGB, 4701112babdSJacopo Mondi .com7 = FMT_RGB555 | OFMT_RGB, 4711112babdSJacopo Mondi }, 4721112babdSJacopo Mondi { 4731112babdSJacopo Mondi .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, 4741112babdSJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 4751112babdSJacopo Mondi .dsp3 = 0x0, 4761112babdSJacopo Mondi .dsp4 = DSP_OFMT_YUV, 4771112babdSJacopo Mondi .com3 = 0x0, 4781112babdSJacopo Mondi .com7 = FMT_RGB555 | OFMT_RGB, 4791112babdSJacopo Mondi }, 4801112babdSJacopo Mondi { 4811112babdSJacopo Mondi .code = MEDIA_BUS_FMT_RGB565_2X8_LE, 4821112babdSJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 4831112babdSJacopo Mondi .dsp3 = 0x0, 4841112babdSJacopo Mondi .dsp4 = DSP_OFMT_YUV, 4851112babdSJacopo Mondi .com3 = SWAP_RGB, 4861112babdSJacopo Mondi .com7 = FMT_RGB565 | OFMT_RGB, 4871112babdSJacopo Mondi }, 4881112babdSJacopo Mondi { 4891112babdSJacopo Mondi .code = MEDIA_BUS_FMT_RGB565_2X8_BE, 4901112babdSJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 4911112babdSJacopo Mondi .dsp3 = 0x0, 4921112babdSJacopo Mondi .dsp4 = DSP_OFMT_YUV, 4931112babdSJacopo Mondi .com3 = 0x0, 4941112babdSJacopo Mondi .com7 = FMT_RGB565 | OFMT_RGB, 4951112babdSJacopo Mondi }, 4961112babdSJacopo Mondi { 4971112babdSJacopo Mondi /* Setting DSP4 to DSP_OFMT_RAW8 still gives 10-bit output, 4981112babdSJacopo Mondi * regardless of the COM7 value. We can thus only support 10-bit 4991112babdSJacopo Mondi * Bayer until someone figures it out. 5001112babdSJacopo Mondi */ 5011112babdSJacopo Mondi .code = MEDIA_BUS_FMT_SBGGR10_1X10, 5021112babdSJacopo Mondi .colorspace = V4L2_COLORSPACE_SRGB, 5031112babdSJacopo Mondi .dsp3 = 0x0, 5041112babdSJacopo Mondi .dsp4 = DSP_OFMT_RAW10, 5051112babdSJacopo Mondi .com3 = 0x0, 5061112babdSJacopo Mondi .com7 = SENSOR_RAW | OFMT_BRAW, 5071112babdSJacopo Mondi }, 5081112babdSJacopo Mondi }; 5091112babdSJacopo Mondi 5101112babdSJacopo Mondi /* 5111112babdSJacopo Mondi * window size list 5121112babdSJacopo Mondi */ 5131112babdSJacopo Mondi 5141112babdSJacopo Mondi static const struct ov772x_win_size ov772x_win_sizes[] = { 5151112babdSJacopo Mondi { 5161112babdSJacopo Mondi .name = "VGA", 5171112babdSJacopo Mondi .com7_bit = SLCT_VGA, 51835089491SJacopo Mondi .sizeimage = 510 * 748, 5191112babdSJacopo Mondi .rect = { 5201112babdSJacopo Mondi .left = 140, 5211112babdSJacopo Mondi .top = 14, 5221112babdSJacopo Mondi .width = VGA_WIDTH, 5231112babdSJacopo Mondi .height = VGA_HEIGHT, 5241112babdSJacopo Mondi }, 5251112babdSJacopo Mondi }, { 5261112babdSJacopo Mondi .name = "QVGA", 5271112babdSJacopo Mondi .com7_bit = SLCT_QVGA, 52835089491SJacopo Mondi .sizeimage = 278 * 576, 5291112babdSJacopo Mondi .rect = { 5301112babdSJacopo Mondi .left = 252, 5311112babdSJacopo Mondi .top = 6, 5321112babdSJacopo Mondi .width = QVGA_WIDTH, 5331112babdSJacopo Mondi .height = QVGA_HEIGHT, 5341112babdSJacopo Mondi }, 5351112babdSJacopo Mondi }, 5361112babdSJacopo Mondi }; 5371112babdSJacopo Mondi 5381112babdSJacopo Mondi /* 53935089491SJacopo Mondi * frame rate settings lists 54035089491SJacopo Mondi */ 5417b69f2cbSMauro Carvalho Chehab static const unsigned int ov772x_frame_intervals[] = { 5, 10, 15, 20, 30, 60 }; 54235089491SJacopo Mondi 54335089491SJacopo Mondi /* 5441112babdSJacopo Mondi * general function 5451112babdSJacopo Mondi */ 5461112babdSJacopo Mondi 5471112babdSJacopo Mondi static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd) 5481112babdSJacopo Mondi { 5491112babdSJacopo Mondi return container_of(sd, struct ov772x_priv, subdev); 5501112babdSJacopo Mondi } 5511112babdSJacopo Mondi 5520b964d18SAkinobu Mita static int ov772x_read(struct i2c_client *client, u8 addr) 5531112babdSJacopo Mondi { 5540b964d18SAkinobu Mita int ret; 5550b964d18SAkinobu Mita u8 val; 5560b964d18SAkinobu Mita 5570b964d18SAkinobu Mita ret = i2c_master_send(client, &addr, 1); 5580b964d18SAkinobu Mita if (ret < 0) 5590b964d18SAkinobu Mita return ret; 5600b964d18SAkinobu Mita ret = i2c_master_recv(client, &val, 1); 5610b964d18SAkinobu Mita if (ret < 0) 5620b964d18SAkinobu Mita return ret; 5630b964d18SAkinobu Mita 5640b964d18SAkinobu Mita return val; 5651112babdSJacopo Mondi } 5661112babdSJacopo Mondi 5671112babdSJacopo Mondi static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value) 5681112babdSJacopo Mondi { 5691112babdSJacopo Mondi return i2c_smbus_write_byte_data(client, addr, value); 5701112babdSJacopo Mondi } 5711112babdSJacopo Mondi 5721112babdSJacopo Mondi static int ov772x_mask_set(struct i2c_client *client, u8 command, u8 mask, 5731112babdSJacopo Mondi u8 set) 5741112babdSJacopo Mondi { 5751112babdSJacopo Mondi s32 val = ov772x_read(client, command); 576054d8830SMauro Carvalho Chehab 5771112babdSJacopo Mondi if (val < 0) 5781112babdSJacopo Mondi return val; 5791112babdSJacopo Mondi 5801112babdSJacopo Mondi val &= ~mask; 5811112babdSJacopo Mondi val |= set & mask; 5821112babdSJacopo Mondi 5831112babdSJacopo Mondi return ov772x_write(client, command, val); 5841112babdSJacopo Mondi } 5851112babdSJacopo Mondi 5861112babdSJacopo Mondi static int ov772x_reset(struct i2c_client *client) 5871112babdSJacopo Mondi { 5881112babdSJacopo Mondi int ret; 5891112babdSJacopo Mondi 5901112babdSJacopo Mondi ret = ov772x_write(client, COM7, SCCB_RESET); 5911112babdSJacopo Mondi if (ret < 0) 5921112babdSJacopo Mondi return ret; 5931112babdSJacopo Mondi 594d9c70bbdSJacopo Mondi usleep_range(1000, 5000); 5951112babdSJacopo Mondi 5961112babdSJacopo Mondi return ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); 5971112babdSJacopo Mondi } 5981112babdSJacopo Mondi 5991112babdSJacopo Mondi /* 600762c2812SJacopo Mondi * subdev ops 6011112babdSJacopo Mondi */ 6021112babdSJacopo Mondi 6031112babdSJacopo Mondi static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) 6041112babdSJacopo Mondi { 6051112babdSJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 6061112babdSJacopo Mondi struct ov772x_priv *priv = to_ov772x(sd); 6077b9998c9SAkinobu Mita int ret = 0; 6081112babdSJacopo Mondi 6097b9998c9SAkinobu Mita mutex_lock(&priv->lock); 6101112babdSJacopo Mondi 6117b9998c9SAkinobu Mita if (priv->streaming == enable) 6127b9998c9SAkinobu Mita goto done; 6131112babdSJacopo Mondi 6147b9998c9SAkinobu Mita ret = ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 6157b9998c9SAkinobu Mita enable ? 0 : SOFT_SLEEP_MODE); 6167b9998c9SAkinobu Mita if (ret) 6177b9998c9SAkinobu Mita goto done; 6187b9998c9SAkinobu Mita 6197b9998c9SAkinobu Mita if (enable) { 6201112babdSJacopo Mondi dev_dbg(&client->dev, "format %d, win %s\n", 6211112babdSJacopo Mondi priv->cfmt->code, priv->win->name); 6227b9998c9SAkinobu Mita } 6237b9998c9SAkinobu Mita priv->streaming = enable; 6241112babdSJacopo Mondi 6257b9998c9SAkinobu Mita done: 6267b9998c9SAkinobu Mita mutex_unlock(&priv->lock); 6277b9998c9SAkinobu Mita 6287b9998c9SAkinobu Mita return ret; 6291112babdSJacopo Mondi } 6301112babdSJacopo Mondi 63180dc2a49SAkinobu Mita static unsigned int ov772x_select_fps(struct ov772x_priv *priv, 63280dc2a49SAkinobu Mita struct v4l2_fract *tpf) 63335089491SJacopo Mondi { 63435089491SJacopo Mondi unsigned int fps = tpf->numerator ? 63535089491SJacopo Mondi tpf->denominator / tpf->numerator : 63635089491SJacopo Mondi tpf->denominator; 63735089491SJacopo Mondi unsigned int best_diff; 63835089491SJacopo Mondi unsigned int diff; 63935089491SJacopo Mondi unsigned int idx; 64035089491SJacopo Mondi unsigned int i; 64135089491SJacopo Mondi 64235089491SJacopo Mondi /* Approximate to the closest supported frame interval. */ 64335089491SJacopo Mondi best_diff = ~0L; 64435089491SJacopo Mondi for (i = 0, idx = 0; i < ARRAY_SIZE(ov772x_frame_intervals); i++) { 64535089491SJacopo Mondi diff = abs(fps - ov772x_frame_intervals[i]); 64635089491SJacopo Mondi if (diff < best_diff) { 64735089491SJacopo Mondi idx = i; 64835089491SJacopo Mondi best_diff = diff; 64935089491SJacopo Mondi } 65035089491SJacopo Mondi } 65180dc2a49SAkinobu Mita 65280dc2a49SAkinobu Mita return ov772x_frame_intervals[idx]; 65380dc2a49SAkinobu Mita } 65480dc2a49SAkinobu Mita 65580dc2a49SAkinobu Mita static int ov772x_set_frame_rate(struct ov772x_priv *priv, 65680dc2a49SAkinobu Mita unsigned int fps, 65780dc2a49SAkinobu Mita const struct ov772x_color_format *cfmt, 65880dc2a49SAkinobu Mita const struct ov772x_win_size *win) 65980dc2a49SAkinobu Mita { 66080dc2a49SAkinobu Mita struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); 66180dc2a49SAkinobu Mita unsigned long fin = clk_get_rate(priv->clk); 66280dc2a49SAkinobu Mita unsigned int best_diff; 66380dc2a49SAkinobu Mita unsigned int fsize; 66480dc2a49SAkinobu Mita unsigned int pclk; 66580dc2a49SAkinobu Mita unsigned int diff; 66680dc2a49SAkinobu Mita unsigned int i; 66780dc2a49SAkinobu Mita u8 clkrc = 0; 66880dc2a49SAkinobu Mita u8 com4 = 0; 66980dc2a49SAkinobu Mita int ret; 67035089491SJacopo Mondi 67135089491SJacopo Mondi /* Use image size (with blankings) to calculate desired pixel clock. */ 67235089491SJacopo Mondi switch (cfmt->com7 & OFMT_MASK) { 67335089491SJacopo Mondi case OFMT_BRAW: 67435089491SJacopo Mondi fsize = win->sizeimage; 67535089491SJacopo Mondi break; 67635089491SJacopo Mondi case OFMT_RGB: 67735089491SJacopo Mondi case OFMT_YUV: 67835089491SJacopo Mondi default: 67935089491SJacopo Mondi fsize = win->sizeimage * 2; 68035089491SJacopo Mondi break; 68135089491SJacopo Mondi } 68235089491SJacopo Mondi 68335089491SJacopo Mondi pclk = fps * fsize; 68435089491SJacopo Mondi 68535089491SJacopo Mondi /* 68635089491SJacopo Mondi * Pixel clock generation circuit is pretty simple: 68735089491SJacopo Mondi * 68835089491SJacopo Mondi * Fin -> [ / CLKRC_div] -> [ * PLL_mult] -> pclk 68935089491SJacopo Mondi * 69035089491SJacopo Mondi * Try to approximate the desired pixel clock testing all available 69135089491SJacopo Mondi * PLL multipliers (1x, 4x, 6x, 8x) and calculate corresponding 69235089491SJacopo Mondi * divisor with: 69335089491SJacopo Mondi * 69435089491SJacopo Mondi * div = PLL_mult * Fin / pclk 69535089491SJacopo Mondi * 69635089491SJacopo Mondi * and re-calculate the pixel clock using it: 69735089491SJacopo Mondi * 69835089491SJacopo Mondi * pclk = Fin * PLL_mult / CLKRC_div 69935089491SJacopo Mondi * 70035089491SJacopo Mondi * Choose the PLL_mult and CLKRC_div pair that gives a pixel clock 70135089491SJacopo Mondi * closer to the desired one. 70235089491SJacopo Mondi * 70335089491SJacopo Mondi * The desired pixel clock is calculated using a known frame size 70435089491SJacopo Mondi * (blanking included) and FPS. 70535089491SJacopo Mondi */ 70635089491SJacopo Mondi best_diff = ~0L; 70735089491SJacopo Mondi for (i = 0; i < ARRAY_SIZE(ov772x_pll); i++) { 70835089491SJacopo Mondi unsigned int pll_mult = ov772x_pll[i].mult; 70935089491SJacopo Mondi unsigned int pll_out = pll_mult * fin; 71035089491SJacopo Mondi unsigned int t_pclk; 71135089491SJacopo Mondi unsigned int div; 71235089491SJacopo Mondi 71335089491SJacopo Mondi if (pll_out < pclk) 71435089491SJacopo Mondi continue; 71535089491SJacopo Mondi 71635089491SJacopo Mondi div = DIV_ROUND_CLOSEST(pll_out, pclk); 71735089491SJacopo Mondi t_pclk = DIV_ROUND_CLOSEST(fin * pll_mult, div); 71835089491SJacopo Mondi diff = abs(pclk - t_pclk); 71935089491SJacopo Mondi if (diff < best_diff) { 72035089491SJacopo Mondi best_diff = diff; 72135089491SJacopo Mondi clkrc = CLKRC_DIV(div); 72235089491SJacopo Mondi com4 = ov772x_pll[i].com4; 72335089491SJacopo Mondi } 72435089491SJacopo Mondi } 72535089491SJacopo Mondi 72635089491SJacopo Mondi ret = ov772x_write(client, COM4, com4 | COM4_RESERVED); 72735089491SJacopo Mondi if (ret < 0) 72835089491SJacopo Mondi return ret; 72935089491SJacopo Mondi 73035089491SJacopo Mondi ret = ov772x_write(client, CLKRC, clkrc | CLKRC_RESERVED); 73135089491SJacopo Mondi if (ret < 0) 73235089491SJacopo Mondi return ret; 73335089491SJacopo Mondi 73435089491SJacopo Mondi return 0; 73535089491SJacopo Mondi } 73635089491SJacopo Mondi 73735089491SJacopo Mondi static int ov772x_g_frame_interval(struct v4l2_subdev *sd, 73835089491SJacopo Mondi struct v4l2_subdev_frame_interval *ival) 73935089491SJacopo Mondi { 74035089491SJacopo Mondi struct ov772x_priv *priv = to_ov772x(sd); 74135089491SJacopo Mondi struct v4l2_fract *tpf = &ival->interval; 74235089491SJacopo Mondi 74335089491SJacopo Mondi tpf->numerator = 1; 74435089491SJacopo Mondi tpf->denominator = priv->fps; 74535089491SJacopo Mondi 74635089491SJacopo Mondi return 0; 74735089491SJacopo Mondi } 74835089491SJacopo Mondi 74935089491SJacopo Mondi static int ov772x_s_frame_interval(struct v4l2_subdev *sd, 75035089491SJacopo Mondi struct v4l2_subdev_frame_interval *ival) 75135089491SJacopo Mondi { 75235089491SJacopo Mondi struct ov772x_priv *priv = to_ov772x(sd); 75335089491SJacopo Mondi struct v4l2_fract *tpf = &ival->interval; 75480dc2a49SAkinobu Mita unsigned int fps; 75595f5a45aSAkinobu Mita int ret = 0; 75635089491SJacopo Mondi 7577b9998c9SAkinobu Mita mutex_lock(&priv->lock); 7587b9998c9SAkinobu Mita 7597b9998c9SAkinobu Mita if (priv->streaming) { 7607b9998c9SAkinobu Mita ret = -EBUSY; 7617b9998c9SAkinobu Mita goto error; 7627b9998c9SAkinobu Mita } 7637b9998c9SAkinobu Mita 76480dc2a49SAkinobu Mita fps = ov772x_select_fps(priv, tpf); 76580dc2a49SAkinobu Mita 76695f5a45aSAkinobu Mita /* 76795f5a45aSAkinobu Mita * If the device is not powered up by the host driver do 76895f5a45aSAkinobu Mita * not apply any changes to H/W at this time. Instead 76995f5a45aSAkinobu Mita * the frame rate will be restored right after power-up. 77095f5a45aSAkinobu Mita */ 77195f5a45aSAkinobu Mita if (priv->power_count > 0) { 77280dc2a49SAkinobu Mita ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win); 77380dc2a49SAkinobu Mita if (ret) 77495f5a45aSAkinobu Mita goto error; 77595f5a45aSAkinobu Mita } 77680dc2a49SAkinobu Mita 77780dc2a49SAkinobu Mita tpf->numerator = 1; 77880dc2a49SAkinobu Mita tpf->denominator = fps; 77980dc2a49SAkinobu Mita priv->fps = fps; 78080dc2a49SAkinobu Mita 78195f5a45aSAkinobu Mita error: 78295f5a45aSAkinobu Mita mutex_unlock(&priv->lock); 78395f5a45aSAkinobu Mita 78495f5a45aSAkinobu Mita return ret; 78535089491SJacopo Mondi } 78635089491SJacopo Mondi 7871112babdSJacopo Mondi static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) 7881112babdSJacopo Mondi { 7891112babdSJacopo Mondi struct ov772x_priv *priv = container_of(ctrl->handler, 7901112babdSJacopo Mondi struct ov772x_priv, hdl); 7911112babdSJacopo Mondi struct v4l2_subdev *sd = &priv->subdev; 7921112babdSJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 7931112babdSJacopo Mondi int ret = 0; 7941112babdSJacopo Mondi u8 val; 7951112babdSJacopo Mondi 79695f5a45aSAkinobu Mita /* v4l2_ctrl_lock() locks our own mutex */ 79795f5a45aSAkinobu Mita 79895f5a45aSAkinobu Mita /* 79995f5a45aSAkinobu Mita * If the device is not powered up by the host driver do 80095f5a45aSAkinobu Mita * not apply any controls to H/W at this time. Instead 80195f5a45aSAkinobu Mita * the controls will be restored right after power-up. 80295f5a45aSAkinobu Mita */ 80395f5a45aSAkinobu Mita if (priv->power_count == 0) 80495f5a45aSAkinobu Mita return 0; 80595f5a45aSAkinobu Mita 8061112babdSJacopo Mondi switch (ctrl->id) { 8071112babdSJacopo Mondi case V4L2_CID_VFLIP: 8081112babdSJacopo Mondi val = ctrl->val ? VFLIP_IMG : 0x00; 809c2cae895SAkinobu Mita if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) 8101112babdSJacopo Mondi val ^= VFLIP_IMG; 8111112babdSJacopo Mondi return ov772x_mask_set(client, COM3, VFLIP_IMG, val); 8121112babdSJacopo Mondi case V4L2_CID_HFLIP: 8131112babdSJacopo Mondi val = ctrl->val ? HFLIP_IMG : 0x00; 814c2cae895SAkinobu Mita if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) 8151112babdSJacopo Mondi val ^= HFLIP_IMG; 8161112babdSJacopo Mondi return ov772x_mask_set(client, COM3, HFLIP_IMG, val); 8171112babdSJacopo Mondi case V4L2_CID_BAND_STOP_FILTER: 8181112babdSJacopo Mondi if (!ctrl->val) { 8191112babdSJacopo Mondi /* Switch the filter off, it is on now */ 8201112babdSJacopo Mondi ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff); 8211112babdSJacopo Mondi if (!ret) 8221112babdSJacopo Mondi ret = ov772x_mask_set(client, COM8, 8231112babdSJacopo Mondi BNDF_ON_OFF, 0); 8241112babdSJacopo Mondi } else { 8251112babdSJacopo Mondi /* Switch the filter on, set AEC low limit */ 8261112babdSJacopo Mondi val = 256 - ctrl->val; 8271112babdSJacopo Mondi ret = ov772x_mask_set(client, COM8, 8281112babdSJacopo Mondi BNDF_ON_OFF, BNDF_ON_OFF); 8291112babdSJacopo Mondi if (!ret) 8301112babdSJacopo Mondi ret = ov772x_mask_set(client, BDBASE, 8311112babdSJacopo Mondi 0xff, val); 8321112babdSJacopo Mondi } 83309e620c6SAkinobu Mita 8341112babdSJacopo Mondi return ret; 8351112babdSJacopo Mondi } 8361112babdSJacopo Mondi 8371112babdSJacopo Mondi return -EINVAL; 8381112babdSJacopo Mondi } 8391112babdSJacopo Mondi 8401112babdSJacopo Mondi #ifdef CONFIG_VIDEO_ADV_DEBUG 8411112babdSJacopo Mondi static int ov772x_g_register(struct v4l2_subdev *sd, 8421112babdSJacopo Mondi struct v4l2_dbg_register *reg) 8431112babdSJacopo Mondi { 8441112babdSJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 8451112babdSJacopo Mondi int ret; 8461112babdSJacopo Mondi 8471112babdSJacopo Mondi reg->size = 1; 8481112babdSJacopo Mondi if (reg->reg > 0xff) 8491112babdSJacopo Mondi return -EINVAL; 8501112babdSJacopo Mondi 8511112babdSJacopo Mondi ret = ov772x_read(client, reg->reg); 8521112babdSJacopo Mondi if (ret < 0) 8531112babdSJacopo Mondi return ret; 8541112babdSJacopo Mondi 8551112babdSJacopo Mondi reg->val = (__u64)ret; 8561112babdSJacopo Mondi 8571112babdSJacopo Mondi return 0; 8581112babdSJacopo Mondi } 8591112babdSJacopo Mondi 8601112babdSJacopo Mondi static int ov772x_s_register(struct v4l2_subdev *sd, 8611112babdSJacopo Mondi const struct v4l2_dbg_register *reg) 8621112babdSJacopo Mondi { 8631112babdSJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(sd); 8641112babdSJacopo Mondi 8651112babdSJacopo Mondi if (reg->reg > 0xff || 8661112babdSJacopo Mondi reg->val > 0xff) 8671112babdSJacopo Mondi return -EINVAL; 8681112babdSJacopo Mondi 8691112babdSJacopo Mondi return ov772x_write(client, reg->reg, reg->val); 8701112babdSJacopo Mondi } 8711112babdSJacopo Mondi #endif 8721112babdSJacopo Mondi 873762c2812SJacopo Mondi static int ov772x_power_on(struct ov772x_priv *priv) 874762c2812SJacopo Mondi { 875762c2812SJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); 876762c2812SJacopo Mondi int ret; 877762c2812SJacopo Mondi 878762c2812SJacopo Mondi if (priv->clk) { 879762c2812SJacopo Mondi ret = clk_prepare_enable(priv->clk); 880762c2812SJacopo Mondi if (ret) 881762c2812SJacopo Mondi return ret; 882762c2812SJacopo Mondi } 883762c2812SJacopo Mondi 884762c2812SJacopo Mondi if (priv->pwdn_gpio) { 885762c2812SJacopo Mondi gpiod_set_value(priv->pwdn_gpio, 1); 886762c2812SJacopo Mondi usleep_range(500, 1000); 887762c2812SJacopo Mondi } 888762c2812SJacopo Mondi 889762c2812SJacopo Mondi /* 890762c2812SJacopo Mondi * FIXME: The reset signal is connected to a shared GPIO on some 891762c2812SJacopo Mondi * platforms (namely the SuperH Migo-R). Until a framework becomes 892762c2812SJacopo Mondi * available to handle this cleanly, request the GPIO temporarily 893762c2812SJacopo Mondi * to avoid conflicts. 894762c2812SJacopo Mondi */ 89540519d54SAkinobu Mita priv->rstb_gpio = gpiod_get_optional(&client->dev, "reset", 896762c2812SJacopo Mondi GPIOD_OUT_LOW); 897762c2812SJacopo Mondi if (IS_ERR(priv->rstb_gpio)) { 89840519d54SAkinobu Mita dev_info(&client->dev, "Unable to get GPIO \"reset\""); 899762c2812SJacopo Mondi return PTR_ERR(priv->rstb_gpio); 900762c2812SJacopo Mondi } 901762c2812SJacopo Mondi 902762c2812SJacopo Mondi if (priv->rstb_gpio) { 903762c2812SJacopo Mondi gpiod_set_value(priv->rstb_gpio, 1); 904762c2812SJacopo Mondi usleep_range(500, 1000); 905762c2812SJacopo Mondi gpiod_set_value(priv->rstb_gpio, 0); 906762c2812SJacopo Mondi usleep_range(500, 1000); 907762c2812SJacopo Mondi 908762c2812SJacopo Mondi gpiod_put(priv->rstb_gpio); 909762c2812SJacopo Mondi } 910762c2812SJacopo Mondi 911762c2812SJacopo Mondi return 0; 912762c2812SJacopo Mondi } 913762c2812SJacopo Mondi 914762c2812SJacopo Mondi static int ov772x_power_off(struct ov772x_priv *priv) 915762c2812SJacopo Mondi { 916762c2812SJacopo Mondi clk_disable_unprepare(priv->clk); 917762c2812SJacopo Mondi 918762c2812SJacopo Mondi if (priv->pwdn_gpio) { 919762c2812SJacopo Mondi gpiod_set_value(priv->pwdn_gpio, 0); 920762c2812SJacopo Mondi usleep_range(500, 1000); 921762c2812SJacopo Mondi } 922762c2812SJacopo Mondi 923762c2812SJacopo Mondi return 0; 924762c2812SJacopo Mondi } 925762c2812SJacopo Mondi 92695f5a45aSAkinobu Mita static int ov772x_set_params(struct ov772x_priv *priv, 92795f5a45aSAkinobu Mita const struct ov772x_color_format *cfmt, 92895f5a45aSAkinobu Mita const struct ov772x_win_size *win); 92995f5a45aSAkinobu Mita 9301112babdSJacopo Mondi static int ov772x_s_power(struct v4l2_subdev *sd, int on) 9311112babdSJacopo Mondi { 9321112babdSJacopo Mondi struct ov772x_priv *priv = to_ov772x(sd); 93334af7d92SAkinobu Mita int ret = 0; 9341112babdSJacopo Mondi 93534af7d92SAkinobu Mita mutex_lock(&priv->lock); 93634af7d92SAkinobu Mita 93734af7d92SAkinobu Mita /* If the power count is modified from 0 to != 0 or from != 0 to 0, 93834af7d92SAkinobu Mita * update the power state. 93934af7d92SAkinobu Mita */ 94095f5a45aSAkinobu Mita if (priv->power_count == !on) { 94195f5a45aSAkinobu Mita if (on) { 94295f5a45aSAkinobu Mita ret = ov772x_power_on(priv); 94395f5a45aSAkinobu Mita /* 94495f5a45aSAkinobu Mita * Restore the format, the frame rate, and 94595f5a45aSAkinobu Mita * the controls 94695f5a45aSAkinobu Mita */ 94795f5a45aSAkinobu Mita if (!ret) 94895f5a45aSAkinobu Mita ret = ov772x_set_params(priv, priv->cfmt, 94995f5a45aSAkinobu Mita priv->win); 95095f5a45aSAkinobu Mita } else { 95195f5a45aSAkinobu Mita ret = ov772x_power_off(priv); 95295f5a45aSAkinobu Mita } 95395f5a45aSAkinobu Mita } 95434af7d92SAkinobu Mita 95534af7d92SAkinobu Mita if (!ret) { 95634af7d92SAkinobu Mita /* Update the power count. */ 95734af7d92SAkinobu Mita priv->power_count += on ? 1 : -1; 95834af7d92SAkinobu Mita WARN(priv->power_count < 0, "Unbalanced power count\n"); 95934af7d92SAkinobu Mita WARN(priv->power_count > 1, "Duplicated s_power call\n"); 96034af7d92SAkinobu Mita } 96134af7d92SAkinobu Mita 96234af7d92SAkinobu Mita mutex_unlock(&priv->lock); 96334af7d92SAkinobu Mita 96434af7d92SAkinobu Mita return ret; 9651112babdSJacopo Mondi } 9661112babdSJacopo Mondi 9671112babdSJacopo Mondi static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) 9681112babdSJacopo Mondi { 9691112babdSJacopo Mondi const struct ov772x_win_size *win = &ov772x_win_sizes[0]; 9701112babdSJacopo Mondi u32 best_diff = UINT_MAX; 9711112babdSJacopo Mondi unsigned int i; 9721112babdSJacopo Mondi 9731112babdSJacopo Mondi for (i = 0; i < ARRAY_SIZE(ov772x_win_sizes); ++i) { 9741112babdSJacopo Mondi u32 diff = abs(width - ov772x_win_sizes[i].rect.width) 9751112babdSJacopo Mondi + abs(height - ov772x_win_sizes[i].rect.height); 9761112babdSJacopo Mondi if (diff < best_diff) { 9771112babdSJacopo Mondi best_diff = diff; 9781112babdSJacopo Mondi win = &ov772x_win_sizes[i]; 9791112babdSJacopo Mondi } 9801112babdSJacopo Mondi } 9811112babdSJacopo Mondi 9821112babdSJacopo Mondi return win; 9831112babdSJacopo Mondi } 9841112babdSJacopo Mondi 9851112babdSJacopo Mondi static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf, 9861112babdSJacopo Mondi const struct ov772x_color_format **cfmt, 9871112babdSJacopo Mondi const struct ov772x_win_size **win) 9881112babdSJacopo Mondi { 9891112babdSJacopo Mondi unsigned int i; 9901112babdSJacopo Mondi 9911112babdSJacopo Mondi /* Select a format. */ 9921112babdSJacopo Mondi *cfmt = &ov772x_cfmts[0]; 9931112babdSJacopo Mondi 9941112babdSJacopo Mondi for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { 9951112babdSJacopo Mondi if (mf->code == ov772x_cfmts[i].code) { 9961112babdSJacopo Mondi *cfmt = &ov772x_cfmts[i]; 9971112babdSJacopo Mondi break; 9981112babdSJacopo Mondi } 9991112babdSJacopo Mondi } 10001112babdSJacopo Mondi 10011112babdSJacopo Mondi /* Select a window size. */ 10021112babdSJacopo Mondi *win = ov772x_select_win(mf->width, mf->height); 10031112babdSJacopo Mondi } 10041112babdSJacopo Mondi 1005c2cae895SAkinobu Mita static int ov772x_edgectrl(struct ov772x_priv *priv) 1006c2cae895SAkinobu Mita { 1007c2cae895SAkinobu Mita struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); 1008c2cae895SAkinobu Mita int ret; 1009c2cae895SAkinobu Mita 1010c2cae895SAkinobu Mita if (!priv->info) 1011c2cae895SAkinobu Mita return 0; 1012c2cae895SAkinobu Mita 1013c2cae895SAkinobu Mita if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { 1014c2cae895SAkinobu Mita /* 1015c2cae895SAkinobu Mita * Manual Edge Control Mode. 1016c2cae895SAkinobu Mita * 1017c2cae895SAkinobu Mita * Edge auto strength bit is set by default. 1018c2cae895SAkinobu Mita * Remove it when manual mode. 1019c2cae895SAkinobu Mita */ 1020c2cae895SAkinobu Mita 1021c2cae895SAkinobu Mita ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00); 1022c2cae895SAkinobu Mita if (ret < 0) 1023c2cae895SAkinobu Mita return ret; 1024c2cae895SAkinobu Mita 1025c2cae895SAkinobu Mita ret = ov772x_mask_set(client, 1026c2cae895SAkinobu Mita EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK, 1027c2cae895SAkinobu Mita priv->info->edgectrl.threshold); 1028c2cae895SAkinobu Mita if (ret < 0) 1029c2cae895SAkinobu Mita return ret; 1030c2cae895SAkinobu Mita 1031c2cae895SAkinobu Mita ret = ov772x_mask_set(client, 1032c2cae895SAkinobu Mita EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK, 1033c2cae895SAkinobu Mita priv->info->edgectrl.strength); 1034c2cae895SAkinobu Mita if (ret < 0) 1035c2cae895SAkinobu Mita return ret; 1036c2cae895SAkinobu Mita 1037c2cae895SAkinobu Mita } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { 1038c2cae895SAkinobu Mita /* 1039c2cae895SAkinobu Mita * Auto Edge Control Mode. 1040c2cae895SAkinobu Mita * 1041c2cae895SAkinobu Mita * Set upper and lower limit. 1042c2cae895SAkinobu Mita */ 1043c2cae895SAkinobu Mita ret = ov772x_mask_set(client, 1044c2cae895SAkinobu Mita EDGE_UPPER, OV772X_EDGE_UPPER_MASK, 1045c2cae895SAkinobu Mita priv->info->edgectrl.upper); 1046c2cae895SAkinobu Mita if (ret < 0) 1047c2cae895SAkinobu Mita return ret; 1048c2cae895SAkinobu Mita 1049c2cae895SAkinobu Mita ret = ov772x_mask_set(client, 1050c2cae895SAkinobu Mita EDGE_LOWER, OV772X_EDGE_LOWER_MASK, 1051c2cae895SAkinobu Mita priv->info->edgectrl.lower); 1052c2cae895SAkinobu Mita if (ret < 0) 1053c2cae895SAkinobu Mita return ret; 1054c2cae895SAkinobu Mita } 1055c2cae895SAkinobu Mita 1056c2cae895SAkinobu Mita return 0; 1057c2cae895SAkinobu Mita } 1058c2cae895SAkinobu Mita 10591112babdSJacopo Mondi static int ov772x_set_params(struct ov772x_priv *priv, 10601112babdSJacopo Mondi const struct ov772x_color_format *cfmt, 10611112babdSJacopo Mondi const struct ov772x_win_size *win) 10621112babdSJacopo Mondi { 10631112babdSJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); 10641112babdSJacopo Mondi int ret; 10651112babdSJacopo Mondi u8 val; 10661112babdSJacopo Mondi 10672a2f21e3SJacopo Mondi /* Reset hardware. */ 10681112babdSJacopo Mondi ov772x_reset(client); 10691112babdSJacopo Mondi 10702a2f21e3SJacopo Mondi /* Edge Ctrl. */ 1071c2cae895SAkinobu Mita ret = ov772x_edgectrl(priv); 10721112babdSJacopo Mondi if (ret < 0) 1073c2cae895SAkinobu Mita return ret; 10741112babdSJacopo Mondi 10752a2f21e3SJacopo Mondi /* Format and window size. */ 10761112babdSJacopo Mondi ret = ov772x_write(client, HSTART, win->rect.left >> 2); 10771112babdSJacopo Mondi if (ret < 0) 10781112babdSJacopo Mondi goto ov772x_set_fmt_error; 10791112babdSJacopo Mondi ret = ov772x_write(client, HSIZE, win->rect.width >> 2); 10801112babdSJacopo Mondi if (ret < 0) 10811112babdSJacopo Mondi goto ov772x_set_fmt_error; 10821112babdSJacopo Mondi ret = ov772x_write(client, VSTART, win->rect.top >> 1); 10831112babdSJacopo Mondi if (ret < 0) 10841112babdSJacopo Mondi goto ov772x_set_fmt_error; 10851112babdSJacopo Mondi ret = ov772x_write(client, VSIZE, win->rect.height >> 1); 10861112babdSJacopo Mondi if (ret < 0) 10871112babdSJacopo Mondi goto ov772x_set_fmt_error; 10881112babdSJacopo Mondi ret = ov772x_write(client, HOUTSIZE, win->rect.width >> 2); 10891112babdSJacopo Mondi if (ret < 0) 10901112babdSJacopo Mondi goto ov772x_set_fmt_error; 10911112babdSJacopo Mondi ret = ov772x_write(client, VOUTSIZE, win->rect.height >> 1); 10921112babdSJacopo Mondi if (ret < 0) 10931112babdSJacopo Mondi goto ov772x_set_fmt_error; 10941112babdSJacopo Mondi ret = ov772x_write(client, HREF, 10951112babdSJacopo Mondi ((win->rect.top & 1) << HREF_VSTART_SHIFT) | 10961112babdSJacopo Mondi ((win->rect.left & 3) << HREF_HSTART_SHIFT) | 10971112babdSJacopo Mondi ((win->rect.height & 1) << HREF_VSIZE_SHIFT) | 10981112babdSJacopo Mondi ((win->rect.width & 3) << HREF_HSIZE_SHIFT)); 10991112babdSJacopo Mondi if (ret < 0) 11001112babdSJacopo Mondi goto ov772x_set_fmt_error; 11011112babdSJacopo Mondi ret = ov772x_write(client, EXHCH, 11021112babdSJacopo Mondi ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) | 11031112babdSJacopo Mondi ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT)); 11041112babdSJacopo Mondi if (ret < 0) 11051112babdSJacopo Mondi goto ov772x_set_fmt_error; 11061112babdSJacopo Mondi 11072a2f21e3SJacopo Mondi /* Set DSP_CTRL3. */ 11081112babdSJacopo Mondi val = cfmt->dsp3; 11091112babdSJacopo Mondi if (val) { 11101112babdSJacopo Mondi ret = ov772x_mask_set(client, 11111112babdSJacopo Mondi DSP_CTRL3, UV_MASK, val); 11121112babdSJacopo Mondi if (ret < 0) 11131112babdSJacopo Mondi goto ov772x_set_fmt_error; 11141112babdSJacopo Mondi } 11151112babdSJacopo Mondi 11161112babdSJacopo Mondi /* DSP_CTRL4: AEC reference point and DSP output format. */ 11171112babdSJacopo Mondi if (cfmt->dsp4) { 11181112babdSJacopo Mondi ret = ov772x_write(client, DSP_CTRL4, cfmt->dsp4); 11191112babdSJacopo Mondi if (ret < 0) 11201112babdSJacopo Mondi goto ov772x_set_fmt_error; 11211112babdSJacopo Mondi } 11221112babdSJacopo Mondi 11232a2f21e3SJacopo Mondi /* Set COM3. */ 11241112babdSJacopo Mondi val = cfmt->com3; 1125c2cae895SAkinobu Mita if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) 11261112babdSJacopo Mondi val |= VFLIP_IMG; 1127c2cae895SAkinobu Mita if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) 11281112babdSJacopo Mondi val |= HFLIP_IMG; 112909e620c6SAkinobu Mita if (priv->vflip_ctrl->val) 11301112babdSJacopo Mondi val ^= VFLIP_IMG; 113109e620c6SAkinobu Mita if (priv->hflip_ctrl->val) 11321112babdSJacopo Mondi val ^= HFLIP_IMG; 11331112babdSJacopo Mondi 11341112babdSJacopo Mondi ret = ov772x_mask_set(client, 11351112babdSJacopo Mondi COM3, SWAP_MASK | IMG_MASK, val); 11361112babdSJacopo Mondi if (ret < 0) 11371112babdSJacopo Mondi goto ov772x_set_fmt_error; 11381112babdSJacopo Mondi 11391112babdSJacopo Mondi /* COM7: Sensor resolution and output format control. */ 11401112babdSJacopo Mondi ret = ov772x_write(client, COM7, win->com7_bit | cfmt->com7); 11411112babdSJacopo Mondi if (ret < 0) 11421112babdSJacopo Mondi goto ov772x_set_fmt_error; 11431112babdSJacopo Mondi 114435089491SJacopo Mondi /* COM4, CLKRC: Set pixel clock and framerate. */ 114580dc2a49SAkinobu Mita ret = ov772x_set_frame_rate(priv, priv->fps, cfmt, win); 114635089491SJacopo Mondi if (ret < 0) 114735089491SJacopo Mondi goto ov772x_set_fmt_error; 114835089491SJacopo Mondi 11492a2f21e3SJacopo Mondi /* Set COM8. */ 115009e620c6SAkinobu Mita if (priv->band_filter_ctrl->val) { 115109e620c6SAkinobu Mita unsigned short band_filter = priv->band_filter_ctrl->val; 115209e620c6SAkinobu Mita 1153a024ee14SAkinobu Mita ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); 11541112babdSJacopo Mondi if (!ret) 11551112babdSJacopo Mondi ret = ov772x_mask_set(client, BDBASE, 115609e620c6SAkinobu Mita 0xff, 256 - band_filter); 11571112babdSJacopo Mondi if (ret < 0) 11581112babdSJacopo Mondi goto ov772x_set_fmt_error; 11591112babdSJacopo Mondi } 11601112babdSJacopo Mondi 11611112babdSJacopo Mondi return ret; 11621112babdSJacopo Mondi 11631112babdSJacopo Mondi ov772x_set_fmt_error: 11641112babdSJacopo Mondi 11651112babdSJacopo Mondi ov772x_reset(client); 11661112babdSJacopo Mondi 11671112babdSJacopo Mondi return ret; 11681112babdSJacopo Mondi } 11691112babdSJacopo Mondi 11701112babdSJacopo Mondi static int ov772x_get_selection(struct v4l2_subdev *sd, 11711112babdSJacopo Mondi struct v4l2_subdev_pad_config *cfg, 11721112babdSJacopo Mondi struct v4l2_subdev_selection *sel) 11731112babdSJacopo Mondi { 1174762c2812SJacopo Mondi struct ov772x_priv *priv = to_ov772x(sd); 1175762c2812SJacopo Mondi 11761112babdSJacopo Mondi if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) 11771112babdSJacopo Mondi return -EINVAL; 11781112babdSJacopo Mondi 11791112babdSJacopo Mondi sel->r.left = 0; 11801112babdSJacopo Mondi sel->r.top = 0; 11811112babdSJacopo Mondi switch (sel->target) { 11821112babdSJacopo Mondi case V4L2_SEL_TGT_CROP_BOUNDS: 11831112babdSJacopo Mondi case V4L2_SEL_TGT_CROP_DEFAULT: 11841112babdSJacopo Mondi case V4L2_SEL_TGT_CROP: 1185762c2812SJacopo Mondi sel->r.width = priv->win->rect.width; 1186762c2812SJacopo Mondi sel->r.height = priv->win->rect.height; 11871112babdSJacopo Mondi return 0; 11881112babdSJacopo Mondi default: 11891112babdSJacopo Mondi return -EINVAL; 11901112babdSJacopo Mondi } 11911112babdSJacopo Mondi } 11921112babdSJacopo Mondi 11931112babdSJacopo Mondi static int ov772x_get_fmt(struct v4l2_subdev *sd, 11941112babdSJacopo Mondi struct v4l2_subdev_pad_config *cfg, 11951112babdSJacopo Mondi struct v4l2_subdev_format *format) 11961112babdSJacopo Mondi { 11971112babdSJacopo Mondi struct v4l2_mbus_framefmt *mf = &format->format; 11981112babdSJacopo Mondi struct ov772x_priv *priv = to_ov772x(sd); 11991112babdSJacopo Mondi 12001112babdSJacopo Mondi if (format->pad) 12011112babdSJacopo Mondi return -EINVAL; 12021112babdSJacopo Mondi 12031112babdSJacopo Mondi mf->width = priv->win->rect.width; 12041112babdSJacopo Mondi mf->height = priv->win->rect.height; 12051112babdSJacopo Mondi mf->code = priv->cfmt->code; 12061112babdSJacopo Mondi mf->colorspace = priv->cfmt->colorspace; 12071112babdSJacopo Mondi mf->field = V4L2_FIELD_NONE; 12081112babdSJacopo Mondi 12091112babdSJacopo Mondi return 0; 12101112babdSJacopo Mondi } 12111112babdSJacopo Mondi 12121112babdSJacopo Mondi static int ov772x_set_fmt(struct v4l2_subdev *sd, 12131112babdSJacopo Mondi struct v4l2_subdev_pad_config *cfg, 12141112babdSJacopo Mondi struct v4l2_subdev_format *format) 12151112babdSJacopo Mondi { 12161112babdSJacopo Mondi struct ov772x_priv *priv = to_ov772x(sd); 12171112babdSJacopo Mondi struct v4l2_mbus_framefmt *mf = &format->format; 12181112babdSJacopo Mondi const struct ov772x_color_format *cfmt; 12191112babdSJacopo Mondi const struct ov772x_win_size *win; 122095f5a45aSAkinobu Mita int ret = 0; 12211112babdSJacopo Mondi 12221112babdSJacopo Mondi if (format->pad) 12231112babdSJacopo Mondi return -EINVAL; 12241112babdSJacopo Mondi 12251112babdSJacopo Mondi ov772x_select_params(mf, &cfmt, &win); 12261112babdSJacopo Mondi 12271112babdSJacopo Mondi mf->code = cfmt->code; 12281112babdSJacopo Mondi mf->width = win->rect.width; 12291112babdSJacopo Mondi mf->height = win->rect.height; 12301112babdSJacopo Mondi mf->field = V4L2_FIELD_NONE; 12311112babdSJacopo Mondi mf->colorspace = cfmt->colorspace; 1232762c2812SJacopo Mondi mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 1233762c2812SJacopo Mondi mf->quantization = V4L2_QUANTIZATION_DEFAULT; 1234762c2812SJacopo Mondi mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; 12351112babdSJacopo Mondi 12361112babdSJacopo Mondi if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 12371112babdSJacopo Mondi cfg->try_fmt = *mf; 12381112babdSJacopo Mondi return 0; 12391112babdSJacopo Mondi } 12401112babdSJacopo Mondi 124195f5a45aSAkinobu Mita mutex_lock(&priv->lock); 12427b9998c9SAkinobu Mita 12437b9998c9SAkinobu Mita if (priv->streaming) { 12447b9998c9SAkinobu Mita ret = -EBUSY; 12457b9998c9SAkinobu Mita goto error; 12467b9998c9SAkinobu Mita } 12477b9998c9SAkinobu Mita 124895f5a45aSAkinobu Mita /* 124995f5a45aSAkinobu Mita * If the device is not powered up by the host driver do 125095f5a45aSAkinobu Mita * not apply any changes to H/W at this time. Instead 125195f5a45aSAkinobu Mita * the format will be restored right after power-up. 125295f5a45aSAkinobu Mita */ 125395f5a45aSAkinobu Mita if (priv->power_count > 0) { 12541112babdSJacopo Mondi ret = ov772x_set_params(priv, cfmt, win); 12551112babdSJacopo Mondi if (ret < 0) 125695f5a45aSAkinobu Mita goto error; 125795f5a45aSAkinobu Mita } 12581112babdSJacopo Mondi priv->win = win; 12591112babdSJacopo Mondi priv->cfmt = cfmt; 1260e0853a43SJacopo Mondi 126195f5a45aSAkinobu Mita error: 126295f5a45aSAkinobu Mita mutex_unlock(&priv->lock); 126395f5a45aSAkinobu Mita 126495f5a45aSAkinobu Mita return ret; 12651112babdSJacopo Mondi } 12661112babdSJacopo Mondi 12671112babdSJacopo Mondi static int ov772x_video_probe(struct ov772x_priv *priv) 12681112babdSJacopo Mondi { 12691112babdSJacopo Mondi struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); 127030f3b17eSAkinobu Mita int pid, ver, midh, midl; 12711112babdSJacopo Mondi const char *devname; 12721112babdSJacopo Mondi int ret; 12731112babdSJacopo Mondi 127495f5a45aSAkinobu Mita ret = ov772x_power_on(priv); 12751112babdSJacopo Mondi if (ret < 0) 12761112babdSJacopo Mondi return ret; 12771112babdSJacopo Mondi 12782a2f21e3SJacopo Mondi /* Check and show product ID and manufacturer ID. */ 12791112babdSJacopo Mondi pid = ov772x_read(client, PID); 128030f3b17eSAkinobu Mita if (pid < 0) 128130f3b17eSAkinobu Mita return pid; 12821112babdSJacopo Mondi ver = ov772x_read(client, VER); 128330f3b17eSAkinobu Mita if (ver < 0) 128430f3b17eSAkinobu Mita return ver; 12851112babdSJacopo Mondi 12861112babdSJacopo Mondi switch (VERSION(pid, ver)) { 12871112babdSJacopo Mondi case OV7720: 12881112babdSJacopo Mondi devname = "ov7720"; 12891112babdSJacopo Mondi break; 12901112babdSJacopo Mondi case OV7725: 12911112babdSJacopo Mondi devname = "ov7725"; 12921112babdSJacopo Mondi break; 12931112babdSJacopo Mondi default: 12941112babdSJacopo Mondi dev_err(&client->dev, 12951112babdSJacopo Mondi "Product ID error %x:%x\n", pid, ver); 12961112babdSJacopo Mondi ret = -ENODEV; 12971112babdSJacopo Mondi goto done; 12981112babdSJacopo Mondi } 12991112babdSJacopo Mondi 130030f3b17eSAkinobu Mita midh = ov772x_read(client, MIDH); 130130f3b17eSAkinobu Mita if (midh < 0) 130230f3b17eSAkinobu Mita return midh; 130330f3b17eSAkinobu Mita midl = ov772x_read(client, MIDL); 130430f3b17eSAkinobu Mita if (midl < 0) 130530f3b17eSAkinobu Mita return midl; 130630f3b17eSAkinobu Mita 13071112babdSJacopo Mondi dev_info(&client->dev, 13081112babdSJacopo Mondi "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", 130930f3b17eSAkinobu Mita devname, pid, ver, midh, midl); 131030f3b17eSAkinobu Mita 13111112babdSJacopo Mondi ret = v4l2_ctrl_handler_setup(&priv->hdl); 13121112babdSJacopo Mondi 13131112babdSJacopo Mondi done: 131495f5a45aSAkinobu Mita ov772x_power_off(priv); 1315e0853a43SJacopo Mondi 13161112babdSJacopo Mondi return ret; 13171112babdSJacopo Mondi } 13181112babdSJacopo Mondi 13191112babdSJacopo Mondi static const struct v4l2_ctrl_ops ov772x_ctrl_ops = { 13201112babdSJacopo Mondi .s_ctrl = ov772x_s_ctrl, 13211112babdSJacopo Mondi }; 13221112babdSJacopo Mondi 13231112babdSJacopo Mondi static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { 13241112babdSJacopo Mondi #ifdef CONFIG_VIDEO_ADV_DEBUG 13251112babdSJacopo Mondi .g_register = ov772x_g_register, 13261112babdSJacopo Mondi .s_register = ov772x_s_register, 13271112babdSJacopo Mondi #endif 13281112babdSJacopo Mondi .s_power = ov772x_s_power, 13291112babdSJacopo Mondi }; 13301112babdSJacopo Mondi 133135089491SJacopo Mondi static int ov772x_enum_frame_interval(struct v4l2_subdev *sd, 133235089491SJacopo Mondi struct v4l2_subdev_pad_config *cfg, 133335089491SJacopo Mondi struct v4l2_subdev_frame_interval_enum *fie) 133435089491SJacopo Mondi { 133535089491SJacopo Mondi if (fie->pad || fie->index >= ARRAY_SIZE(ov772x_frame_intervals)) 133635089491SJacopo Mondi return -EINVAL; 133735089491SJacopo Mondi 133835089491SJacopo Mondi if (fie->width != VGA_WIDTH && fie->width != QVGA_WIDTH) 133935089491SJacopo Mondi return -EINVAL; 134035089491SJacopo Mondi if (fie->height != VGA_HEIGHT && fie->height != QVGA_HEIGHT) 134135089491SJacopo Mondi return -EINVAL; 134235089491SJacopo Mondi 134335089491SJacopo Mondi fie->interval.numerator = 1; 134435089491SJacopo Mondi fie->interval.denominator = ov772x_frame_intervals[fie->index]; 134535089491SJacopo Mondi 134635089491SJacopo Mondi return 0; 134735089491SJacopo Mondi } 134835089491SJacopo Mondi 13491112babdSJacopo Mondi static int ov772x_enum_mbus_code(struct v4l2_subdev *sd, 13501112babdSJacopo Mondi struct v4l2_subdev_pad_config *cfg, 13511112babdSJacopo Mondi struct v4l2_subdev_mbus_code_enum *code) 13521112babdSJacopo Mondi { 13531112babdSJacopo Mondi if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts)) 13541112babdSJacopo Mondi return -EINVAL; 13551112babdSJacopo Mondi 13561112babdSJacopo Mondi code->code = ov772x_cfmts[code->index].code; 1357e0853a43SJacopo Mondi 13581112babdSJacopo Mondi return 0; 13591112babdSJacopo Mondi } 13601112babdSJacopo Mondi 13611112babdSJacopo Mondi static const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { 13621112babdSJacopo Mondi .s_stream = ov772x_s_stream, 136335089491SJacopo Mondi .s_frame_interval = ov772x_s_frame_interval, 136435089491SJacopo Mondi .g_frame_interval = ov772x_g_frame_interval, 13651112babdSJacopo Mondi }; 13661112babdSJacopo Mondi 13671112babdSJacopo Mondi static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { 136835089491SJacopo Mondi .enum_frame_interval = ov772x_enum_frame_interval, 13691112babdSJacopo Mondi .enum_mbus_code = ov772x_enum_mbus_code, 13701112babdSJacopo Mondi .get_selection = ov772x_get_selection, 13711112babdSJacopo Mondi .get_fmt = ov772x_get_fmt, 13721112babdSJacopo Mondi .set_fmt = ov772x_set_fmt, 13731112babdSJacopo Mondi }; 13741112babdSJacopo Mondi 13751112babdSJacopo Mondi static const struct v4l2_subdev_ops ov772x_subdev_ops = { 13761112babdSJacopo Mondi .core = &ov772x_subdev_core_ops, 13771112babdSJacopo Mondi .video = &ov772x_subdev_video_ops, 13781112babdSJacopo Mondi .pad = &ov772x_subdev_pad_ops, 13791112babdSJacopo Mondi }; 13801112babdSJacopo Mondi 13811112babdSJacopo Mondi /* 13821112babdSJacopo Mondi * i2c_driver function 13831112babdSJacopo Mondi */ 13841112babdSJacopo Mondi 13851112babdSJacopo Mondi static int ov772x_probe(struct i2c_client *client, 13861112babdSJacopo Mondi const struct i2c_device_id *did) 13871112babdSJacopo Mondi { 13881112babdSJacopo Mondi struct ov772x_priv *priv; 1389762c2812SJacopo Mondi struct i2c_adapter *adapter = client->adapter; 13901112babdSJacopo Mondi int ret; 13911112babdSJacopo Mondi 1392c2cae895SAkinobu Mita if (!client->dev.of_node && !client->dev.platform_data) { 1393c2cae895SAkinobu Mita dev_err(&client->dev, 1394c2cae895SAkinobu Mita "Missing ov772x platform data for non-DT device\n"); 13951112babdSJacopo Mondi return -EINVAL; 13961112babdSJacopo Mondi } 13971112babdSJacopo Mondi 13980b964d18SAkinobu Mita if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { 13991112babdSJacopo Mondi dev_err(&adapter->dev, 14000b964d18SAkinobu Mita "I2C-Adapter doesn't support SMBUS_BYTE_DATA\n"); 14011112babdSJacopo Mondi return -EIO; 14021112babdSJacopo Mondi } 14031112babdSJacopo Mondi 14041112babdSJacopo Mondi priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); 14051112babdSJacopo Mondi if (!priv) 14061112babdSJacopo Mondi return -ENOMEM; 14071112babdSJacopo Mondi 1408762c2812SJacopo Mondi priv->info = client->dev.platform_data; 140934af7d92SAkinobu Mita mutex_init(&priv->lock); 14101112babdSJacopo Mondi 14111112babdSJacopo Mondi v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); 14121112babdSJacopo Mondi v4l2_ctrl_handler_init(&priv->hdl, 3); 141395f5a45aSAkinobu Mita /* Use our mutex for the controls */ 141495f5a45aSAkinobu Mita priv->hdl.lock = &priv->lock; 141509e620c6SAkinobu Mita priv->vflip_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, 14161112babdSJacopo Mondi V4L2_CID_VFLIP, 0, 1, 1, 0); 141709e620c6SAkinobu Mita priv->hflip_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, 14181112babdSJacopo Mondi V4L2_CID_HFLIP, 0, 1, 1, 0); 141909e620c6SAkinobu Mita priv->band_filter_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, 142009e620c6SAkinobu Mita V4L2_CID_BAND_STOP_FILTER, 142109e620c6SAkinobu Mita 0, 256, 1, 0); 14221112babdSJacopo Mondi priv->subdev.ctrl_handler = &priv->hdl; 142334af7d92SAkinobu Mita if (priv->hdl.error) { 142434af7d92SAkinobu Mita ret = priv->hdl.error; 142534af7d92SAkinobu Mita goto error_mutex_destroy; 142634af7d92SAkinobu Mita } 14271112babdSJacopo Mondi 142889ce93fdSAkinobu Mita priv->clk = clk_get(&client->dev, NULL); 14291112babdSJacopo Mondi if (IS_ERR(priv->clk)) { 1430762c2812SJacopo Mondi dev_err(&client->dev, "Unable to get xclk clock\n"); 14311112babdSJacopo Mondi ret = PTR_ERR(priv->clk); 1432762c2812SJacopo Mondi goto error_ctrl_free; 1433762c2812SJacopo Mondi } 1434762c2812SJacopo Mondi 143540519d54SAkinobu Mita priv->pwdn_gpio = gpiod_get_optional(&client->dev, "powerdown", 1436762c2812SJacopo Mondi GPIOD_OUT_LOW); 1437762c2812SJacopo Mondi if (IS_ERR(priv->pwdn_gpio)) { 143840519d54SAkinobu Mita dev_info(&client->dev, "Unable to get GPIO \"powerdown\""); 1439762c2812SJacopo Mondi ret = PTR_ERR(priv->pwdn_gpio); 1440762c2812SJacopo Mondi goto error_clk_put; 14411112babdSJacopo Mondi } 14421112babdSJacopo Mondi 14431112babdSJacopo Mondi ret = ov772x_video_probe(priv); 1444762c2812SJacopo Mondi if (ret < 0) 1445762c2812SJacopo Mondi goto error_gpio_put; 1446762c2812SJacopo Mondi 14474b610d6dSAkinobu Mita #ifdef CONFIG_MEDIA_CONTROLLER 14484b610d6dSAkinobu Mita priv->pad.flags = MEDIA_PAD_FL_SOURCE; 14494b610d6dSAkinobu Mita priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; 14504b610d6dSAkinobu Mita ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); 14514b610d6dSAkinobu Mita if (ret < 0) 14524b610d6dSAkinobu Mita goto error_gpio_put; 14534b610d6dSAkinobu Mita #endif 14544b610d6dSAkinobu Mita 14551112babdSJacopo Mondi priv->cfmt = &ov772x_cfmts[0]; 14561112babdSJacopo Mondi priv->win = &ov772x_win_sizes[0]; 145735089491SJacopo Mondi priv->fps = 15; 1458762c2812SJacopo Mondi 1459762c2812SJacopo Mondi ret = v4l2_async_register_subdev(&priv->subdev); 1460762c2812SJacopo Mondi if (ret) 14614b610d6dSAkinobu Mita goto error_entity_cleanup; 1462762c2812SJacopo Mondi 1463762c2812SJacopo Mondi return 0; 1464762c2812SJacopo Mondi 14654b610d6dSAkinobu Mita error_entity_cleanup: 14664b610d6dSAkinobu Mita media_entity_cleanup(&priv->subdev.entity); 1467762c2812SJacopo Mondi error_gpio_put: 1468762c2812SJacopo Mondi if (priv->pwdn_gpio) 1469762c2812SJacopo Mondi gpiod_put(priv->pwdn_gpio); 1470762c2812SJacopo Mondi error_clk_put: 1471762c2812SJacopo Mondi clk_put(priv->clk); 1472762c2812SJacopo Mondi error_ctrl_free: 1473762c2812SJacopo Mondi v4l2_ctrl_handler_free(&priv->hdl); 147434af7d92SAkinobu Mita error_mutex_destroy: 147534af7d92SAkinobu Mita mutex_destroy(&priv->lock); 14761112babdSJacopo Mondi 14771112babdSJacopo Mondi return ret; 14781112babdSJacopo Mondi } 14791112babdSJacopo Mondi 14801112babdSJacopo Mondi static int ov772x_remove(struct i2c_client *client) 14811112babdSJacopo Mondi { 14821112babdSJacopo Mondi struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client)); 14831112babdSJacopo Mondi 14844b610d6dSAkinobu Mita media_entity_cleanup(&priv->subdev.entity); 1485762c2812SJacopo Mondi clk_put(priv->clk); 1486762c2812SJacopo Mondi if (priv->pwdn_gpio) 1487762c2812SJacopo Mondi gpiod_put(priv->pwdn_gpio); 148827a48feaSJacopo Mondi v4l2_async_unregister_subdev(&priv->subdev); 14891112babdSJacopo Mondi v4l2_ctrl_handler_free(&priv->hdl); 149034af7d92SAkinobu Mita mutex_destroy(&priv->lock); 1491e0853a43SJacopo Mondi 14921112babdSJacopo Mondi return 0; 14931112babdSJacopo Mondi } 14941112babdSJacopo Mondi 14951112babdSJacopo Mondi static const struct i2c_device_id ov772x_id[] = { 14961112babdSJacopo Mondi { "ov772x", 0 }, 14971112babdSJacopo Mondi { } 14981112babdSJacopo Mondi }; 14991112babdSJacopo Mondi MODULE_DEVICE_TABLE(i2c, ov772x_id); 15001112babdSJacopo Mondi 1501c2cae895SAkinobu Mita static const struct of_device_id ov772x_of_match[] = { 1502c2cae895SAkinobu Mita { .compatible = "ovti,ov7725", }, 1503c2cae895SAkinobu Mita { .compatible = "ovti,ov7720", }, 1504c2cae895SAkinobu Mita { /* sentinel */ }, 1505c2cae895SAkinobu Mita }; 1506c2cae895SAkinobu Mita MODULE_DEVICE_TABLE(of, ov772x_of_match); 1507c2cae895SAkinobu Mita 15081112babdSJacopo Mondi static struct i2c_driver ov772x_i2c_driver = { 15091112babdSJacopo Mondi .driver = { 15101112babdSJacopo Mondi .name = "ov772x", 1511c2cae895SAkinobu Mita .of_match_table = ov772x_of_match, 15121112babdSJacopo Mondi }, 15131112babdSJacopo Mondi .probe = ov772x_probe, 15141112babdSJacopo Mondi .remove = ov772x_remove, 15151112babdSJacopo Mondi .id_table = ov772x_id, 15161112babdSJacopo Mondi }; 15171112babdSJacopo Mondi 15181112babdSJacopo Mondi module_i2c_driver(ov772x_i2c_driver); 15191112babdSJacopo Mondi 1520762c2812SJacopo Mondi MODULE_DESCRIPTION("V4L2 driver for OV772x image sensor"); 15211112babdSJacopo Mondi MODULE_AUTHOR("Kuninori Morimoto"); 15221112babdSJacopo Mondi MODULE_LICENSE("GPL v2"); 1523