2010-12-02 17:53:28 +03:00
/*
* ov2640 Camera Driver
*
* Copyright ( C ) 2010 Alberto Panizzo < maramaopercheseimorto @ gmail . com >
*
* Based on ov772x , ov9640 drivers and previous non merged implementations .
*
* Copyright 2005 - 2009 Freescale Semiconductor , Inc . All Rights Reserved .
* Copyright ( C ) 2006 , OmniVision
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/slab.h>
# include <linux/delay.h>
2011-09-09 20:56:04 +04:00
# include <linux/v4l2-mediabus.h>
2010-12-02 17:53:28 +03:00
# include <linux/videodev2.h>
2011-07-26 18:59:53 +04:00
2010-12-02 17:53:28 +03:00
# include <media/soc_camera.h>
2011-07-26 18:59:53 +04:00
# include <media/v4l2-chip-ident.h>
# include <media/v4l2-subdev.h>
2011-09-07 12:43:05 +04:00
# include <media/v4l2-ctrls.h>
2010-12-02 17:53:28 +03:00
# define VAL_SET(x, mask, rshift, lshift) \
( ( ( ( x ) > > rshift ) & mask ) < < lshift )
/*
* DSP registers
* register offset for BANK_SEL = = BANK_SEL_DSP
*/
# define R_BYPASS 0x05 /* Bypass DSP */
# define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */
# define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */
# define QS 0x44 /* Quantization Scale Factor */
# define CTRLI 0x50
# define CTRLI_LP_DP 0x80
# define CTRLI_ROUND 0x40
# define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3)
# define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0)
# define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */
# define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
# define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */
# define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
# define XOFFL 0x53 /* OFFSET_X[7:0] */
# define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
# define YOFFL 0x54 /* OFFSET_Y[7:0] */
# define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
# define VHYX 0x55 /* Offset and size completion */
# define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7)
# define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3)
# define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4)
# define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0)
# define DPRP 0x56
# define TEST 0x57 /* Horizontal size completion */
# define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7)
# define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */
# define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0)
# define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */
# define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0)
# define ZMHH 0x5C /* Zoom: Speed and H&W completion */
# define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4)
# define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2)
# define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0)
# define BPADDR 0x7C /* SDE Indirect Register Access: Address */
# define BPDATA 0x7D /* SDE Indirect Register Access: Data */
# define CTRL2 0x86 /* DSP Module enable 2 */
# define CTRL2_DCW_EN 0x20
# define CTRL2_SDE_EN 0x10
# define CTRL2_UV_ADJ_EN 0x08
# define CTRL2_UV_AVG_EN 0x04
# define CTRL2_CMX_EN 0x01
# define CTRL3 0x87 /* DSP Module enable 3 */
# define CTRL3_BPC_EN 0x80
# define CTRL3_WPC_EN 0x40
# define SIZEL 0x8C /* Image Size Completion */
# define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6)
# define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3)
# define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0)
# define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */
# define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
# define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */
# define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
# define CTRL0 0xC2 /* DSP Module enable 0 */
# define CTRL0_AEC_EN 0x80
# define CTRL0_AEC_SEL 0x40
# define CTRL0_STAT_SEL 0x20
# define CTRL0_VFIRST 0x10
# define CTRL0_YUV422 0x08
# define CTRL0_YUV_EN 0x04
# define CTRL0_RGB_EN 0x02
# define CTRL0_RAW_EN 0x01
# define CTRL1 0xC3 /* DSP Module enable 1 */
# define CTRL1_CIP 0x80
# define CTRL1_DMY 0x40
# define CTRL1_RAW_GMA 0x20
# define CTRL1_DG 0x10
# define CTRL1_AWB 0x08
# define CTRL1_AWB_GAIN 0x04
# define CTRL1_LENC 0x02
# define CTRL1_PRE 0x01
# define R_DVP_SP 0xD3 /* DVP output speed control */
# define R_DVP_SP_AUTO_MODE 0x80
# define R_DVP_SP_DVP_MASK 0x3F / * DVP PCLK = sysclk (48) / [6:0] (YUV0);
* = sysclk ( 48 ) / ( 2 * [ 6 : 0 ] ) ( RAW ) ; */
# define IMAGE_MODE 0xDA /* Image Output Format Select */
# define IMAGE_MODE_Y8_DVP_EN 0x40
# define IMAGE_MODE_JPEG_EN 0x10
# define IMAGE_MODE_YUV422 0x00
# define IMAGE_MODE_RAW10 0x04 /* (DVP) */
# define IMAGE_MODE_RGB565 0x08
# define IMAGE_MODE_HREF_VSYNC 0x02 / * HREF timing select in DVP JPEG output
* mode ( 0 for HREF is same as sensor ) */
# define IMAGE_MODE_LBYTE_FIRST 0x01 / * Byte swap enable for DVP
* 1 : Low byte first UYVY ( C2 [ 4 ] = 0 )
* VYUY ( C2 [ 4 ] = 1 )
* 0 : High byte first YUYV ( C2 [ 4 ] = 0 )
* YVYU ( C2 [ 4 ] = 1 ) */
# define RESET 0xE0 /* Reset */
# define RESET_MICROC 0x40
# define RESET_SCCB 0x20
# define RESET_JPEG 0x10
# define RESET_DVP 0x04
# define RESET_IPU 0x02
# define RESET_CIF 0x01
# define REGED 0xED /* Register ED */
# define REGED_CLK_OUT_DIS 0x10
# define MS_SP 0xF0 /* SCCB Master Speed */
# define SS_ID 0xF7 /* SCCB Slave ID */
# define SS_CTRL 0xF8 /* SCCB Slave Control */
# define SS_CTRL_ADD_AUTO_INC 0x20
# define SS_CTRL_EN 0x08
# define SS_CTRL_DELAY_CLK 0x04
# define SS_CTRL_ACC_EN 0x02
# define SS_CTRL_SEN_PASS_THR 0x01
# define MC_BIST 0xF9 /* Microcontroller misc register */
# define MC_BIST_RESET 0x80 /* Microcontroller Reset */
# define MC_BIST_BOOT_ROM_SEL 0x40
# define MC_BIST_12KB_SEL 0x20
# define MC_BIST_12KB_MASK 0x30
# define MC_BIST_512KB_SEL 0x08
# define MC_BIST_512KB_MASK 0x0C
# define MC_BIST_BUSY_BIT_R 0x02
# define MC_BIST_MC_RES_ONE_SH_W 0x02
# define MC_BIST_LAUNCH 0x01
# define BANK_SEL 0xFF /* Register Bank Select */
# define BANK_SEL_DSP 0x00
# define BANK_SEL_SENS 0x01
/*
* Sensor registers
* register offset for BANK_SEL = = BANK_SEL_SENS
*/
# define GAIN 0x00 /* AGC - Gain control gain setting */
# define COM1 0x03 /* Common control 1 */
# define COM1_1_DUMMY_FR 0x40
# define COM1_3_DUMMY_FR 0x80
# define COM1_7_DUMMY_FR 0xC0
# define COM1_VWIN_LSB_UXGA 0x0F
# define COM1_VWIN_LSB_SVGA 0x0A
# define COM1_VWIN_LSB_CIF 0x06
# define REG04 0x04 /* Register 04 */
# define REG04_DEF 0x20 /* Always set */
# define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */
# define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */
# define REG04_VREF_EN 0x10
# define REG04_HREF_EN 0x08
# define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0)
# define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */
# define COM2 0x09 /* Common control 2 */
# define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
/* Output drive capability */
# define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */
# define PID 0x0A /* Product ID Number MSB */
# define VER 0x0B /* Product ID Number LSB */
# define COM3 0x0C /* Common control 3 */
# define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */
# define COM3_BAND_AUTO 0x02 /* Auto Banding */
# define COM3_SING_FR_SNAPSH 0x01 / * 0 For enable live video output after the
* snapshot sequence */
# define AEC 0x10 /* AEC[9:2] Exposure Value */
# define CLKRC 0x11 /* Internal clock */
# define CLKRC_EN 0x80
# define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */
# define COM7 0x12 /* Common control 7 */
# define COM7_SRST 0x80 / * Initiates system reset. All registers are
* set to factory default values after which
* the chip resumes normal operation */
# define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */
# define COM7_RES_SVGA 0x40 /* SVGA */
# define COM7_RES_CIF 0x20 /* CIF */
# define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */
# define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */
# define COM8 0x13 /* Common control 8 */
# define COM8_DEF 0xC0 /* Banding filter ON/OFF */
# define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */
# define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
# define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
# define COM9 0x14 / * Common control 9
* Automatic gain ceiling - maximum AGC value [ 7 : 5 ] */
# define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */
# define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */
# define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */
# define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */
# define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */
# define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */
# define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */
# define COM10 0x15 /* Common control 10 */
# define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */
# define COM10_PCLK_RISE 0x10 / * Data is updated at the rising edge of
* PCLK ( user can latch data at the next
* falling edge of PCLK ) .
* 0 otherwise . */
# define COM10_HREF_INV 0x08 / * Invert HREF polarity:
* HREF negative for valid data */
# define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */
# define HSTART 0x17 /* Horizontal Window start MSB 8 bit */
# define HEND 0x18 /* Horizontal Window end MSB 8 bit */
# define VSTART 0x19 /* Vertical Window start MSB 8 bit */
# define VEND 0x1A /* Vertical Window end MSB 8 bit */
# define MIDH 0x1C /* Manufacturer ID byte - high */
# define MIDL 0x1D /* Manufacturer ID byte - low */
# define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
# define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
# define VV 0x26 /* AGC/AEC Fast mode operating region */
# define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4)
# define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0)
# define REG2A 0x2A /* Dummy pixel insert MSB */
# define FRARL 0x2B /* Dummy pixel insert LSB */
# define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
# define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */
# define YAVG 0x2F /* Y/G Channel Average value */
# define REG32 0x32 /* Common Control 32 */
# define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */
# define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */
# define ARCOM2 0x34 /* Zoom: Horizontal start point */
# define REG45 0x45 /* Register 45 */
# define FLL 0x46 /* Frame Length Adjustment LSBs */
# define FLH 0x47 /* Frame Length Adjustment MSBs */
# define COM19 0x48 /* Zoom: Vertical start point */
# define ZOOMS 0x49 /* Zoom: Vertical start point */
# define COM22 0x4B /* Flash light control */
# define COM25 0x4E /* For Banding operations */
# define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */
# define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */
# define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */
# define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */
# define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */
# define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */
# define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */
# define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */
/*
* ID
*/
# define MANUFACTURER_ID 0x7FA2
# define PID_OV2640 0x2642
# define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF))
/*
* Struct
*/
struct regval_list {
u8 reg_num ;
u8 value ;
} ;
/* Supported resolutions */
enum ov2640_width {
W_QCIF = 176 ,
W_QVGA = 320 ,
W_CIF = 352 ,
W_VGA = 640 ,
W_SVGA = 800 ,
W_XGA = 1024 ,
W_SXGA = 1280 ,
W_UXGA = 1600 ,
} ;
enum ov2640_height {
H_QCIF = 144 ,
H_QVGA = 240 ,
H_CIF = 288 ,
H_VGA = 480 ,
H_SVGA = 600 ,
H_XGA = 768 ,
H_SXGA = 1024 ,
H_UXGA = 1200 ,
} ;
struct ov2640_win_size {
char * name ;
enum ov2640_width width ;
enum ov2640_height height ;
const struct regval_list * regs ;
} ;
struct ov2640_priv {
struct v4l2_subdev subdev ;
2011-09-07 12:43:05 +04:00
struct v4l2_ctrl_handler hdl ;
2010-12-02 17:53:28 +03:00
enum v4l2_mbus_pixelcode cfmt_code ;
const struct ov2640_win_size * win ;
int model ;
} ;
/*
* Registers settings
*/
# define ENDMARKER { 0xff, 0xff }
static const struct regval_list ov2640_init_regs [ ] = {
{ BANK_SEL , BANK_SEL_DSP } ,
{ 0x2c , 0xff } ,
{ 0x2e , 0xdf } ,
{ BANK_SEL , BANK_SEL_SENS } ,
{ 0x3c , 0x32 } ,
{ CLKRC , CLKRC_DIV_SET ( 1 ) } ,
{ COM2 , COM2_OCAP_Nx_SET ( 3 ) } ,
{ REG04 , REG04_DEF | REG04_HREF_EN } ,
{ COM8 , COM8_DEF | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN } ,
{ COM9 , COM9_AGC_GAIN_8x | 0x08 } ,
{ 0x2c , 0x0c } ,
{ 0x33 , 0x78 } ,
{ 0x3a , 0x33 } ,
{ 0x3b , 0xfb } ,
{ 0x3e , 0x00 } ,
{ 0x43 , 0x11 } ,
{ 0x16 , 0x10 } ,
{ 0x39 , 0x02 } ,
{ 0x35 , 0x88 } ,
{ 0x22 , 0x0a } ,
{ 0x37 , 0x40 } ,
{ 0x23 , 0x00 } ,
{ ARCOM2 , 0xa0 } ,
{ 0x06 , 0x02 } ,
{ 0x06 , 0x88 } ,
{ 0x07 , 0xc0 } ,
{ 0x0d , 0xb7 } ,
{ 0x0e , 0x01 } ,
{ 0x4c , 0x00 } ,
{ 0x4a , 0x81 } ,
{ 0x21 , 0x99 } ,
{ AEW , 0x40 } ,
{ AEB , 0x38 } ,
{ VV , VV_HIGH_TH_SET ( 0x08 ) | VV_LOW_TH_SET ( 0x02 ) } ,
{ 0x5c , 0x00 } ,
{ 0x63 , 0x00 } ,
{ FLL , 0x22 } ,
{ COM3 , 0x38 | COM3_BAND_AUTO } ,
{ REG5D , 0x55 } ,
{ REG5E , 0x7d } ,
{ REG5F , 0x7d } ,
{ REG60 , 0x55 } ,
{ HISTO_LOW , 0x70 } ,
{ HISTO_HIGH , 0x80 } ,
{ 0x7c , 0x05 } ,
{ 0x20 , 0x80 } ,
{ 0x28 , 0x30 } ,
{ 0x6c , 0x00 } ,
{ 0x6d , 0x80 } ,
{ 0x6e , 0x00 } ,
{ 0x70 , 0x02 } ,
{ 0x71 , 0x94 } ,
{ 0x73 , 0xc1 } ,
{ 0x3d , 0x34 } ,
{ COM7 , COM7_RES_UXGA | COM7_ZOOM_EN } ,
{ 0x5a , 0x57 } ,
{ BD50 , 0xbb } ,
{ BD60 , 0x9c } ,
{ BANK_SEL , BANK_SEL_DSP } ,
{ 0xe5 , 0x7f } ,
{ MC_BIST , MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL } ,
{ 0x41 , 0x24 } ,
{ RESET , RESET_JPEG | RESET_DVP } ,
{ 0x76 , 0xff } ,
{ 0x33 , 0xa0 } ,
{ 0x42 , 0x20 } ,
{ 0x43 , 0x18 } ,
{ 0x4c , 0x00 } ,
{ CTRL3 , CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 } ,
{ 0x88 , 0x3f } ,
{ 0xd7 , 0x03 } ,
{ 0xd9 , 0x10 } ,
{ R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 } ,
{ 0xc8 , 0x08 } ,
{ 0xc9 , 0x80 } ,
{ BPADDR , 0x00 } ,
{ BPDATA , 0x00 } ,
{ BPADDR , 0x03 } ,
{ BPDATA , 0x48 } ,
{ BPDATA , 0x48 } ,
{ BPADDR , 0x08 } ,
{ BPDATA , 0x20 } ,
{ BPDATA , 0x10 } ,
{ BPDATA , 0x0e } ,
{ 0x90 , 0x00 } ,
{ 0x91 , 0x0e } ,
{ 0x91 , 0x1a } ,
{ 0x91 , 0x31 } ,
{ 0x91 , 0x5a } ,
{ 0x91 , 0x69 } ,
{ 0x91 , 0x75 } ,
{ 0x91 , 0x7e } ,
{ 0x91 , 0x88 } ,
{ 0x91 , 0x8f } ,
{ 0x91 , 0x96 } ,
{ 0x91 , 0xa3 } ,
{ 0x91 , 0xaf } ,
{ 0x91 , 0xc4 } ,
{ 0x91 , 0xd7 } ,
{ 0x91 , 0xe8 } ,
{ 0x91 , 0x20 } ,
{ 0x92 , 0x00 } ,
{ 0x93 , 0x06 } ,
{ 0x93 , 0xe3 } ,
{ 0x93 , 0x03 } ,
{ 0x93 , 0x03 } ,
{ 0x93 , 0x00 } ,
{ 0x93 , 0x02 } ,
{ 0x93 , 0x00 } ,
{ 0x93 , 0x00 } ,
{ 0x93 , 0x00 } ,
{ 0x93 , 0x00 } ,
{ 0x93 , 0x00 } ,
{ 0x93 , 0x00 } ,
{ 0x93 , 0x00 } ,
{ 0x96 , 0x00 } ,
{ 0x97 , 0x08 } ,
{ 0x97 , 0x19 } ,
{ 0x97 , 0x02 } ,
{ 0x97 , 0x0c } ,
{ 0x97 , 0x24 } ,
{ 0x97 , 0x30 } ,
{ 0x97 , 0x28 } ,
{ 0x97 , 0x26 } ,
{ 0x97 , 0x02 } ,
{ 0x97 , 0x98 } ,
{ 0x97 , 0x80 } ,
{ 0x97 , 0x00 } ,
{ 0x97 , 0x00 } ,
{ 0xa4 , 0x00 } ,
{ 0xa8 , 0x00 } ,
{ 0xc5 , 0x11 } ,
{ 0xc6 , 0x51 } ,
{ 0xbf , 0x80 } ,
{ 0xc7 , 0x10 } ,
{ 0xb6 , 0x66 } ,
{ 0xb8 , 0xA5 } ,
{ 0xb7 , 0x64 } ,
{ 0xb9 , 0x7C } ,
{ 0xb3 , 0xaf } ,
{ 0xb4 , 0x97 } ,
{ 0xb5 , 0xFF } ,
{ 0xb0 , 0xC5 } ,
{ 0xb1 , 0x94 } ,
{ 0xb2 , 0x0f } ,
{ 0xc4 , 0x5c } ,
{ 0xa6 , 0x00 } ,
{ 0xa7 , 0x20 } ,
{ 0xa7 , 0xd8 } ,
{ 0xa7 , 0x1b } ,
{ 0xa7 , 0x31 } ,
{ 0xa7 , 0x00 } ,
{ 0xa7 , 0x18 } ,
{ 0xa7 , 0x20 } ,
{ 0xa7 , 0xd8 } ,
{ 0xa7 , 0x19 } ,
{ 0xa7 , 0x31 } ,
{ 0xa7 , 0x00 } ,
{ 0xa7 , 0x18 } ,
{ 0xa7 , 0x20 } ,
{ 0xa7 , 0xd8 } ,
{ 0xa7 , 0x19 } ,
{ 0xa7 , 0x31 } ,
{ 0xa7 , 0x00 } ,
{ 0xa7 , 0x18 } ,
{ 0x7f , 0x00 } ,
{ 0xe5 , 0x1f } ,
{ 0xe1 , 0x77 } ,
{ 0xdd , 0x7f } ,
{ CTRL0 , CTRL0_YUV422 | CTRL0_YUV_EN | CTRL0_RGB_EN } ,
ENDMARKER ,
} ;
/*
* Register settings for window size
* The preamble , setup the internal DSP to input an UXGA ( 1600 x1200 ) image .
* Then the different zooming configurations will setup the output image size .
*/
static const struct regval_list ov2640_size_change_preamble_regs [ ] = {
{ BANK_SEL , BANK_SEL_DSP } ,
{ RESET , RESET_DVP } ,
{ HSIZE8 , HSIZE8_SET ( W_UXGA ) } ,
{ VSIZE8 , VSIZE8_SET ( H_UXGA ) } ,
{ CTRL2 , CTRL2_DCW_EN | CTRL2_SDE_EN |
CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN } ,
{ HSIZE , HSIZE_SET ( W_UXGA ) } ,
{ VSIZE , VSIZE_SET ( H_UXGA ) } ,
{ XOFFL , XOFFL_SET ( 0 ) } ,
{ YOFFL , YOFFL_SET ( 0 ) } ,
{ VHYX , VHYX_HSIZE_SET ( W_UXGA ) | VHYX_VSIZE_SET ( H_UXGA ) |
VHYX_XOFF_SET ( 0 ) | VHYX_YOFF_SET ( 0 ) } ,
{ TEST , TEST_HSIZE_SET ( W_UXGA ) } ,
ENDMARKER ,
} ;
# define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \
{ CTRLI , CTRLI_LP_DP | CTRLI_V_DIV_SET ( v_div ) | \
CTRLI_H_DIV_SET ( h_div ) } , \
{ ZMOW , ZMOW_OUTW_SET ( x ) } , \
{ ZMOH , ZMOH_OUTH_SET ( y ) } , \
{ ZMHH , ZMHH_OUTW_SET ( x ) | ZMHH_OUTH_SET ( y ) } , \
{ R_DVP_SP , pclk_div } , \
{ RESET , 0x00 }
static const struct regval_list ov2640_qcif_regs [ ] = {
PER_SIZE_REG_SEQ ( W_QCIF , H_QCIF , 3 , 3 , 4 ) ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_qvga_regs [ ] = {
PER_SIZE_REG_SEQ ( W_QVGA , H_QVGA , 2 , 2 , 4 ) ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_cif_regs [ ] = {
PER_SIZE_REG_SEQ ( W_CIF , H_CIF , 2 , 2 , 8 ) ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_vga_regs [ ] = {
PER_SIZE_REG_SEQ ( W_VGA , H_VGA , 0 , 0 , 2 ) ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_svga_regs [ ] = {
PER_SIZE_REG_SEQ ( W_SVGA , H_SVGA , 1 , 1 , 2 ) ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_xga_regs [ ] = {
PER_SIZE_REG_SEQ ( W_XGA , H_XGA , 0 , 0 , 2 ) ,
{ CTRLI , 0x00 } ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_sxga_regs [ ] = {
PER_SIZE_REG_SEQ ( W_SXGA , H_SXGA , 0 , 0 , 2 ) ,
{ CTRLI , 0x00 } ,
{ R_DVP_SP , 2 | R_DVP_SP_AUTO_MODE } ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_uxga_regs [ ] = {
PER_SIZE_REG_SEQ ( W_UXGA , H_UXGA , 0 , 0 , 0 ) ,
{ CTRLI , 0x00 } ,
{ R_DVP_SP , 0 | R_DVP_SP_AUTO_MODE } ,
ENDMARKER ,
} ;
# define OV2640_SIZE(n, w, h, r) \
{ . name = n , . width = w , . height = h , . regs = r }
static const struct ov2640_win_size ov2640_supported_win_sizes [ ] = {
OV2640_SIZE ( " QCIF " , W_QCIF , H_QCIF , ov2640_qcif_regs ) ,
OV2640_SIZE ( " QVGA " , W_QVGA , H_QVGA , ov2640_qvga_regs ) ,
OV2640_SIZE ( " CIF " , W_CIF , H_CIF , ov2640_cif_regs ) ,
OV2640_SIZE ( " VGA " , W_VGA , H_VGA , ov2640_vga_regs ) ,
OV2640_SIZE ( " SVGA " , W_SVGA , H_SVGA , ov2640_svga_regs ) ,
OV2640_SIZE ( " XGA " , W_XGA , H_XGA , ov2640_xga_regs ) ,
OV2640_SIZE ( " SXGA " , W_SXGA , H_SXGA , ov2640_sxga_regs ) ,
OV2640_SIZE ( " UXGA " , W_UXGA , H_UXGA , ov2640_uxga_regs ) ,
} ;
/*
* Register settings for pixel formats
*/
static const struct regval_list ov2640_format_change_preamble_regs [ ] = {
{ BANK_SEL , BANK_SEL_DSP } ,
{ R_BYPASS , R_BYPASS_USE_DSP } ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_yuv422_regs [ ] = {
{ IMAGE_MODE , IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 } ,
{ 0xD7 , 0x01 } ,
{ 0x33 , 0xa0 } ,
{ 0xe1 , 0x67 } ,
{ RESET , 0x00 } ,
{ R_BYPASS , R_BYPASS_USE_DSP } ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_rgb565_regs [ ] = {
{ IMAGE_MODE , IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 } ,
{ 0xd7 , 0x03 } ,
{ RESET , 0x00 } ,
{ R_BYPASS , R_BYPASS_USE_DSP } ,
ENDMARKER ,
} ;
static enum v4l2_mbus_pixelcode ov2640_codes [ ] = {
V4L2_MBUS_FMT_UYVY8_2X8 ,
V4L2_MBUS_FMT_RGB565_2X8_LE ,
} ;
/*
* General functions
*/
static struct ov2640_priv * to_ov2640 ( const struct i2c_client * client )
{
return container_of ( i2c_get_clientdata ( client ) , struct ov2640_priv ,
subdev ) ;
}
static int ov2640_write_array ( struct i2c_client * client ,
const struct regval_list * vals )
{
int ret ;
while ( ( vals - > reg_num ! = 0xff ) | | ( vals - > value ! = 0xff ) ) {
ret = i2c_smbus_write_byte_data ( client ,
vals - > reg_num , vals - > value ) ;
dev_vdbg ( & client - > dev , " array: 0x%02x, 0x%02x " ,
vals - > reg_num , vals - > value ) ;
if ( ret < 0 )
return ret ;
vals + + ;
}
return 0 ;
}
static int ov2640_mask_set ( struct i2c_client * client ,
u8 reg , u8 mask , u8 set )
{
s32 val = i2c_smbus_read_byte_data ( client , reg ) ;
if ( val < 0 )
return val ;
val & = ~ mask ;
val | = set & mask ;
dev_vdbg ( & client - > dev , " masks: 0x%02x, 0x%02x " , reg , val ) ;
return i2c_smbus_write_byte_data ( client , reg , val ) ;
}
static int ov2640_reset ( struct i2c_client * client )
{
int ret ;
const struct regval_list reset_seq [ ] = {
{ BANK_SEL , BANK_SEL_SENS } ,
{ COM7 , COM7_SRST } ,
ENDMARKER ,
} ;
ret = ov2640_write_array ( client , reset_seq ) ;
if ( ret )
goto err ;
msleep ( 5 ) ;
err :
dev_dbg ( & client - > dev , " %s: (ret %d) " , __func__ , ret ) ;
return ret ;
}
/*
* soc_camera_ops functions
*/
static int ov2640_s_stream ( struct v4l2_subdev * sd , int enable )
{
return 0 ;
}
2011-09-07 12:43:05 +04:00
static int ov2640_s_ctrl ( struct v4l2_ctrl * ctrl )
2010-12-02 17:53:28 +03:00
{
2011-09-07 12:43:05 +04:00
struct v4l2_subdev * sd =
& container_of ( ctrl - > handler , struct ov2640_priv , hdl ) - > subdev ;
2010-12-02 17:53:28 +03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
u8 val ;
switch ( ctrl - > id ) {
case V4L2_CID_VFLIP :
2011-09-07 12:43:05 +04:00
val = ctrl - > val ? REG04_VFLIP_IMG : 0x00 ;
return ov2640_mask_set ( client , REG04 , REG04_VFLIP_IMG , val ) ;
2010-12-02 17:53:28 +03:00
case V4L2_CID_HFLIP :
2011-09-07 12:43:05 +04:00
val = ctrl - > val ? REG04_HFLIP_IMG : 0x00 ;
return ov2640_mask_set ( client , REG04 , REG04_HFLIP_IMG , val ) ;
2010-12-02 17:53:28 +03:00
}
2011-09-07 12:43:05 +04:00
return - EINVAL ;
2010-12-02 17:53:28 +03:00
}
static int ov2640_g_chip_ident ( struct v4l2_subdev * sd ,
struct v4l2_dbg_chip_ident * id )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct ov2640_priv * priv = to_ov2640 ( client ) ;
id - > ident = priv - > model ;
id - > revision = 0 ;
return 0 ;
}
# ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov2640_g_register ( struct v4l2_subdev * sd ,
struct v4l2_dbg_register * reg )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
int ret ;
reg - > size = 1 ;
if ( reg - > reg > 0xff )
return - EINVAL ;
ret = i2c_smbus_read_byte_data ( client , reg - > reg ) ;
if ( ret < 0 )
return ret ;
reg - > val = ret ;
return 0 ;
}
static int ov2640_s_register ( struct v4l2_subdev * sd ,
struct v4l2_dbg_register * reg )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
if ( reg - > reg > 0xff | |
reg - > val > 0xff )
return - EINVAL ;
return i2c_smbus_write_byte_data ( client , reg - > reg , reg - > val ) ;
}
# endif
/* Select the nearest higher resolution for capture */
static const struct ov2640_win_size * ov2640_select_win ( u32 * width , u32 * height )
{
int i , default_size = ARRAY_SIZE ( ov2640_supported_win_sizes ) - 1 ;
for ( i = 0 ; i < ARRAY_SIZE ( ov2640_supported_win_sizes ) ; i + + ) {
if ( ov2640_supported_win_sizes [ i ] . width > = * width & &
ov2640_supported_win_sizes [ i ] . height > = * height ) {
* width = ov2640_supported_win_sizes [ i ] . width ;
* height = ov2640_supported_win_sizes [ i ] . height ;
return & ov2640_supported_win_sizes [ i ] ;
}
}
* width = ov2640_supported_win_sizes [ default_size ] . width ;
* height = ov2640_supported_win_sizes [ default_size ] . height ;
return & ov2640_supported_win_sizes [ default_size ] ;
}
static int ov2640_set_params ( struct i2c_client * client , u32 * width , u32 * height ,
enum v4l2_mbus_pixelcode code )
{
struct ov2640_priv * priv = to_ov2640 ( client ) ;
const struct regval_list * selected_cfmt_regs ;
int ret ;
/* select win */
priv - > win = ov2640_select_win ( width , height ) ;
/* select format */
priv - > cfmt_code = 0 ;
switch ( code ) {
case V4L2_MBUS_FMT_RGB565_2X8_LE :
dev_dbg ( & client - > dev , " %s: Selected cfmt RGB565 " , __func__ ) ;
selected_cfmt_regs = ov2640_rgb565_regs ;
break ;
default :
case V4L2_MBUS_FMT_UYVY8_2X8 :
dev_dbg ( & client - > dev , " %s: Selected cfmt YUV422 " , __func__ ) ;
selected_cfmt_regs = ov2640_yuv422_regs ;
}
/* reset hardware */
ov2640_reset ( client ) ;
/* initialize the sensor with default data */
dev_dbg ( & client - > dev , " %s: Init default " , __func__ ) ;
ret = ov2640_write_array ( client , ov2640_init_regs ) ;
if ( ret < 0 )
goto err ;
/* select preamble */
dev_dbg ( & client - > dev , " %s: Set size to %s " , __func__ , priv - > win - > name ) ;
ret = ov2640_write_array ( client , ov2640_size_change_preamble_regs ) ;
if ( ret < 0 )
goto err ;
/* set size win */
ret = ov2640_write_array ( client , priv - > win - > regs ) ;
if ( ret < 0 )
goto err ;
/* cfmt preamble */
dev_dbg ( & client - > dev , " %s: Set cfmt " , __func__ ) ;
ret = ov2640_write_array ( client , ov2640_format_change_preamble_regs ) ;
if ( ret < 0 )
goto err ;
/* set cfmt */
ret = ov2640_write_array ( client , selected_cfmt_regs ) ;
if ( ret < 0 )
goto err ;
priv - > cfmt_code = code ;
* width = priv - > win - > width ;
* height = priv - > win - > height ;
return 0 ;
err :
dev_err ( & client - > dev , " %s: Error %d " , __func__ , ret ) ;
ov2640_reset ( client ) ;
priv - > win = NULL ;
return ret ;
}
static int ov2640_g_fmt ( struct v4l2_subdev * sd ,
struct v4l2_mbus_framefmt * mf )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct ov2640_priv * priv = to_ov2640 ( client ) ;
if ( ! priv - > win ) {
u32 width = W_SVGA , height = H_SVGA ;
int ret = ov2640_set_params ( client , & width , & height ,
V4L2_MBUS_FMT_UYVY8_2X8 ) ;
if ( ret < 0 )
return ret ;
}
mf - > width = priv - > win - > width ;
mf - > height = priv - > win - > height ;
mf - > code = priv - > cfmt_code ;
switch ( mf - > code ) {
case V4L2_MBUS_FMT_RGB565_2X8_LE :
mf - > colorspace = V4L2_COLORSPACE_SRGB ;
break ;
default :
case V4L2_MBUS_FMT_UYVY8_2X8 :
mf - > colorspace = V4L2_COLORSPACE_JPEG ;
}
mf - > field = V4L2_FIELD_NONE ;
return 0 ;
}
static int ov2640_s_fmt ( struct v4l2_subdev * sd ,
struct v4l2_mbus_framefmt * mf )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
int ret ;
switch ( mf - > code ) {
case V4L2_MBUS_FMT_RGB565_2X8_LE :
mf - > colorspace = V4L2_COLORSPACE_SRGB ;
break ;
default :
mf - > code = V4L2_MBUS_FMT_UYVY8_2X8 ;
case V4L2_MBUS_FMT_UYVY8_2X8 :
mf - > colorspace = V4L2_COLORSPACE_JPEG ;
}
ret = ov2640_set_params ( client , & mf - > width , & mf - > height , mf - > code ) ;
return ret ;
}
static int ov2640_try_fmt ( struct v4l2_subdev * sd ,
struct v4l2_mbus_framefmt * mf )
{
const struct ov2640_win_size * win ;
/*
* select suitable win
*/
win = ov2640_select_win ( & mf - > width , & mf - > height ) ;
mf - > field = V4L2_FIELD_NONE ;
switch ( mf - > code ) {
case V4L2_MBUS_FMT_RGB565_2X8_LE :
mf - > colorspace = V4L2_COLORSPACE_SRGB ;
break ;
default :
mf - > code = V4L2_MBUS_FMT_UYVY8_2X8 ;
case V4L2_MBUS_FMT_UYVY8_2X8 :
mf - > colorspace = V4L2_COLORSPACE_JPEG ;
}
return 0 ;
}
static int ov2640_enum_fmt ( struct v4l2_subdev * sd , unsigned int index ,
enum v4l2_mbus_pixelcode * code )
{
if ( index > = ARRAY_SIZE ( ov2640_codes ) )
return - EINVAL ;
* code = ov2640_codes [ index ] ;
return 0 ;
}
static int ov2640_g_crop ( struct v4l2_subdev * sd , struct v4l2_crop * a )
{
a - > c . left = 0 ;
a - > c . top = 0 ;
a - > c . width = W_UXGA ;
a - > c . height = H_UXGA ;
a - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
return 0 ;
}
static int ov2640_cropcap ( struct v4l2_subdev * sd , struct v4l2_cropcap * a )
{
a - > bounds . left = 0 ;
a - > bounds . top = 0 ;
a - > bounds . width = W_UXGA ;
a - > bounds . height = H_UXGA ;
a - > defrect = a - > bounds ;
a - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
a - > pixelaspect . numerator = 1 ;
a - > pixelaspect . denominator = 1 ;
return 0 ;
}
2011-09-21 22:16:30 +04:00
static int ov2640_video_probe ( struct i2c_client * client )
2010-12-02 17:53:28 +03:00
{
struct ov2640_priv * priv = to_ov2640 ( client ) ;
u8 pid , ver , midh , midl ;
const char * devname ;
int ret ;
/*
* check and show product ID and manufacturer ID
*/
i2c_smbus_write_byte_data ( client , BANK_SEL , BANK_SEL_SENS ) ;
pid = i2c_smbus_read_byte_data ( client , PID ) ;
ver = i2c_smbus_read_byte_data ( client , VER ) ;
midh = i2c_smbus_read_byte_data ( client , MIDH ) ;
midl = i2c_smbus_read_byte_data ( client , MIDL ) ;
switch ( VERSION ( pid , ver ) ) {
case PID_OV2640 :
devname = " ov2640 " ;
priv - > model = V4L2_IDENT_OV2640 ;
break ;
default :
dev_err ( & client - > dev ,
" Product ID error %x:%x \n " , pid , ver ) ;
ret = - ENODEV ;
goto err ;
}
dev_info ( & client - > dev ,
" %s Product ID %0x:%0x Manufacturer ID %x:%x \n " ,
devname , pid , ver , midh , midl ) ;
2011-09-07 12:43:05 +04:00
return v4l2_ctrl_handler_setup ( & priv - > hdl ) ;
2010-12-02 17:53:28 +03:00
err :
return ret ;
}
2011-09-07 12:43:05 +04:00
static const struct v4l2_ctrl_ops ov2640_ctrl_ops = {
. s_ctrl = ov2640_s_ctrl ,
2010-12-02 17:53:28 +03:00
} ;
static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
. g_chip_ident = ov2640_g_chip_ident ,
# ifdef CONFIG_VIDEO_ADV_DEBUG
. g_register = ov2640_g_register ,
. s_register = ov2640_s_register ,
# endif
} ;
2011-07-26 18:59:53 +04:00
static int ov2640_g_mbus_config ( struct v4l2_subdev * sd ,
struct v4l2_mbus_config * cfg )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2011-09-21 22:16:30 +04:00
struct soc_camera_link * icl = soc_camera_i2c_to_link ( client ) ;
2011-07-26 18:59:53 +04:00
cfg - > flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
V4L2_MBUS_DATA_ACTIVE_HIGH ;
cfg - > type = V4L2_MBUS_PARALLEL ;
cfg - > flags = soc_camera_apply_board_flags ( icl , cfg ) ;
return 0 ;
}
2010-12-02 17:53:28 +03:00
static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
. s_stream = ov2640_s_stream ,
. g_mbus_fmt = ov2640_g_fmt ,
. s_mbus_fmt = ov2640_s_fmt ,
. try_mbus_fmt = ov2640_try_fmt ,
. cropcap = ov2640_cropcap ,
. g_crop = ov2640_g_crop ,
. enum_mbus_fmt = ov2640_enum_fmt ,
2011-07-26 18:59:53 +04:00
. g_mbus_config = ov2640_g_mbus_config ,
2010-12-02 17:53:28 +03:00
} ;
static struct v4l2_subdev_ops ov2640_subdev_ops = {
. core = & ov2640_subdev_core_ops ,
. video = & ov2640_subdev_video_ops ,
} ;
/*
* i2c_driver functions
*/
static int ov2640_probe ( struct i2c_client * client ,
const struct i2c_device_id * did )
{
2011-09-21 22:16:30 +04:00
struct ov2640_priv * priv ;
struct soc_camera_link * icl = soc_camera_i2c_to_link ( client ) ;
struct i2c_adapter * adapter = to_i2c_adapter ( client - > dev . parent ) ;
int ret ;
2010-12-02 17:53:28 +03:00
if ( ! icl ) {
dev_err ( & adapter - > dev ,
" OV2640: Missing platform_data for driver \n " ) ;
return - EINVAL ;
}
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) ) {
dev_err ( & adapter - > dev ,
" OV2640: I2C-Adapter doesn't support SMBUS \n " ) ;
return - EIO ;
}
priv = kzalloc ( sizeof ( struct ov2640_priv ) , GFP_KERNEL ) ;
if ( ! priv ) {
dev_err ( & adapter - > dev ,
" Failed to allocate memory for private data! \n " ) ;
return - ENOMEM ;
}
v4l2_i2c_subdev_init ( & priv - > subdev , client , & ov2640_subdev_ops ) ;
2011-09-07 12:43:05 +04:00
v4l2_ctrl_handler_init ( & priv - > hdl , 2 ) ;
v4l2_ctrl_new_std ( & priv - > hdl , & ov2640_ctrl_ops ,
V4L2_CID_VFLIP , 0 , 1 , 1 , 0 ) ;
v4l2_ctrl_new_std ( & priv - > hdl , & ov2640_ctrl_ops ,
V4L2_CID_HFLIP , 0 , 1 , 1 , 0 ) ;
priv - > subdev . ctrl_handler = & priv - > hdl ;
if ( priv - > hdl . error ) {
int err = priv - > hdl . error ;
2010-12-02 17:53:28 +03:00
2011-09-07 12:43:05 +04:00
kfree ( priv ) ;
return err ;
}
2010-12-02 17:53:28 +03:00
2011-09-21 22:16:30 +04:00
ret = ov2640_video_probe ( client ) ;
2010-12-02 17:53:28 +03:00
if ( ret ) {
2011-09-07 12:43:05 +04:00
v4l2_ctrl_handler_free ( & priv - > hdl ) ;
2010-12-02 17:53:28 +03:00
kfree ( priv ) ;
} else {
dev_info ( & adapter - > dev , " OV2640 Probed \n " ) ;
}
return ret ;
}
static int ov2640_remove ( struct i2c_client * client )
{
struct ov2640_priv * priv = to_ov2640 ( client ) ;
2011-09-07 12:43:05 +04:00
v4l2_device_unregister_subdev ( & priv - > subdev ) ;
v4l2_ctrl_handler_free ( & priv - > hdl ) ;
2010-12-02 17:53:28 +03:00
kfree ( priv ) ;
return 0 ;
}
static const struct i2c_device_id ov2640_id [ ] = {
{ " ov2640 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ov2640_id ) ;
static struct i2c_driver ov2640_i2c_driver = {
. driver = {
. name = " ov2640 " ,
} ,
. probe = ov2640_probe ,
. remove = ov2640_remove ,
. id_table = ov2640_id ,
} ;
2012-02-12 13:56:32 +04:00
module_i2c_driver ( ov2640_i2c_driver ) ;
2010-12-02 17:53:28 +03:00
MODULE_DESCRIPTION ( " SoC Camera driver for Omni Vision 2640 sensor " ) ;
MODULE_AUTHOR ( " Alberto Panizzo " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;