1111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 2111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * A V4L2 driver for OmniVision OV7670 cameras. 3111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * 4111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Copyright 2006 One Laptop Per Child Association, Inc. Written 5111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * by Jonathan Corbet with substantial inspiration from Mark 6111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * McClelland's ovcamchip code. 7111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * 877d5140fe78ca628bfc9d2567c35457481f9c63dJonathan Corbet * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> 977d5140fe78ca628bfc9d2567c35457481f9c63dJonathan Corbet * 10111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * This file may be distributed under the terms of the GNU General 11111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Public License, version 2. 12111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 13111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#include <linux/init.h> 14111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#include <linux/module.h> 155a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 1614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil#include <linux/i2c.h> 17111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#include <linux/delay.h> 187e0a16f6118a297dd467c1e5a0908429fcdf56afMauro Carvalho Chehab#include <linux/videodev2.h> 1914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil#include <media/v4l2-device.h> 20492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin#include <media/v4l2-ctrls.h> 21959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil#include <media/v4l2-mediabus.h> 224721b3eb662ca5ea60a636f0f190f2fd2ac5df14Axel Lin#include <media/v4l2-image-sizes.h> 23f8fc729870ee82662ae6e3a713d59b2fbf3b04c6Jonathan Corbet#include <media/ov7670.h> 24111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 255e614475decfcc852d2c8c50702f81ee34901fe2Dave JonesMODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); 26111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan CorbetMODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); 27111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan CorbetMODULE_LICENSE("GPL"); 28111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 2990ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool debug; 3014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilmodule_param(debug, bool, 0644); 3114386c2b7793652a656021a3345cff3b0f6771f9Hans VerkuilMODULE_PARM_DESC(debug, "Debug level (0-1)"); 3214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil 33111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 34111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * The 7670 sits on i2c with ID 0x42 35111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 36111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define OV7670_I2C_ADDR 0x42 37111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 38f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define PLL_FACTOR 4 39f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 40111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* Registers */ 41111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ 42111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_BLUE 0x01 /* blue gain */ 43111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_RED 0x02 /* red gain */ 44111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ 45111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM1 0x04 /* Control 1 */ 46111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM1_CCIR656 0x40 /* CCIR656 enable */ 47111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_BAVE 0x05 /* U/B Average level */ 48111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_GbAVE 0x06 /* Y/Gb Average level */ 49111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_AECHH 0x07 /* AEC MS 5 bits */ 50111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_RAVE 0x08 /* V/R Average level */ 51111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM2 0x09 /* Control 2 */ 52111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM2_SSLEEP 0x10 /* Soft sleep mode */ 53111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_PID 0x0a /* Product ID MSB */ 54111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_VER 0x0b /* Product ID LSB */ 55111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM3 0x0c /* Control 3 */ 56111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM3_SWAP 0x40 /* Byte swap */ 57111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM3_SCALEEN 0x08 /* Enable scaling */ 58111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ 59111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM4 0x0d /* Control 4 */ 60111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM5 0x0e /* All "reserved" */ 61111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM6 0x0f /* Control 6 */ 62111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_AECH 0x10 /* More bits of AEC value */ 63111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_CLKRC 0x11 /* Clocl control */ 64111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define CLK_EXT 0x40 /* Use external clock directly */ 65111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define CLK_SCALE 0x3f /* Mask for internal clock scale */ 66111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM7 0x12 /* Control 7 */ 67111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM7_RESET 0x80 /* Register reset */ 68111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM7_FMT_MASK 0x38 69111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM7_FMT_VGA 0x00 70111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM7_FMT_CIF 0x20 /* CIF format */ 71111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM7_FMT_QVGA 0x10 /* QVGA format */ 72111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM7_FMT_QCIF 0x08 /* QCIF format */ 73111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ 74111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM7_YUV 0x00 /* YUV */ 75111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM7_BAYER 0x01 /* Bayer format */ 76111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM7_PBAYER 0x05 /* "Processed bayer" */ 77111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM8 0x13 /* Control 8 */ 78111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ 79111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ 80111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM8_BFILT 0x20 /* Band filter enable */ 81111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM8_AGC 0x04 /* Auto gain enable */ 82111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM8_AWB 0x02 /* White balance enable */ 83111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM8_AEC 0x01 /* Auto exposure enable */ 84111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM9 0x14 /* Control 9 - gain ceiling */ 85111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM10 0x15 /* Control 10 */ 86111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ 87111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ 88111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM10_HREF_REV 0x08 /* Reverse HREF */ 89111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ 90111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM10_VS_NEG 0x02 /* VSYNC negative */ 91111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM10_HS_NEG 0x01 /* HSYNC negative */ 92111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HSTART 0x17 /* Horiz start high bits */ 93111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HSTOP 0x18 /* Horiz stop high bits */ 94111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_VSTART 0x19 /* Vert start high bits */ 95111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_VSTOP 0x1a /* Vert stop high bits */ 96111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_PSHFT 0x1b /* Pixel delay after HREF */ 97111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_MIDH 0x1c /* Manuf. ID high */ 98111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_MIDL 0x1d /* Manuf. ID low */ 99111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_MVFP 0x1e /* Mirror / vflip */ 100111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define MVFP_MIRROR 0x20 /* Mirror image */ 101111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define MVFP_FLIP 0x10 /* Vertical flip */ 102111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 103111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_AEW 0x24 /* AGC upper limit */ 104111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_AEB 0x25 /* AGC lower limit */ 105111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ 106111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HSYST 0x30 /* HSYNC rising edge delay */ 107111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ 108111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HREF 0x32 /* HREF pieces */ 109111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_TSLB 0x3a /* lots of stuff */ 110111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ 111111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM11 0x3b /* Control 11 */ 112111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM11_NIGHT 0x80 /* NIght mode enable */ 113111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM11_NMFR 0x60 /* Two bit NM frame rate */ 114111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ 115111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM11_50HZ 0x08 /* Manual 50Hz select */ 116111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM11_EXP 0x02 117111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM12 0x3c /* Control 12 */ 118111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM12_HREF 0x80 /* HREF always */ 119111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM13 0x3d /* Control 13 */ 120111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM13_GAMMA 0x80 /* Gamma enable */ 121111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ 122111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ 123111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM14 0x3e /* Control 14 */ 124111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ 125111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_EDGE 0x3f /* Edge enhancement factor */ 126111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM15 0x40 /* Control 15 */ 127111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM15_R10F0 0x00 /* Data range 10 to F0 */ 128111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM15_R01FE 0x80 /* 01 to FE */ 129111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM15_R00FF 0xc0 /* 00 to FF */ 130111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM15_RGB565 0x10 /* RGB565 output */ 131111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM15_RGB555 0x30 /* RGB555 output */ 132111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM16 0x41 /* Control 16 */ 133111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM16_AWBGAIN 0x08 /* AWB gain enable */ 134111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM17 0x42 /* Control 17 */ 135111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ 136111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define COM17_CBAR 0x08 /* DSP Color bar */ 137111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 138f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet/* 139f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * This matrix defines how the colors are generated, must be 140f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * tweaked to adjust hue and saturation. 141f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * 142f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Order: v-red, v-green, v-blue, u-red, u-green, u-blue 143f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * 144f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * They are nine-bit signed quantities, with the sign bit 145f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * stored in 0x58. Sign for v-red is bit 0, and up from there. 146f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */ 147f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet#define REG_CMATRIX_BASE 0x4f 148f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet#define CMATRIX_LEN 6 149f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet#define REG_CMATRIX_SIGN 0x58 150f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 151f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 152111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_BRIGHT 0x55 /* Brightness */ 153111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_CONTRAS 0x56 /* Contrast control */ 154111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 155111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_GFIX 0x69 /* Fix gain control */ 156111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 157f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define REG_DBLV 0x6b /* PLL control an debugging */ 158f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define DBLV_BYPASS 0x00 /* Bypass PLL */ 159f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define DBLV_X4 0x01 /* clock x4 */ 160f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define DBLV_X6 0x10 /* clock x6 */ 161f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define DBLV_X8 0x11 /* clock x8 */ 162f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 163585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet#define REG_REG76 0x76 /* OV's name */ 164585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ 165585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet#define R76_WHTPCOR 0x40 /* White pixel correction enable */ 166585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet 167111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_RGB444 0x8c /* RGB 444 control */ 168111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ 169111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define R444_RGBX 0x01 /* Empty nibble at end */ 170111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 171111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ 172111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ 173111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 174111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ 175111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ 176111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ 177111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ 178111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ 179111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ 180111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_BD60MAX 0xab /* 60hz banding step limit */ 181111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 182d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinenum ov7670_model { 183d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin MODEL_OV7670 = 0, 184d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin MODEL_OV7675, 185d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin}; 186d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin 187d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinstruct ov7670_win_size { 188d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin int width; 189d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin int height; 190d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin unsigned char com7_bit; 191d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin int hstart; /* Start/stop values for the camera. Note */ 192d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin int hstop; /* that they do not always make complete */ 193d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin int vstart; /* sense to humans, but evidently the sensor */ 194d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin int vstop; /* will do the right thing... */ 195d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin struct regval_list *regs; /* Regs to tweak */ 196d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin}; 197d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin 198d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinstruct ov7670_devtype { 199d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin /* formats supported for each model */ 200d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin struct ov7670_win_size *win_sizes; 201d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin unsigned int n_win_sizes; 202f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin /* callbacks for frame rate control */ 203f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin int (*set_framerate)(struct v4l2_subdev *, struct v4l2_fract *); 204f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin void (*get_framerate)(struct v4l2_subdev *, struct v4l2_fract *); 205d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin}; 206111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 207111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 208f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Information we maintain about a known sensor. 209f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */ 210f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstruct ov7670_format_struct; /* coming later */ 211f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstruct ov7670_info { 21214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil struct v4l2_subdev sd; 213492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct v4l2_ctrl_handler hdl; 214492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct { 215492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin /* gain cluster */ 216492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct v4l2_ctrl *auto_gain; 217492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct v4l2_ctrl *gain; 218492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin }; 219492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct { 220492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin /* exposure cluster */ 221492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct v4l2_ctrl *auto_exposure; 222492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct v4l2_ctrl *exposure; 223492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin }; 224492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct { 225492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin /* saturation/hue cluster */ 226492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct v4l2_ctrl *saturation; 227492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct v4l2_ctrl *hue; 228492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin }; 229f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet struct ov7670_format_struct *fmt; /* Current format */ 23075e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake int min_width; /* Filter out smaller sizes */ 23175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake int min_height; /* Filter out smaller sizes */ 23275e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake int clock_speed; /* External clock speed (MHz) */ 233d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet u8 clkrc; /* Clock divider value */ 23475e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake bool use_smbus; /* Use smbus I/O instead of I2C */ 23504ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin bool pll_bypass; 236ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin bool pclk_hb_disable; 237d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin const struct ov7670_devtype *devtype; /* Device specifics */ 238f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet}; 239f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 24014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic inline struct ov7670_info *to_state(struct v4l2_subdev *sd) 24114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil{ 24214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil return container_of(sd, struct ov7670_info, sd); 24314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil} 244f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 245492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martinstatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 246492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin{ 247492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return &container_of(ctrl->handler, struct ov7670_info, hdl)->sd; 248492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin} 249492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin 250f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 251f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 252f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet/* 253111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * The default register settings, as obtained from OmniVision. There 254111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * is really no making sense of most of these - lots of "reserved" values 255111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * and such. 256111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * 257111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * These settings give VGA YUYV. 258111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 259111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 260111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstruct regval_list { 261111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet unsigned char reg_num; 262111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet unsigned char value; 263111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}; 264111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 265111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic struct regval_list ov7670_default_regs[] = { 266111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM7, COM7_RESET }, 267111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 268111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Clock scale: 3 = 15fps 269111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * 2 = 20fps 270111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * 1 = 30fps 271111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 272f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ 273111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_TSLB, 0x04 }, /* OV */ 274111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM7, 0 }, /* VGA */ 275111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* 276111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Set the hardware window. These values from OV don't entirely 277111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * make sense - hstop is less than hstart. But they work... 278111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 279111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, 280111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, 281111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, 282111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 283111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM3, 0 }, { REG_COM14, 0 }, 284111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* Mystery scaling numbers */ 285111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x70, 0x3a }, { 0x71, 0x35 }, 286111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x72, 0x11 }, { 0x73, 0xf0 }, 287111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0xa2, 0x02 }, { REG_COM10, 0x0 }, 288111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 289111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* Gamma curve values */ 290111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x7a, 0x20 }, { 0x7b, 0x10 }, 291111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x7c, 0x1e }, { 0x7d, 0x35 }, 292111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x7e, 0x5a }, { 0x7f, 0x69 }, 293111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x80, 0x76 }, { 0x81, 0x80 }, 294111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x82, 0x88 }, { 0x83, 0x8f }, 295111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x84, 0x96 }, { 0x85, 0xa3 }, 296111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x86, 0xaf }, { 0x87, 0xc4 }, 297111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x88, 0xd7 }, { 0x89, 0xe8 }, 298111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 299111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* AGC and AEC parameters. Note we start by disabling those features, 300111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet then turn them only after tweaking the values. */ 301111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, 302111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_GAIN, 0 }, { REG_AECH, 0 }, 303111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM4, 0x40 }, /* magic reserved bit */ 304111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ 305111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, 306111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, 307111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, 308111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ 309111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, 310111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, 311111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_HAECC7, 0x94 }, 312111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, 313111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 314111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* Almost all of these are magic "reserved" values. */ 315111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, 3167f7b12f09be8e396d159800baeaf922df46e25cfJonathan Corbet { 0x16, 0x02 }, { REG_MVFP, 0x07 }, 317111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x21, 0x02 }, { 0x22, 0x91 }, 318111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x29, 0x07 }, { 0x33, 0x0b }, 319111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x35, 0x0b }, { 0x37, 0x1d }, 320111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x38, 0x71 }, { 0x39, 0x2a }, 321111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM12, 0x78 }, { 0x4d, 0x40 }, 322111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x4e, 0x20 }, { REG_GFIX, 0 }, 323111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x6b, 0x4a }, { 0x74, 0x10 }, 324111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x8d, 0x4f }, { 0x8e, 0 }, 325111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x8f, 0 }, { 0x90, 0 }, 326111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x91, 0 }, { 0x96, 0 }, 327111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x9a, 0 }, { 0xb0, 0x84 }, 328111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0xb1, 0x0c }, { 0xb2, 0x0e }, 329111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0xb3, 0x82 }, { 0xb8, 0x0a }, 330111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 331111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* More reserved magic, some of which tweaks white balance */ 332111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x43, 0x0a }, { 0x44, 0xf0 }, 333111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x45, 0x34 }, { 0x46, 0x58 }, 334111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x47, 0x28 }, { 0x48, 0x3a }, 335111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x59, 0x88 }, { 0x5a, 0x88 }, 336111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x5b, 0x44 }, { 0x5c, 0x67 }, 337111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x5d, 0x49 }, { 0x5e, 0x0e }, 338111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x6c, 0x0a }, { 0x6d, 0x55 }, 339111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ 340111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, 341111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_RED, 0x60 }, 342111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, 343111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 344111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* Matrix coefficients */ 345111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x4f, 0x80 }, { 0x50, 0x80 }, 346111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x51, 0 }, { 0x52, 0x22 }, 347111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x53, 0x5e }, { 0x54, 0x80 }, 348111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x58, 0x9e }, 349111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 350111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, 351111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x75, 0x05 }, { 0x76, 0xe1 }, 352111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x4c, 0 }, { 0x77, 0x01 }, 353111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, 354111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0xc9, 0x60 }, { REG_COM16, 0x38 }, 355111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x56, 0x40 }, 356111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 357c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, 358111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0xa4, 0x88 }, { 0x96, 0 }, 359111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x97, 0x30 }, { 0x98, 0x20 }, 360111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x99, 0x30 }, { 0x9a, 0x84 }, 361111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x9b, 0x29 }, { 0x9c, 0x03 }, 362111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x9d, 0x4c }, { 0x9e, 0x3f }, 363111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x78, 0x04 }, 364111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 365111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* Extra-weird stuff. Some sort of multiplexor register */ 366111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x01 }, { 0xc8, 0xf0 }, 367111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x0f }, { 0xc8, 0x00 }, 368111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x10 }, { 0xc8, 0x7e }, 369111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x0a }, { 0xc8, 0x80 }, 370111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x0b }, { 0xc8, 0x01 }, 371111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x0c }, { 0xc8, 0x0f }, 372111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x0d }, { 0xc8, 0x20 }, 373111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x09 }, { 0xc8, 0x80 }, 374111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x02 }, { 0xc8, 0xc0 }, 375111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x03 }, { 0xc8, 0x40 }, 376111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x05 }, { 0xc8, 0x30 }, 377111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x79, 0x26 }, 378111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 379111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0xff, 0xff }, /* END MARKER */ 380111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}; 381111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 382111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 383111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 384111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Here we'll try to encapsulate the changes for just the output 385111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * video format. 386111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * 387111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * RGB656 and YUV422 come from OV; RGB444 is homebrewed. 388111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * 389111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why. 390111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 391111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 392111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 393111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic struct regval_list ov7670_fmt_yuv422[] = { 394111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM7, 0x0 }, /* Selects YUV mode */ 395111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_RGB444, 0 }, /* No RGB444 please */ 39697693f9178ba83068f6b4e419a47ffd3d1a20897Jonathan Corbet { REG_COM1, 0 }, /* CCIR601 */ 397111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM15, COM15_R00FF }, 398c01b7429e2b713a3d039cd2b1a94dc1b97cdf321Javier Martin { REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */ 399111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x4f, 0x80 }, /* "matrix coefficient 1" */ 400111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x50, 0x80 }, /* "matrix coefficient 2" */ 401f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x51, 0 }, /* vb */ 402111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x52, 0x22 }, /* "matrix coefficient 4" */ 403111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x53, 0x5e }, /* "matrix coefficient 5" */ 404111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x54, 0x80 }, /* "matrix coefficient 6" */ 405111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM13, COM13_GAMMA|COM13_UVSAT }, 406111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0xff, 0xff }, 407111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}; 408111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 409111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic struct regval_list ov7670_fmt_rgb565[] = { 410111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM7, COM7_RGB }, /* Selects RGB mode */ 411111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_RGB444, 0 }, /* No RGB444 please */ 41297693f9178ba83068f6b4e419a47ffd3d1a20897Jonathan Corbet { REG_COM1, 0x0 }, /* CCIR601 */ 413111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM15, COM15_RGB565 }, 414111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ 415111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ 416111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x50, 0xb3 }, /* "matrix coefficient 2" */ 417f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x51, 0 }, /* vb */ 418111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x52, 0x3d }, /* "matrix coefficient 4" */ 419111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x53, 0xa7 }, /* "matrix coefficient 5" */ 420111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x54, 0xe4 }, /* "matrix coefficient 6" */ 421111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM13, COM13_GAMMA|COM13_UVSAT }, 422111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0xff, 0xff }, 423111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}; 424111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 425111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic struct regval_list ov7670_fmt_rgb444[] = { 426111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM7, COM7_RGB }, /* Selects RGB mode */ 427111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ 42897693f9178ba83068f6b4e419a47ffd3d1a20897Jonathan Corbet { REG_COM1, 0x0 }, /* CCIR601 */ 429111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ 430111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ 431111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ 432111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x50, 0xb3 }, /* "matrix coefficient 2" */ 433f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x51, 0 }, /* vb */ 434111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x52, 0x3d }, /* "matrix coefficient 4" */ 435111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x53, 0xa7 }, /* "matrix coefficient 5" */ 436111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0x54, 0xe4 }, /* "matrix coefficient 6" */ 437111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ 438111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 0xff, 0xff }, 439111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}; 440111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 441585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbetstatic struct regval_list ov7670_fmt_raw[] = { 442585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet { REG_COM7, COM7_BAYER }, 443585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ 444585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ 445585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ 446585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet { 0xff, 0xff }, 447585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet}; 448111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 449111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 450111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 451111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 452111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Low-level register I/O. 453467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * 454467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * Note that there are two versions of these. On the XO 1, the 455467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * i2c controller only does SMBUS, so that's what we use. The 456467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * ov7670 is not really an SMBUS device, though, so the communication 457467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * is not always entirely reliable. 458467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet */ 45975e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, 460467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet unsigned char *value) 461467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet{ 462467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet struct i2c_client *client = v4l2_get_subdevdata(sd); 463467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet int ret; 464467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet 465467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet ret = i2c_smbus_read_byte_data(client, reg); 466467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet if (ret >= 0) { 467467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet *value = (unsigned char)ret; 468467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet ret = 0; 469467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet } 470467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet return ret; 471467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet} 472467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet 473467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet 47475e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, 475467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet unsigned char value) 476467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet{ 477467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet struct i2c_client *client = v4l2_get_subdevdata(sd); 478467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet int ret = i2c_smbus_write_byte_data(client, reg, value); 479467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet 480467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet if (reg == REG_COM7 && (value & COM7_RESET)) 481467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet msleep(5); /* Wait for reset to run */ 482467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet return ret; 483467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet} 484467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet 485467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet/* 486467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * On most platforms, we'd rather do straight i2c I/O. 487111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 48875e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, 489111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet unsigned char *value) 490111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 49114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil struct i2c_client *client = v4l2_get_subdevdata(sd); 4922bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet u8 data = reg; 4932bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet struct i2c_msg msg; 494111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet int ret; 495111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 4962bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet /* 4972bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet * Send out the register address... 4982bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet */ 4992bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet msg.addr = client->addr; 5002bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet msg.flags = 0; 5012bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet msg.len = 1; 5022bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet msg.buf = &data; 5032bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet ret = i2c_transfer(client->adapter, &msg, 1); 5042bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet if (ret < 0) { 5052bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet printk(KERN_ERR "Error %d on register write\n", ret); 5062bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet return ret; 5072bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet } 5082bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet /* 5092bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet * ...then read back the result. 5102bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet */ 5112bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet msg.flags = I2C_M_RD; 5122bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet ret = i2c_transfer(client->adapter, &msg, 1); 513bca5c2c550f16d2dc2d21ffb7b4712bd0a7d32a9Andres Salomon if (ret >= 0) { 5142bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet *value = data; 515bca5c2c550f16d2dc2d21ffb7b4712bd0a7d32a9Andres Salomon ret = 0; 516bca5c2c550f16d2dc2d21ffb7b4712bd0a7d32a9Andres Salomon } 517111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 518111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 519111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 520111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 52175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, 522111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet unsigned char value) 523111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 52414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil struct i2c_client *client = v4l2_get_subdevdata(sd); 5252bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet struct i2c_msg msg; 5262bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet unsigned char data[2] = { reg, value }; 5272bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet int ret; 52814386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil 5292bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet msg.addr = client->addr; 5302bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet msg.flags = 0; 5312bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet msg.len = 2; 5322bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet msg.buf = data; 5332bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet ret = i2c_transfer(client->adapter, &msg, 1); 5342bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet if (ret > 0) 5352bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet ret = 0; 5366d77444aca298b43a88086be446f943cd0442ef7Jonathan Corbet if (reg == REG_COM7 && (value & COM7_RESET)) 53797693f9178ba83068f6b4e419a47ffd3d1a20897Jonathan Corbet msleep(5); /* Wait for reset to run */ 5386d77444aca298b43a88086be446f943cd0442ef7Jonathan Corbet return ret; 539111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 540111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 54175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, 54275e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake unsigned char *value) 54375e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake{ 54475e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake struct ov7670_info *info = to_state(sd); 54575e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake if (info->use_smbus) 54675e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake return ov7670_read_smbus(sd, reg, value); 54775e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake else 54875e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake return ov7670_read_i2c(sd, reg, value); 54975e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake} 55075e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake 55175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, 55275e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake unsigned char value) 55375e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake{ 55475e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake struct ov7670_info *info = to_state(sd); 55575e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake if (info->use_smbus) 55675e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake return ov7670_write_smbus(sd, reg, value); 55775e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake else 55875e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake return ov7670_write_i2c(sd, reg, value); 55975e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake} 560111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 561111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 562111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Write a list of register settings; ff/ff stops the process. 563111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 56414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) 565111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 566111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet while (vals->reg_num != 0xff || vals->value != 0xff) { 56714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil int ret = ov7670_write(sd, vals->reg_num, vals->value); 568111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (ret < 0) 569111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 570111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet vals++; 571111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet } 572111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return 0; 573111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 574111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 575111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 576111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 577111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Stuff that knows about the sensor. 578111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 57914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_reset(struct v4l2_subdev *sd, u32 val) 580111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 58114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ov7670_write(sd, REG_COM7, COM7_RESET); 582111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet msleep(1); 58314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil return 0; 584111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 585111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 586111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 58714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_init(struct v4l2_subdev *sd, u32 val) 588111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 58914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil return ov7670_write_array(sd, ov7670_default_regs); 590111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 591111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 592111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 593111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 59414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_detect(struct v4l2_subdev *sd) 595111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 596111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet unsigned char v; 597111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet int ret; 598111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 59914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_init(sd, 0); 600111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (ret < 0) 601111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 60214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_read(sd, REG_MIDH, &v); 603111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (ret < 0) 604111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 605111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (v != 0x7f) /* OV manuf. id. */ 606111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return -ENODEV; 60714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_read(sd, REG_MIDL, &v); 608111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (ret < 0) 609111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 610111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (v != 0xa2) 611111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return -ENODEV; 612111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* 613111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * OK, we know we have an OmniVision chip...but which one? 614111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 61514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_read(sd, REG_PID, &v); 616111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (ret < 0) 617111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 618111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ 619111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return -ENODEV; 62014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_read(sd, REG_VER, &v); 621111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (ret < 0) 622111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 623111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ 624111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return -ENODEV; 625111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return 0; 626111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 627111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 628111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 629f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet/* 630f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Store information about the video data format. The color matrix 631f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * is deeply tied into the format, so keep the relevant values here. 632959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil * The magic matrix numbers come from OmniVision. 633f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */ 634111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic struct ov7670_format_struct { 635959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil enum v4l2_mbus_pixelcode mbus_code; 636959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil enum v4l2_colorspace colorspace; 637111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet struct regval_list *regs; 638f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet int cmatrix[CMATRIX_LEN]; 639111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} ov7670_formats[] = { 640111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 641959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, 642959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .colorspace = V4L2_COLORSPACE_JPEG, 643111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .regs = ov7670_fmt_yuv422, 644f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet .cmatrix = { 128, -128, 0, -34, -94, 128 }, 645111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet }, 646111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 647959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, 648959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .colorspace = V4L2_COLORSPACE_SRGB, 649111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .regs = ov7670_fmt_rgb444, 650f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet .cmatrix = { 179, -179, 0, -61, -176, 228 }, 651111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet }, 652111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 653959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, 654959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .colorspace = V4L2_COLORSPACE_SRGB, 655111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .regs = ov7670_fmt_rgb565, 656f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet .cmatrix = { 179, -179, 0, -61, -176, 228 }, 657585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet }, 658585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet { 659959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8, 660959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .colorspace = V4L2_COLORSPACE_SRGB, 661585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet .regs = ov7670_fmt_raw, 662585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet .cmatrix = { 0, 0, 0, 0, 0, 0 }, 663111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet }, 664111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}; 665585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) 666111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 667111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 668111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 669111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Then there is the issue of window sizes. Try to capture the info here. 670111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 671f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 672f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet/* 673f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * QCIF mode is done (by OV) in a very strange way - it actually looks like 674f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * VGA with weird scaling options - they do *not* use the canned QCIF mode 675f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * which is allegedly provided by the sensor. So here's the weird register 676f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * settings. 677f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */ 678f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstatic struct regval_list ov7670_qcif_regs[] = { 679f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, 680f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { REG_COM3, COM3_DCWEN }, 681f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { REG_COM14, COM14_DCWEN | 0x01}, 682f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x73, 0xf1 }, 683f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0xa2, 0x52 }, 684f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x7b, 0x1c }, 685f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x7c, 0x28 }, 686f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x7d, 0x3c }, 687f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x7f, 0x69 }, 688f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { REG_COM9, 0x38 }, 689f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0xa1, 0x0b }, 690f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x74, 0x19 }, 691f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x9a, 0x80 }, 692f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0x43, 0x14 }, 693f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { REG_COM13, 0xc0 }, 694f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 0xff, 0xff }, 695f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet}; 696f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 697d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinstatic struct ov7670_win_size ov7670_win_sizes[] = { 698111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* VGA */ 699111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 700111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .width = VGA_WIDTH, 701111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .height = VGA_HEIGHT, 702111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .com7_bit = COM7_FMT_VGA, 703d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .hstart = 158, /* These values from */ 704d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .hstop = 14, /* Omnivision */ 705111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .vstart = 10, 706111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .vstop = 490, 707d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .regs = NULL, 708111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet }, 709111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* CIF */ 710111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 711111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .width = CIF_WIDTH, 712111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .height = CIF_HEIGHT, 713111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .com7_bit = COM7_FMT_CIF, 714d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .hstart = 170, /* Empirically determined */ 715111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .hstop = 90, 716111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .vstart = 14, 717111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .vstop = 494, 718d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .regs = NULL, 719111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet }, 720111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* QVGA */ 721111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet { 722111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .width = QVGA_WIDTH, 723111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .height = QVGA_HEIGHT, 724111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet .com7_bit = COM7_FMT_QVGA, 725d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .hstart = 168, /* Empirically determined */ 726dc4589c814a3a50a4cfc2077690fc7fd397308c8Daniel Drake .hstop = 24, 727dc4589c814a3a50a4cfc2077690fc7fd397308c8Daniel Drake .vstart = 12, 728dc4589c814a3a50a4cfc2077690fc7fd397308c8Daniel Drake .vstop = 492, 729d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .regs = NULL, 730f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet }, 731f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet /* QCIF */ 732f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet { 733f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet .width = QCIF_WIDTH, 734f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet .height = QCIF_HEIGHT, 735f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet .com7_bit = COM7_FMT_VGA, /* see comment above */ 736d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .hstart = 456, /* Empirically determined */ 737f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet .hstop = 24, 738f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet .vstart = 14, 739f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet .vstop = 494, 740d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .regs = ov7670_qcif_regs, 741d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin } 742111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}; 743111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 744d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinstatic struct ov7670_win_size ov7675_win_sizes[] = { 745d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin /* 746d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin * Currently, only VGA is supported. Theoretically it could be possible 747d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin * to support CIF, QVGA and QCIF too. Taking values for ov7670 as a 748d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin * base and tweak them empirically could be required. 749d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin */ 750d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin { 751d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .width = VGA_WIDTH, 752d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .height = VGA_HEIGHT, 753d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .com7_bit = COM7_FMT_VGA, 754d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .hstart = 158, /* These values from */ 755d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .hstop = 14, /* Omnivision */ 756d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .vstart = 14, /* Empirically determined */ 757d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .vstop = 494, 758d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .regs = NULL, 759d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin } 760d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin}; 761111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 762f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martinstatic void ov7675_get_framerate(struct v4l2_subdev *sd, 763f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin struct v4l2_fract *tpf) 764f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin{ 765f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin struct ov7670_info *info = to_state(sd); 766f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin u32 clkrc = info->clkrc; 76704ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin int pll_factor; 76804ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin 76904ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin if (info->pll_bypass) 77004ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin pll_factor = 1; 77104ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin else 77204ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin pll_factor = PLL_FACTOR; 773f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 774f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin clkrc++; 775f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8) 776f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin clkrc = (clkrc >> 1); 777f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 778f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin tpf->numerator = 1; 779f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin tpf->denominator = (5 * pll_factor * info->clock_speed) / 780f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin (4 * clkrc); 781f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin} 782f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 783f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martinstatic int ov7675_set_framerate(struct v4l2_subdev *sd, 784f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin struct v4l2_fract *tpf) 785f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin{ 786f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin struct ov7670_info *info = to_state(sd); 787f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin u32 clkrc; 78804ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin int pll_factor; 789f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin int ret; 790f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 791f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin /* 792f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin * The formula is fps = 5/4*pixclk for YUV/RGB and 793f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin * fps = 5/2*pixclk for RAW. 794f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin * 795f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin * pixclk = clock_speed / (clkrc + 1) * PLLfactor 796f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin * 797f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin */ 79804ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin if (info->pll_bypass) { 79904ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin pll_factor = 1; 80004ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS); 80104ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin } else { 80204ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin pll_factor = PLL_FACTOR; 80304ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin ret = ov7670_write(sd, REG_DBLV, DBLV_X4); 80404ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin } 80504ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin if (ret < 0) 80604ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin return ret; 80704ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin 808f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin if (tpf->numerator == 0 || tpf->denominator == 0) { 809f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin clkrc = 0; 810f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin } else { 811f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) / 812f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin (4 * tpf->denominator); 813f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8) 814f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin clkrc = (clkrc << 1); 815f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin clkrc--; 816f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin } 817f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 818f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin /* 819f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin * The datasheet claims that clkrc = 0 will divide the input clock by 1 820f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin * but we've checked with an oscilloscope that it divides by 2 instead. 821f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin * So, if clkrc = 0 just bypass the divider. 822f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin */ 823f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin if (clkrc <= 0) 824f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin clkrc = CLK_EXT; 825f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin else if (clkrc > CLK_SCALE) 826f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin clkrc = CLK_SCALE; 827f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin info->clkrc = clkrc; 828f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 829f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin /* Recalculate frame rate */ 830f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin ov7675_get_framerate(sd, tpf); 831f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 832f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin ret = ov7670_write(sd, REG_CLKRC, info->clkrc); 833f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin if (ret < 0) 834f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin return ret; 83504ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin 836f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin return ov7670_write(sd, REG_DBLV, DBLV_X4); 837f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin} 838f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 839f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martinstatic void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, 840f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin struct v4l2_fract *tpf) 841f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin{ 842f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin struct ov7670_info *info = to_state(sd); 843f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 844f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin tpf->numerator = 1; 845f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin tpf->denominator = info->clock_speed; 846f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) 847f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin tpf->denominator /= (info->clkrc & CLK_SCALE); 848f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin} 849f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 850f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martinstatic int ov7670_set_framerate_legacy(struct v4l2_subdev *sd, 851f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin struct v4l2_fract *tpf) 852f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin{ 853f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin struct ov7670_info *info = to_state(sd); 854f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin int div; 855f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 856f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin if (tpf->numerator == 0 || tpf->denominator == 0) 857f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin div = 1; /* Reset to full rate */ 858f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin else 859f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin div = (tpf->numerator * info->clock_speed) / tpf->denominator; 860f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin if (div == 0) 861f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin div = 1; 862f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin else if (div > CLK_SCALE) 863f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin div = CLK_SCALE; 864f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin info->clkrc = (info->clkrc & 0x80) | div; 865f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin tpf->numerator = 1; 866f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin tpf->denominator = info->clock_speed / div; 867f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin return ov7670_write(sd, REG_CLKRC, info->clkrc); 868f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin} 869f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 870111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 871111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Store a set of start/stop values into the camera. 872111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 87314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, 874111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet int vstart, int vstop) 875111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 876111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet int ret; 877111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet unsigned char v; 878111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 879111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of 880111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is 881111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * a mystery "edge offset" value in the top two bits of href. 882111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 88314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); 88414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); 88514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_read(sd, REG_HREF, &v); 886111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); 887111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet msleep(10); 88814386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_write(sd, REG_HREF, v); 889111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 890111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Vertical: similar arrangement, but only 10 bits. 891111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 89214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); 89314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); 89414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_read(sd, REG_VREF, &v); 895111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); 896111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet msleep(10); 89714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_write(sd, REG_VREF, v); 898111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 899111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 900111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 901111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 902959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuilstatic int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, 903959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil enum v4l2_mbus_pixelcode *code) 904959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil{ 905959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil if (index >= N_OV7670_FMTS) 906959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil return -EINVAL; 907959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil 908959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil *code = ov7670_formats[index].mbus_code; 909959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil return 0; 910959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil} 911111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 91214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_try_fmt_internal(struct v4l2_subdev *sd, 913959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil struct v4l2_mbus_framefmt *fmt, 914111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet struct ov7670_format_struct **ret_fmt, 915111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet struct ov7670_win_size **ret_wsize) 916111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 917f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin int index, i; 918111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet struct ov7670_win_size *wsize; 919d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin struct ov7670_info *info = to_state(sd); 920d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin unsigned int n_win_sizes = info->devtype->n_win_sizes; 921f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin unsigned int win_sizes_limit = n_win_sizes; 922111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 923111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet for (index = 0; index < N_OV7670_FMTS; index++) 924959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil if (ov7670_formats[index].mbus_code == fmt->code) 925111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet break; 926cd257a6f4dd908d94e504d2431710f0fcfe62036Daniel Drake if (index >= N_OV7670_FMTS) { 927cd257a6f4dd908d94e504d2431710f0fcfe62036Daniel Drake /* default to first format */ 928cd257a6f4dd908d94e504d2431710f0fcfe62036Daniel Drake index = 0; 929959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil fmt->code = ov7670_formats[0].mbus_code; 930cd257a6f4dd908d94e504d2431710f0fcfe62036Daniel Drake } 931111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (ret_fmt != NULL) 932111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet *ret_fmt = ov7670_formats + index; 933111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* 934111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Fields: the OV devices claim to be progressive. 935111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 936959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil fmt->field = V4L2_FIELD_NONE; 937f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin 938f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin /* 939f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin * Don't consider values that don't match min_height and min_width 940f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin * constraints. 941f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin */ 942f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin if (info->min_width || info->min_height) 943f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin for (i = 0; i < n_win_sizes; i++) { 944f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin wsize = info->devtype->win_sizes + i; 945f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin 946f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin if (wsize->width < info->min_width || 947f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin wsize->height < info->min_height) { 948f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin win_sizes_limit = i; 949f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin break; 950f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin } 951f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin } 952111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* 953111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Round requested image size down to the nearest 954111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * we support, but not below the smallest. 955111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 956d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin for (wsize = info->devtype->win_sizes; 957f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin wsize < info->devtype->win_sizes + win_sizes_limit; wsize++) 958959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil if (fmt->width >= wsize->width && fmt->height >= wsize->height) 959111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet break; 960f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin if (wsize >= info->devtype->win_sizes + win_sizes_limit) 961111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet wsize--; /* Take the smallest one */ 962111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (ret_wsize != NULL) 963111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet *ret_wsize = wsize; 964111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* 965111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Note the size we'll actually handle. 966111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 967959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil fmt->width = wsize->width; 968959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil fmt->height = wsize->height; 969959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil fmt->colorspace = ov7670_formats[index].colorspace; 970111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return 0; 971111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 972111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 973959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuilstatic int ov7670_try_mbus_fmt(struct v4l2_subdev *sd, 974959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil struct v4l2_mbus_framefmt *fmt) 97514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil{ 97614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); 97714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil} 97814386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil 979111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 980111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Set a format. 981111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 982959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuilstatic int ov7670_s_mbus_fmt(struct v4l2_subdev *sd, 983959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil struct v4l2_mbus_framefmt *fmt) 984111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 985111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet struct ov7670_format_struct *ovfmt; 986111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet struct ov7670_win_size *wsize; 98714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil struct ov7670_info *info = to_state(sd); 988d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet unsigned char com7; 989959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil int ret; 990111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 99114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); 992959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil 993111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (ret) 994111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 995111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* 996111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * COM7 is a pain in the ass, it doesn't like to be read then 997111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * quickly written afterward. But we have everything we need 998111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * to set it absolutely here, as long as the format-specific 999111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * register sets list it first. 1000111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 1001111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet com7 = ovfmt->regs[0].value; 1002111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet com7 |= wsize->com7_bit; 100314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ov7670_write(sd, REG_COM7, com7); 1004111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet /* 1005111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Now write the rest of the array. Also store start/stops 1006111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 100714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ov7670_write_array(sd, ovfmt->regs + 1); 100814386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, 1009111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet wsize->vstop); 1010f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet ret = 0; 1011f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet if (wsize->regs) 101214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_write_array(sd, wsize->regs); 1013f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet info->fmt = ovfmt; 1014edd75ede2d40eadb98e07d87e88fa970f86ffe9eJonathan Corbet 1015d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet /* 1016d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet * If we're running RGB565, we must rewrite clkrc after setting 1017d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet * the other parameters or the image looks poor. If we're *not* 1018d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet * doing RGB565, we must not rewrite clkrc or the image looks 1019d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet * *really* poor. 1020a8e68c37c846236499ac05e95af76dff2e9aa1ebJonathan Corbet * 1021a8e68c37c846236499ac05e95af76dff2e9aa1ebJonathan Corbet * (Update) Now that we retain clkrc state, we should be able 1022a8e68c37c846236499ac05e95af76dff2e9aa1ebJonathan Corbet * to write it unconditionally, and that will make the frame 1023a8e68c37c846236499ac05e95af76dff2e9aa1ebJonathan Corbet * rate persistent too. 1024d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet */ 1025a8e68c37c846236499ac05e95af76dff2e9aa1ebJonathan Corbet if (ret == 0) 1026d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet ret = ov7670_write(sd, REG_CLKRC, info->clkrc); 1027959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil return 0; 1028959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil} 1029959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil 1030111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 1031c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet * Implement G/S_PARM. There is a "high quality" mode we could try 1032c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet * to do someday; for now, we just do the frame rate tweak. 1033c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet */ 103414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) 1035c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet{ 1036c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet struct v4l2_captureparm *cp = &parms->parm.capture; 1037d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet struct ov7670_info *info = to_state(sd); 1038c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet 1039c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 1040c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet return -EINVAL; 1041d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet 1042c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet memset(cp, 0, sizeof(struct v4l2_captureparm)); 1043c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet cp->capability = V4L2_CAP_TIMEPERFRAME; 1044f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin info->devtype->get_framerate(sd, &cp->timeperframe); 1045f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 1046c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet return 0; 1047c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet} 1048c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet 104914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) 1050c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet{ 1051c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet struct v4l2_captureparm *cp = &parms->parm.capture; 1052c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet struct v4l2_fract *tpf = &cp->timeperframe; 1053d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet struct ov7670_info *info = to_state(sd); 1054c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet 1055c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 1056c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet return -EINVAL; 1057c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet if (cp->extendedmode != 0) 1058c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet return -EINVAL; 1059d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet 1060f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin return info->devtype->set_framerate(sd, tpf); 1061c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet} 1062c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet 1063c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet 1064c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet/* 1065e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet * Frame intervals. Since frame rates are controlled with the clock 1066e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet * divider, we can only do 30/n for integer n values. So no continuous 1067e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet * or stepwise options. Here we just pick a handful of logical values. 1068111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 1069111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1070e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbetstatic int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 }; 1071f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1072e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbetstatic int ov7670_enum_frameintervals(struct v4l2_subdev *sd, 1073e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet struct v4l2_frmivalenum *interval) 1074e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet{ 1075e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet if (interval->index >= ARRAY_SIZE(ov7670_frame_rates)) 1076e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet return -EINVAL; 1077e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; 1078e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet interval->discrete.numerator = 1; 1079e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet interval->discrete.denominator = ov7670_frame_rates[interval->index]; 1080e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet return 0; 1081e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet} 1082f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1083e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet/* 1084b0326b7f8de020d70487673123bc93138c091151Daniel Drake * Frame size enumeration 1085b0326b7f8de020d70487673123bc93138c091151Daniel Drake */ 1086b0326b7f8de020d70487673123bc93138c091151Daniel Drakestatic int ov7670_enum_framesizes(struct v4l2_subdev *sd, 1087b0326b7f8de020d70487673123bc93138c091151Daniel Drake struct v4l2_frmsizeenum *fsize) 1088b0326b7f8de020d70487673123bc93138c091151Daniel Drake{ 108975e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake struct ov7670_info *info = to_state(sd); 109075e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake int i; 109175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake int num_valid = -1; 1092b0326b7f8de020d70487673123bc93138c091151Daniel Drake __u32 index = fsize->index; 1093d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin unsigned int n_win_sizes = info->devtype->n_win_sizes; 1094b0326b7f8de020d70487673123bc93138c091151Daniel Drake 109575e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake /* 109675e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake * If a minimum width/height was requested, filter out the capture 109775e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake * windows that fall outside that. 109875e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake */ 1099d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin for (i = 0; i < n_win_sizes; i++) { 1100322e6d19ede96ac6c13a132590c435ff17e0827fGuennadi Liakhovetski struct ov7670_win_size *win = &info->devtype->win_sizes[i]; 110175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake if (info->min_width && win->width < info->min_width) 110275e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake continue; 110375e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake if (info->min_height && win->height < info->min_height) 110475e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake continue; 110575e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake if (index == ++num_valid) { 110675e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 110775e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake fsize->discrete.width = win->width; 110875e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake fsize->discrete.height = win->height; 110975e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake return 0; 111075e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake } 111175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake } 111275e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake 111375e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake return -EINVAL; 1114b0326b7f8de020d70487673123bc93138c091151Daniel Drake} 1115b0326b7f8de020d70487673123bc93138c091151Daniel Drake 1116b0326b7f8de020d70487673123bc93138c091151Daniel Drake/* 1117e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet * Code for dealing with controls. 1118e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet */ 1119f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 112014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_store_cmatrix(struct v4l2_subdev *sd, 1121f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet int matrix[CMATRIX_LEN]) 1122f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet{ 1123f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet int i, ret; 1124e3bf20de33b016ac73424a5574177ed46c754be3Hans Verkuil unsigned char signbits = 0; 1125f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1126f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet /* 1127f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Weird crap seems to exist in the upper part of 1128f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * the sign bits register, so let's preserve it. 1129f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */ 113014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); 1131f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet signbits &= 0xc0; 1132f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1133f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet for (i = 0; i < CMATRIX_LEN; i++) { 1134f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet unsigned char raw; 1135f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1136f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet if (matrix[i] < 0) { 1137f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet signbits |= (1 << i); 1138f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet if (matrix[i] < -255) 1139f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet raw = 0xff; 1140f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet else 1141f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet raw = (-1 * matrix[i]) & 0xff; 1142f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet } 1143f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet else { 1144f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet if (matrix[i] > 255) 1145f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet raw = 0xff; 1146f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet else 1147f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet raw = matrix[i] & 0xff; 1148f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet } 114914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); 1150f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet } 115114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); 1152f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet return ret; 1153f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet} 1154f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1155f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1156f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet/* 1157f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Hue also requires messing with the color matrix. It also requires 1158f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * trig functions, which tend not to be well supported in the kernel. 1159f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * So here is a simple table of sine values, 0-90 degrees, in steps 1160f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * of five degrees. Values are multiplied by 1000. 1161f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * 1162f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * The following naive approximate trig functions require an argument 1163f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * carefully limited to -180 <= theta <= 180. 1164f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */ 1165f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet#define SIN_STEP 5 1166f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstatic const int ov7670_sin_table[] = { 1167f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 0, 87, 173, 258, 342, 422, 1168f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 499, 573, 642, 707, 766, 819, 1169f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 866, 906, 939, 965, 984, 996, 1170f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1000 1171f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet}; 1172f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1173f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstatic int ov7670_sine(int theta) 1174f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet{ 1175f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet int chs = 1; 1176f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet int sine; 1177f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1178f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet if (theta < 0) { 1179f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet theta = -theta; 1180f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet chs = -1; 1181f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet } 1182f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet if (theta <= 90) 1183f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet sine = ov7670_sin_table[theta/SIN_STEP]; 1184f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet else { 1185f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet theta -= 90; 1186f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; 1187f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet } 1188f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet return sine*chs; 1189f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet} 1190f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1191f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstatic int ov7670_cosine(int theta) 1192f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet{ 1193f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet theta = 90 - theta; 1194f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet if (theta > 180) 1195f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet theta -= 360; 1196f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet else if (theta < -180) 1197f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet theta += 360; 1198f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet return ov7670_sine(theta); 1199f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet} 1200f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1201f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1202f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1203f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1204f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstatic void ov7670_calc_cmatrix(struct ov7670_info *info, 1205492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin int matrix[CMATRIX_LEN], int sat, int hue) 1206f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet{ 1207f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet int i; 1208f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet /* 1209f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Apply the current saturation setting first. 1210f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */ 1211f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet for (i = 0; i < CMATRIX_LEN; i++) 1212492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin matrix[i] = (info->fmt->cmatrix[i] * sat) >> 7; 1213f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet /* 1214f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Then, if need be, rotate the hue value. 1215f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */ 1216492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin if (hue != 0) { 1217f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet int sinth, costh, tmpmatrix[CMATRIX_LEN]; 1218f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1219f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); 1220492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin sinth = ov7670_sine(hue); 1221492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin costh = ov7670_cosine(hue); 1222f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1223f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; 1224f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; 1225f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; 1226f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; 1227f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; 1228f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; 1229f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet } 1230f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet} 1231f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1232f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1233f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1234492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martinstatic int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue) 1235f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet{ 123614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil struct ov7670_info *info = to_state(sd); 1237f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet int matrix[CMATRIX_LEN]; 1238f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet int ret; 1239f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1240492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin ov7670_calc_cmatrix(info, matrix, sat, hue); 124114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_store_cmatrix(sd, matrix); 1242f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet return ret; 1243f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet} 1244f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1245f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet 1246111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* 1247111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Some weird registers seem to store values in a sign/magnitude format! 1248111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */ 1249111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1250111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic unsigned char ov7670_abs_to_sm(unsigned char v) 1251111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 1252111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (v > 127) 1253111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return v & 0x7f; 125414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil return (128 - v) | 0x80; 1255111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 1256111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1257ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuilstatic int ov7670_s_brightness(struct v4l2_subdev *sd, int value) 1258111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 1259e3bf20de33b016ac73424a5574177ed46c754be3Hans Verkuil unsigned char com8 = 0, v; 1260111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet int ret; 1261111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 126214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ov7670_read(sd, REG_COM8, &com8); 1263111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet com8 &= ~COM8_AEC; 126414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ov7670_write(sd, REG_COM8, com8); 1265f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet v = ov7670_abs_to_sm(value); 126614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_write(sd, REG_BRIGHT, v); 1267111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 1268111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 1269111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1270ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuilstatic int ov7670_s_contrast(struct v4l2_subdev *sd, int value) 1271111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 127214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); 1273111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 1274111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1275ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuilstatic int ov7670_s_hflip(struct v4l2_subdev *sd, int value) 1276111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 1277e3bf20de33b016ac73424a5574177ed46c754be3Hans Verkuil unsigned char v = 0; 1278111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet int ret; 1279111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 128014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_read(sd, REG_MVFP, &v); 1281111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (value) 1282111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet v |= MVFP_MIRROR; 1283111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet else 1284111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet v &= ~MVFP_MIRROR; 1285111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet msleep(10); /* FIXME */ 128614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_write(sd, REG_MVFP, v); 1287111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 1288111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 1289111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1290ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuilstatic int ov7670_s_vflip(struct v4l2_subdev *sd, int value) 1291111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 1292e3bf20de33b016ac73424a5574177ed46c754be3Hans Verkuil unsigned char v = 0; 1293111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet int ret; 1294111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 129514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret = ov7670_read(sd, REG_MVFP, &v); 1296111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet if (value) 1297111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet v |= MVFP_FLIP; 1298111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet else 1299111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet v &= ~MVFP_FLIP; 1300111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet msleep(10); /* FIXME */ 130114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil ret += ov7670_write(sd, REG_MVFP, v); 1302111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return ret; 1303111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 1304111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 130581898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet/* 130681898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes 130781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet * the data sheet, the VREF parts should be the most significant, but 130881898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet * experience shows otherwise. There seems to be little value in 130981898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet * messing with the VREF bits, so we leave them alone. 131081898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet */ 131181898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbetstatic int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) 131281898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet{ 131381898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet int ret; 131481898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet unsigned char gain; 131581898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet 131681898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet ret = ov7670_read(sd, REG_GAIN, &gain); 131781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet *value = gain; 131881898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet return ret; 131981898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet} 132081898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet 132181898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbetstatic int ov7670_s_gain(struct v4l2_subdev *sd, int value) 132281898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet{ 132381898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet int ret; 132481898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet unsigned char com8; 132581898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet 132681898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet ret = ov7670_write(sd, REG_GAIN, value & 0xff); 132781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet /* Have to turn off AGC as well */ 132881898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet if (ret == 0) { 132981898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet ret = ov7670_read(sd, REG_COM8, &com8); 133081898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); 133181898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet } 133281898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet return ret; 133381898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet} 133481898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet 133581898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet/* 133681898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet * Tweak autogain. 133781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet */ 133881898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbetstatic int ov7670_s_autogain(struct v4l2_subdev *sd, int value) 133981898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet{ 134081898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet int ret; 134181898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet unsigned char com8; 134281898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet 134381898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet ret = ov7670_read(sd, REG_COM8, &com8); 134481898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet if (ret == 0) { 134581898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet if (value) 134681898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet com8 |= COM8_AGC; 134781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet else 134881898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet com8 &= ~COM8_AGC; 134981898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet ret = ov7670_write(sd, REG_COM8, com8); 135081898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet } 135181898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet return ret; 135281898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet} 135381898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet 1354364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbetstatic int ov7670_s_exp(struct v4l2_subdev *sd, int value) 1355364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet{ 1356364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet int ret; 1357364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet unsigned char com1, com8, aech, aechh; 1358364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet 1359364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet ret = ov7670_read(sd, REG_COM1, &com1) + 1360364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet ov7670_read(sd, REG_COM8, &com8); 1361364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet ov7670_read(sd, REG_AECHH, &aechh); 1362364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet if (ret) 1363364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet return ret; 1364364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet 1365364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet com1 = (com1 & 0xfc) | (value & 0x03); 1366364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet aech = (value >> 2) & 0xff; 1367364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f); 1368364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet ret = ov7670_write(sd, REG_COM1, com1) + 1369364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet ov7670_write(sd, REG_AECH, aech) + 1370364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet ov7670_write(sd, REG_AECHH, aechh); 1371364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet /* Have to turn off AEC as well */ 1372364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet if (ret == 0) 1373364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC); 1374364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet return ret; 1375364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet} 1376364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet 1377364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet/* 1378364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet * Tweak autoexposure. 1379364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet */ 1380364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbetstatic int ov7670_s_autoexp(struct v4l2_subdev *sd, 1381364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet enum v4l2_exposure_auto_type value) 1382364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet{ 1383364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet int ret; 1384364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet unsigned char com8; 1385364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet 1386364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet ret = ov7670_read(sd, REG_COM8, &com8); 1387364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet if (ret == 0) { 1388364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet if (value == V4L2_EXPOSURE_AUTO) 1389364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet com8 |= COM8_AEC; 1390364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet else 1391364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet com8 &= ~COM8_AEC; 1392364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet ret = ov7670_write(sd, REG_COM8, com8); 1393364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet } 1394364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet return ret; 1395364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet} 1396364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet 139781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet 1398492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martinstatic int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 1399111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 1400492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct v4l2_subdev *sd = to_sd(ctrl); 1401492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct ov7670_info *info = to_state(sd); 1402111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1403ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil switch (ctrl->id) { 140481898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet case V4L2_CID_AUTOGAIN: 1405492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return ov7670_g_gain(sd, &info->gain->val); 1406ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil } 1407ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil return -EINVAL; 1408111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 1409111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1410492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martinstatic int ov7670_s_ctrl(struct v4l2_ctrl *ctrl) 1411111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 1412492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct v4l2_subdev *sd = to_sd(ctrl); 1413492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct ov7670_info *info = to_state(sd); 1414492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin 1415ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil switch (ctrl->id) { 1416ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil case V4L2_CID_BRIGHTNESS: 1417492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return ov7670_s_brightness(sd, ctrl->val); 1418ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil case V4L2_CID_CONTRAST: 1419492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return ov7670_s_contrast(sd, ctrl->val); 1420ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil case V4L2_CID_SATURATION: 1421492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return ov7670_s_sat_hue(sd, 1422492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin info->saturation->val, info->hue->val); 1423ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil case V4L2_CID_VFLIP: 1424492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return ov7670_s_vflip(sd, ctrl->val); 1425ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil case V4L2_CID_HFLIP: 1426492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return ov7670_s_hflip(sd, ctrl->val); 142781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet case V4L2_CID_AUTOGAIN: 1428492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin /* Only set manual gain if auto gain is not explicitly 1429492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin turned on. */ 1430492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin if (!ctrl->val) { 1431492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin /* ov7670_s_gain turns off auto gain */ 1432492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return ov7670_s_gain(sd, info->gain->val); 1433492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin } 1434492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return ov7670_s_autogain(sd, ctrl->val); 1435364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet case V4L2_CID_EXPOSURE_AUTO: 1436492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin /* Only set manual exposure if auto exposure is not explicitly 1437492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin turned on. */ 1438492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin if (ctrl->val == V4L2_EXPOSURE_MANUAL) { 1439492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin /* ov7670_s_exp turns off auto exposure */ 1440492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return ov7670_s_exp(sd, info->exposure->val); 1441492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin } 1442492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return ov7670_s_autoexp(sd, ctrl->val); 1443ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil } 1444ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil return -EINVAL; 1445111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 1446111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1447492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martinstatic const struct v4l2_ctrl_ops ov7670_ctrl_ops = { 1448492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin .s_ctrl = ov7670_s_ctrl, 1449492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin .g_volatile_ctrl = ov7670_g_volatile_ctrl, 1450492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin}; 1451492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin 1452b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil#ifdef CONFIG_VIDEO_ADV_DEBUG 1453b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuilstatic int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) 1454b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil{ 1455b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil unsigned char val = 0; 1456b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil int ret; 1457b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil 1458b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil ret = ov7670_read(sd, reg->reg & 0xff, &val); 1459b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil reg->val = val; 1460b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil reg->size = 1; 1461b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil return ret; 1462b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil} 1463b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil 1464977ba3b1b73f24fae2d0c8bd59d7a4696f1e0cccHans Verkuilstatic int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) 1465b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil{ 1466b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff); 1467b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil return 0; 1468b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil} 1469b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil#endif 1470b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil 147114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil/* ----------------------------------------------------------------------- */ 1472111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 147314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic const struct v4l2_subdev_core_ops ov7670_core_ops = { 147414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil .reset = ov7670_reset, 147514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil .init = ov7670_init, 1476b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil#ifdef CONFIG_VIDEO_ADV_DEBUG 1477b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil .g_register = ov7670_g_register, 1478b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil .s_register = ov7670_s_register, 1479b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil#endif 148014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil}; 1481111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 148214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic const struct v4l2_subdev_video_ops ov7670_video_ops = { 1483959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .enum_mbus_fmt = ov7670_enum_mbus_fmt, 1484959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .try_mbus_fmt = ov7670_try_mbus_fmt, 1485959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil .s_mbus_fmt = ov7670_s_mbus_fmt, 148614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil .s_parm = ov7670_s_parm, 148714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil .g_parm = ov7670_g_parm, 1488e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet .enum_frameintervals = ov7670_enum_frameintervals, 1489b0326b7f8de020d70487673123bc93138c091151Daniel Drake .enum_framesizes = ov7670_enum_framesizes, 149014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil}; 1491111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 149214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic const struct v4l2_subdev_ops ov7670_ops = { 149314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil .core = &ov7670_core_ops, 149414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil .video = &ov7670_video_ops, 149514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil}; 1496111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 149714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil/* ----------------------------------------------------------------------- */ 1498111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1499d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinstatic const struct ov7670_devtype ov7670_devdata[] = { 1500d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin [MODEL_OV7670] = { 1501d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .win_sizes = ov7670_win_sizes, 1502d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .n_win_sizes = ARRAY_SIZE(ov7670_win_sizes), 1503f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin .set_framerate = ov7670_set_framerate_legacy, 1504f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin .get_framerate = ov7670_get_framerate_legacy, 1505d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin }, 1506d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin [MODEL_OV7675] = { 1507d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .win_sizes = ov7675_win_sizes, 1508d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin .n_win_sizes = ARRAY_SIZE(ov7675_win_sizes), 1509f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin .set_framerate = ov7675_set_framerate, 1510f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin .get_framerate = ov7675_get_framerate, 1511d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin }, 1512d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin}; 1513d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin 151414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_probe(struct i2c_client *client, 151514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil const struct i2c_device_id *id) 1516111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 1517f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin struct v4l2_fract tpf; 151814386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil struct v4l2_subdev *sd; 1519f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet struct ov7670_info *info; 15203c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil int ret; 1521111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1522c02b211df6fc54e51ee554c27a6736a11255a764Laurent Pinchart info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); 152314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil if (info == NULL) 1524111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return -ENOMEM; 152514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil sd = &info->sd; 152614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil v4l2_i2c_subdev_init(sd, client, &ov7670_ops); 152714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil 15283c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil info->clock_speed = 30; /* default: a guess */ 15293c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil if (client->dev.platform_data) { 15303c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil struct ov7670_config *config = client->dev.platform_data; 15313c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil 15323c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil /* 15333c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil * Must apply configuration before initializing device, because it 15343c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil * selects I/O method. 15353c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil */ 15363c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil info->min_width = config->min_width; 15373c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil info->min_height = config->min_height; 15383c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil info->use_smbus = config->use_smbus; 15393c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil 15403c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil if (config->clock_speed) 15413c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil info->clock_speed = config->clock_speed; 154204ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin 154304ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin /* 154404ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin * It should be allowed for ov7670 too when it is migrated to 154504ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin * the new frame rate formula. 154604ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin */ 154704ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin if (config->pll_bypass && id->driver_data != MODEL_OV7670) 154804ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin info->pll_bypass = true; 1549ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin 1550ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin if (config->pclk_hb_disable) 1551ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin info->pclk_hb_disable = true; 15523c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil } 15533c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil 15543c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil /* Make sure it's an ov7670 */ 15553c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil ret = ov7670_detect(sd); 15563c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil if (ret) { 15573c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil v4l_dbg(1, debug, client, 15583c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil "chip found @ 0x%x (%s) is not an ov7670 chip.\n", 15593c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil client->addr << 1, client->adapter->name); 15603c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil return ret; 15613c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil } 15623c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil v4l_info(client, "chip found @ 0x%02x (%s)\n", 15633c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil client->addr << 1, client->adapter->name); 15643c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil 1565d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin info->devtype = &ov7670_devdata[id->driver_data]; 15663c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil info->fmt = &ov7670_formats[0]; 1567f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin info->clkrc = 0; 1568f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 1569f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin /* Set default frame rate to 30 fps */ 1570f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin tpf.numerator = 1; 1571f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin tpf.denominator = 30; 1572f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin info->devtype->set_framerate(sd, &tpf); 1573f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin 1574ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin if (info->pclk_hb_disable) 1575ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin ov7670_write(sd, REG_COM10, COM10_PCLK_HB); 1576ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin 1577492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_handler_init(&info->hdl, 10); 1578492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1579492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); 1580492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1581492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_CID_CONTRAST, 0, 127, 1, 64); 1582492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1583492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_CID_VFLIP, 0, 1, 1, 0); 1584492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1585492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_CID_HFLIP, 0, 1, 1, 0); 1586492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin info->saturation = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1587492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_CID_SATURATION, 0, 256, 1, 128); 1588492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin info->hue = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1589492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_CID_HUE, -180, 180, 5, 0); 1590492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin info->gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1591492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_CID_GAIN, 0, 255, 1, 128); 1592492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin info->auto_gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1593492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 1594492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin info->exposure = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1595492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_CID_EXPOSURE, 0, 65535, 1, 500); 1596492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin info->auto_exposure = v4l2_ctrl_new_std_menu(&info->hdl, &ov7670_ctrl_ops, 1597492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, 1598492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_EXPOSURE_AUTO); 1599492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin sd->ctrl_handler = &info->hdl; 1600492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin if (info->hdl.error) { 1601492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin int err = info->hdl.error; 1602492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin 1603492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_handler_free(&info->hdl); 1604492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin return err; 1605492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin } 1606492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin /* 1607492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin * We have checked empirically that hw allows to read back the gain 1608492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin * value chosen by auto gain but that's not the case for auto exposure. 1609492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin */ 1610492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_auto_cluster(2, &info->auto_gain, 0, true); 1611492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_auto_cluster(2, &info->auto_exposure, 1612492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin V4L2_EXPOSURE_MANUAL, false); 1613492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_cluster(2, &info->saturation); 1614492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_handler_setup(&info->hdl); 1615492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin 1616111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet return 0; 1617111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 1618111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 1619111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 162014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_remove(struct i2c_client *client) 1621111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{ 162214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil struct v4l2_subdev *sd = i2c_get_clientdata(client); 1623492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin struct ov7670_info *info = to_state(sd); 1624111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 162514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil v4l2_device_unregister_subdev(sd); 1626492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin v4l2_ctrl_handler_free(&info->hdl); 162714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil return 0; 1628111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} 1629111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet 163014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic const struct i2c_device_id ov7670_id[] = { 1631d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin { "ov7670", MODEL_OV7670 }, 1632d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin { "ov7675", MODEL_OV7675 }, 163314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil { } 163414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil}; 163514386c2b7793652a656021a3345cff3b0f6771f9Hans VerkuilMODULE_DEVICE_TABLE(i2c, ov7670_id); 163614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil 1637ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuilstatic struct i2c_driver ov7670_driver = { 1638ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil .driver = { 1639ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil .owner = THIS_MODULE, 1640ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil .name = "ov7670", 1641ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil }, 1642ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil .probe = ov7670_probe, 1643ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil .remove = ov7670_remove, 1644ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil .id_table = ov7670_id, 1645111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}; 1646ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil 1647c6e8d86fffd8edf1bfccbd441b1812ee919fe3d5Axel Linmodule_i2c_driver(ov7670_driver); 1648