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>
2016-11-24 10:41:42 +03:00
# include <linux/clk.h>
2010-12-02 17:53:28 +03:00
# include <linux/slab.h>
# include <linux/delay.h>
2015-02-10 12:31:35 +03:00
# include <linux/gpio.h>
2015-04-03 11:15:09 +03:00
# include <linux/gpio/consumer.h>
2015-02-10 12:31:35 +03:00
# include <linux/of_gpio.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
2016-11-22 19:44:37 +03:00
# include <media/v4l2-device.h>
2018-11-12 19:00:51 +03:00
# include <media/v4l2-event.h>
2011-07-26 18:59:53 +04:00
# include <media/v4l2-subdev.h>
2011-09-07 12:43:05 +04:00
# include <media/v4l2-ctrls.h>
2014-11-25 11:54:28 +03:00
# include <media/v4l2-image-sizes.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
2017-04-16 20:35:42 +03:00
/* REG 0xC7 (unknown name): affects Auto White Balance (AWB)
* AWB_OFF 0x40
* AWB_SIMPLE 0x10
* AWB_ON 0x00 ( Advanced AWB ? ) */
2010-12-02 17:53:28 +03:00
# 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 */
2017-04-16 20:35:40 +03:00
# define COM8_DEF 0xC0
2010-12-02 17:53:28 +03:00
# 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 */
2017-04-16 20:35:41 +03:00
# define COM25_50HZ_BANDING_AEC_MSBS_MASK 0xC0 /* 50Hz Bd. AEC 2 MSBs */
# define COM25_60HZ_BANDING_AEC_MSBS_MASK 0x30 /* 60Hz Bd. AEC 2 MSBs */
# define COM25_50HZ_BANDING_AEC_MSBS_SET(x) VAL_SET(x, 0x3, 8, 6)
# define COM25_60HZ_BANDING_AEC_MSBS_SET(x) VAL_SET(x, 0x3, 8, 4)
2010-12-02 17:53:28 +03:00
# define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */
2017-04-16 20:35:41 +03:00
# define BD50_50HZ_BANDING_AEC_LSBS_SET(x) VAL_SET(x, 0xFF, 0, 0)
2010-12-02 17:53:28 +03:00
# define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */
2017-04-16 20:35:41 +03:00
# define BD60_60HZ_BANDING_AEC_LSBS_SET(x) VAL_SET(x, 0xFF, 0, 0)
# define REG5A 0x5A /* 50/60Hz Banding Maximum AEC Step */
# define BD50_MAX_AEC_STEP_MASK 0xF0 /* 50Hz Banding Max. AEC Step */
# define BD60_MAX_AEC_STEP_MASK 0x0F /* 60Hz Banding Max. AEC Step */
# define BD50_MAX_AEC_STEP_SET(x) VAL_SET((x - 1), 0x0F, 0, 4)
# define BD60_MAX_AEC_STEP_SET(x) VAL_SET((x - 1), 0x0F, 0, 0)
2010-12-02 17:53:28 +03:00
# 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 ;
} ;
struct ov2640_win_size {
char * name ;
2014-11-25 11:54:28 +03:00
u32 width ;
u32 height ;
2010-12-02 17:53:28 +03:00
const struct regval_list * regs ;
} ;
struct ov2640_priv {
struct v4l2_subdev subdev ;
2017-01-30 16:50:45 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
struct media_pad pad ;
# endif
2011-09-07 12:43:05 +04:00
struct v4l2_ctrl_handler hdl ;
2014-11-10 20:28:29 +03:00
u32 cfmt_code ;
2016-11-24 10:41:42 +03:00
struct clk * clk ;
2010-12-02 17:53:28 +03:00
const struct ov2640_win_size * win ;
2015-02-10 12:31:35 +03:00
struct gpio_desc * resetb_gpio ;
struct gpio_desc * pwdn_gpio ;
2018-02-10 18:28:37 +03:00
2018-02-10 18:28:38 +03:00
struct mutex lock ; /* lock to protect streaming and power_count */
2018-02-10 18:28:37 +03:00
bool streaming ;
2018-02-10 18:28:38 +03:00
int power_count ;
2010-12-02 17:53:28 +03:00
} ;
/*
* 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 } ,
2017-04-16 20:35:40 +03:00
{ 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 } ,
2010-12-02 17:53:28 +03:00
{ 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 } ,
2017-04-16 20:35:40 +03:00
{ COM7 , COM7_RES_UXGA | COM7_ZOOM_EN } ,
2017-04-16 20:35:41 +03:00
{ REG5A , BD50_MAX_AEC_STEP_SET ( 6 )
| BD60_MAX_AEC_STEP_SET ( 8 ) } , /* 0x57 */
{ COM25 , COM25_50HZ_BANDING_AEC_MSBS_SET ( 0x0bb )
| COM25_60HZ_BANDING_AEC_MSBS_SET ( 0x09c ) } , /* 0x00 */
{ BD50 , BD50_50HZ_BANDING_AEC_LSBS_SET ( 0x0bb ) } , /* 0xbb */
{ BD60 , BD60_60HZ_BANDING_AEC_LSBS_SET ( 0x09c ) } , /* 0x9c */
2017-04-16 20:35:40 +03:00
{ BANK_SEL , BANK_SEL_DSP } ,
2010-12-02 17:53:28 +03:00
{ 0xe5 , 0x7f } ,
2017-04-16 20:35:40 +03:00
{ MC_BIST , MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL } ,
2010-12-02 17:53:28 +03:00
{ 0x41 , 0x24 } ,
2017-04-16 20:35:40 +03:00
{ RESET , RESET_JPEG | RESET_DVP } ,
2010-12-02 17:53:28 +03:00
{ 0x76 , 0xff } ,
{ 0x33 , 0xa0 } ,
{ 0x42 , 0x20 } ,
{ 0x43 , 0x18 } ,
{ 0x4c , 0x00 } ,
2017-04-16 20:35:40 +03:00
{ CTRL3 , CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 } ,
2010-12-02 17:53:28 +03:00
{ 0x88 , 0x3f } ,
{ 0xd7 , 0x03 } ,
{ 0xd9 , 0x10 } ,
2017-04-16 20:35:40 +03:00
{ R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 } ,
2010-12-02 17:53:28 +03:00
{ 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 } ,
2017-04-16 20:35:42 +03:00
{ 0xc7 , 0x10 } , /* simple AWB */
2010-12-02 17:53:28 +03:00
{ 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 } ,
2017-04-16 20:35:43 +03:00
{ SIZEL , SIZEL_HSIZE8_11_SET ( UXGA_WIDTH ) |
SIZEL_HSIZE8_SET ( UXGA_WIDTH ) |
SIZEL_VSIZE8_SET ( UXGA_HEIGHT ) } ,
2014-11-25 11:54:28 +03:00
{ HSIZE8 , HSIZE8_SET ( UXGA_WIDTH ) } ,
{ VSIZE8 , VSIZE8_SET ( UXGA_HEIGHT ) } ,
2010-12-02 17:53:28 +03:00
{ CTRL2 , CTRL2_DCW_EN | CTRL2_SDE_EN |
CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN } ,
2014-11-25 11:54:28 +03:00
{ HSIZE , HSIZE_SET ( UXGA_WIDTH ) } ,
{ VSIZE , VSIZE_SET ( UXGA_HEIGHT ) } ,
2010-12-02 17:53:28 +03:00
{ XOFFL , XOFFL_SET ( 0 ) } ,
{ YOFFL , YOFFL_SET ( 0 ) } ,
2014-11-25 11:54:28 +03:00
{ VHYX , VHYX_HSIZE_SET ( UXGA_WIDTH ) | VHYX_VSIZE_SET ( UXGA_HEIGHT ) |
2010-12-02 17:53:28 +03:00
VHYX_XOFF_SET ( 0 ) | VHYX_YOFF_SET ( 0 ) } ,
2014-11-25 11:54:28 +03:00
{ TEST , TEST_HSIZE_SET ( UXGA_WIDTH ) } ,
2010-12-02 17:53:28 +03:00
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 [ ] = {
2014-11-25 11:54:28 +03:00
PER_SIZE_REG_SEQ ( QCIF_WIDTH , QCIF_HEIGHT , 3 , 3 , 4 ) ,
2010-12-02 17:53:28 +03:00
ENDMARKER ,
} ;
static const struct regval_list ov2640_qvga_regs [ ] = {
2014-11-25 11:54:28 +03:00
PER_SIZE_REG_SEQ ( QVGA_WIDTH , QVGA_HEIGHT , 2 , 2 , 4 ) ,
2010-12-02 17:53:28 +03:00
ENDMARKER ,
} ;
static const struct regval_list ov2640_cif_regs [ ] = {
2014-11-25 11:54:28 +03:00
PER_SIZE_REG_SEQ ( CIF_WIDTH , CIF_HEIGHT , 2 , 2 , 8 ) ,
2010-12-02 17:53:28 +03:00
ENDMARKER ,
} ;
static const struct regval_list ov2640_vga_regs [ ] = {
2014-11-25 11:54:28 +03:00
PER_SIZE_REG_SEQ ( VGA_WIDTH , VGA_HEIGHT , 0 , 0 , 2 ) ,
2010-12-02 17:53:28 +03:00
ENDMARKER ,
} ;
static const struct regval_list ov2640_svga_regs [ ] = {
2014-11-25 11:54:28 +03:00
PER_SIZE_REG_SEQ ( SVGA_WIDTH , SVGA_HEIGHT , 1 , 1 , 2 ) ,
2010-12-02 17:53:28 +03:00
ENDMARKER ,
} ;
static const struct regval_list ov2640_xga_regs [ ] = {
2014-11-25 11:54:28 +03:00
PER_SIZE_REG_SEQ ( XGA_WIDTH , XGA_HEIGHT , 0 , 0 , 2 ) ,
2010-12-02 17:53:28 +03:00
{ CTRLI , 0x00 } ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_sxga_regs [ ] = {
2014-11-25 11:54:28 +03:00
PER_SIZE_REG_SEQ ( SXGA_WIDTH , SXGA_HEIGHT , 0 , 0 , 2 ) ,
2010-12-02 17:53:28 +03:00
{ CTRLI , 0x00 } ,
{ R_DVP_SP , 2 | R_DVP_SP_AUTO_MODE } ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_uxga_regs [ ] = {
2014-11-25 11:54:28 +03:00
PER_SIZE_REG_SEQ ( UXGA_WIDTH , UXGA_HEIGHT , 0 , 0 , 0 ) ,
2010-12-02 17:53:28 +03:00
{ 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 [ ] = {
2014-11-25 11:54:28 +03:00
OV2640_SIZE ( " QCIF " , QCIF_WIDTH , QCIF_HEIGHT , ov2640_qcif_regs ) ,
OV2640_SIZE ( " QVGA " , QVGA_WIDTH , QVGA_HEIGHT , ov2640_qvga_regs ) ,
OV2640_SIZE ( " CIF " , CIF_WIDTH , CIF_HEIGHT , ov2640_cif_regs ) ,
OV2640_SIZE ( " VGA " , VGA_WIDTH , VGA_HEIGHT , ov2640_vga_regs ) ,
OV2640_SIZE ( " SVGA " , SVGA_WIDTH , SVGA_HEIGHT , ov2640_svga_regs ) ,
OV2640_SIZE ( " XGA " , XGA_WIDTH , XGA_HEIGHT , ov2640_xga_regs ) ,
OV2640_SIZE ( " SXGA " , SXGA_WIDTH , SXGA_HEIGHT , ov2640_sxga_regs ) ,
OV2640_SIZE ( " UXGA " , UXGA_WIDTH , UXGA_HEIGHT , ov2640_uxga_regs ) ,
2010-12-02 17:53:28 +03:00
} ;
/*
* 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 ,
} ;
2012-09-23 22:28:45 +04:00
static const struct regval_list ov2640_yuyv_regs [ ] = {
{ IMAGE_MODE , IMAGE_MODE_YUV422 } ,
{ 0xd7 , 0x03 } ,
{ 0x33 , 0xa0 } ,
{ 0xe5 , 0x1f } ,
{ 0xe1 , 0x67 } ,
{ RESET , 0x00 } ,
{ R_BYPASS , R_BYPASS_USE_DSP } ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_uyvy_regs [ ] = {
2010-12-02 17:53:28 +03:00
{ IMAGE_MODE , IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 } ,
2012-09-23 22:28:45 +04:00
{ 0xd7 , 0x01 } ,
2010-12-02 17:53:28 +03:00
{ 0x33 , 0xa0 } ,
{ 0xe1 , 0x67 } ,
{ RESET , 0x00 } ,
{ R_BYPASS , R_BYPASS_USE_DSP } ,
ENDMARKER ,
} ;
2012-09-23 22:28:45 +04:00
static const struct regval_list ov2640_rgb565_be_regs [ ] = {
{ IMAGE_MODE , IMAGE_MODE_RGB565 } ,
{ 0xd7 , 0x03 } ,
{ RESET , 0x00 } ,
{ R_BYPASS , R_BYPASS_USE_DSP } ,
ENDMARKER ,
} ;
static const struct regval_list ov2640_rgb565_le_regs [ ] = {
2010-12-02 17:53:28 +03:00
{ IMAGE_MODE , IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 } ,
{ 0xd7 , 0x03 } ,
{ RESET , 0x00 } ,
{ R_BYPASS , R_BYPASS_USE_DSP } ,
ENDMARKER ,
} ;
2014-11-10 20:28:29 +03:00
static u32 ov2640_codes [ ] = {
MEDIA_BUS_FMT_YUYV8_2X8 ,
MEDIA_BUS_FMT_UYVY8_2X8 ,
2017-04-16 20:35:46 +03:00
MEDIA_BUS_FMT_YVYU8_2X8 ,
MEDIA_BUS_FMT_VYUY8_2X8 ,
2014-11-10 20:28:29 +03:00
MEDIA_BUS_FMT_RGB565_2X8_BE ,
MEDIA_BUS_FMT_RGB565_2X8_LE ,
2010-12-02 17:53:28 +03:00
} ;
/*
* 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 ;
2017-09-12 12:11:15 +03:00
static const struct regval_list reset_seq [ ] = {
2010-12-02 17:53:28 +03:00
{ 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 ;
}
2018-11-12 19:00:50 +03:00
static const char * const ov2640_test_pattern_menu [ ] = {
" Disabled " ,
" Eight Vertical Colour Bars " ,
} ;
2010-12-02 17:53:28 +03:00
/*
2016-11-22 19:44:37 +03:00
* functions
2010-12-02 17:53:28 +03:00
*/
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 ) ;
2018-02-10 18:28:38 +03:00
struct ov2640_priv * priv = to_ov2640 ( client ) ;
2010-12-02 17:53:28 +03:00
u8 val ;
2012-09-24 00:16:34 +04:00
int ret ;
2018-02-10 18:28:38 +03:00
/* v4l2_ctrl_lock() locks our own mutex */
/*
* If the device is not powered up by the host driver , do not apply any
* controls to H / W at this time . Instead the controls will be restored
* when the streaming is started .
*/
if ( ! priv - > power_count )
return 0 ;
2012-09-24 00:16:34 +04:00
ret = i2c_smbus_write_byte_data ( client , BANK_SEL , BANK_SEL_SENS ) ;
if ( ret < 0 )
return ret ;
2010-12-02 17:53:28 +03:00
switch ( ctrl - > id ) {
case V4L2_CID_VFLIP :
2017-04-16 20:35:45 +03:00
val = ctrl - > val ? REG04_VFLIP_IMG | REG04_VREF_EN : 0x00 ;
return ov2640_mask_set ( client , REG04 ,
REG04_VFLIP_IMG | REG04_VREF_EN , val ) ;
/* NOTE: REG04_VREF_EN: 1 line shift / even/odd line swap */
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 ) ;
2018-11-12 19:00:50 +03:00
case V4L2_CID_TEST_PATTERN :
val = ctrl - > val ? COM7_COLOR_BAR_TEST : 0x00 ;
return ov2640_mask_set ( client , COM7 , COM7_COLOR_BAR_TEST , 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
}
# 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 ,
2013-03-24 15:28:46 +04:00
const struct v4l2_dbg_register * reg )
2010-12-02 17:53:28 +03:00
{
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
2018-02-10 18:28:38 +03:00
static void ov2640_set_power ( struct ov2640_priv * priv , int on )
2012-07-20 17:19:50 +04:00
{
2017-04-19 15:58:22 +03:00
# ifdef CONFIG_GPIOLIB
2017-04-19 14:43:49 +03:00
if ( priv - > pwdn_gpio )
gpiod_direction_output ( priv - > pwdn_gpio , ! on ) ;
2016-11-22 19:44:37 +03:00
if ( on & & priv - > resetb_gpio ) {
/* Active the resetb pin to perform a reset pulse */
gpiod_direction_output ( priv - > resetb_gpio , 1 ) ;
usleep_range ( 3000 , 5000 ) ;
2017-04-19 15:58:22 +03:00
gpiod_set_value ( priv - > resetb_gpio , 0 ) ;
2016-11-22 19:44:37 +03:00
}
2017-04-19 14:43:49 +03:00
# endif
2018-02-10 18:28:38 +03:00
}
static int ov2640_s_power ( struct v4l2_subdev * sd , int on )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct ov2640_priv * priv = to_ov2640 ( client ) ;
mutex_lock ( & priv - > lock ) ;
/*
* If the power count is modified from 0 to ! = 0 or from ! = 0 to 0 ,
* update the power state .
*/
if ( priv - > power_count = = ! on )
ov2640_set_power ( priv , on ) ;
priv - > power_count + = on ? 1 : - 1 ;
WARN_ON ( priv - > power_count < 0 ) ;
mutex_unlock ( & priv - > lock ) ;
2016-11-22 19:44:37 +03:00
return 0 ;
2012-07-20 17:19:50 +04:00
}
2010-12-02 17:53:28 +03:00
/* Select the nearest higher resolution for capture */
2017-04-16 20:35:44 +03:00
static const struct ov2640_win_size * ov2640_select_win ( u32 width , u32 height )
2010-12-02 17:53:28 +03:00
{
int i , default_size = ARRAY_SIZE ( ov2640_supported_win_sizes ) - 1 ;
for ( i = 0 ; i < ARRAY_SIZE ( ov2640_supported_win_sizes ) ; i + + ) {
2017-04-16 20:35:44 +03:00
if ( ov2640_supported_win_sizes [ i ] . width > = width & &
ov2640_supported_win_sizes [ i ] . height > = height )
2010-12-02 17:53:28 +03:00
return & ov2640_supported_win_sizes [ i ] ;
}
return & ov2640_supported_win_sizes [ default_size ] ;
}
2015-05-04 13:25:51 +03:00
static int ov2640_set_params ( struct i2c_client * client ,
const struct ov2640_win_size * win , u32 code )
2010-12-02 17:53:28 +03:00
{
const struct regval_list * selected_cfmt_regs ;
2017-04-16 20:35:46 +03:00
u8 val ;
2010-12-02 17:53:28 +03:00
int ret ;
switch ( code ) {
2014-11-10 20:28:29 +03:00
case MEDIA_BUS_FMT_RGB565_2X8_BE :
2012-09-23 22:28:45 +04:00
dev_dbg ( & client - > dev , " %s: Selected cfmt RGB565 BE " , __func__ ) ;
selected_cfmt_regs = ov2640_rgb565_be_regs ;
break ;
2014-11-10 20:28:29 +03:00
case MEDIA_BUS_FMT_RGB565_2X8_LE :
2012-09-23 22:28:45 +04:00
dev_dbg ( & client - > dev , " %s: Selected cfmt RGB565 LE " , __func__ ) ;
selected_cfmt_regs = ov2640_rgb565_le_regs ;
break ;
2014-11-10 20:28:29 +03:00
case MEDIA_BUS_FMT_YUYV8_2X8 :
2012-09-23 22:28:45 +04:00
dev_dbg ( & client - > dev , " %s: Selected cfmt YUYV (YUV422) " , __func__ ) ;
selected_cfmt_regs = ov2640_yuyv_regs ;
2010-12-02 17:53:28 +03:00
break ;
2014-11-10 20:28:29 +03:00
case MEDIA_BUS_FMT_UYVY8_2X8 :
2016-08-16 22:56:43 +03:00
default :
2012-09-23 22:28:45 +04:00
dev_dbg ( & client - > dev , " %s: Selected cfmt UYVY " , __func__ ) ;
selected_cfmt_regs = ov2640_uyvy_regs ;
2016-08-16 22:56:43 +03:00
break ;
2017-04-16 20:35:46 +03:00
case MEDIA_BUS_FMT_YVYU8_2X8 :
dev_dbg ( & client - > dev , " %s: Selected cfmt YVYU " , __func__ ) ;
selected_cfmt_regs = ov2640_yuyv_regs ;
break ;
case MEDIA_BUS_FMT_VYUY8_2X8 :
dev_dbg ( & client - > dev , " %s: Selected cfmt VYUY " , __func__ ) ;
selected_cfmt_regs = ov2640_uyvy_regs ;
break ;
2010-12-02 17:53:28 +03:00
}
/* 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 */
2018-02-10 18:28:37 +03:00
dev_dbg ( & client - > dev , " %s: Set size to %s " , __func__ , win - > name ) ;
2010-12-02 17:53:28 +03:00
ret = ov2640_write_array ( client , ov2640_size_change_preamble_regs ) ;
if ( ret < 0 )
goto err ;
/* set size win */
2018-02-10 18:28:37 +03:00
ret = ov2640_write_array ( client , win - > regs ) ;
2010-12-02 17:53:28 +03:00
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 ) ;
2017-04-16 20:35:46 +03:00
if ( ret < 0 )
goto err ;
val = ( code = = MEDIA_BUS_FMT_YVYU8_2X8 )
| | ( code = = MEDIA_BUS_FMT_VYUY8_2X8 ) ? CTRL0_VFIRST : 0x00 ;
ret = ov2640_mask_set ( client , CTRL0 , CTRL0_VFIRST , val ) ;
2010-12-02 17:53:28 +03:00
if ( ret < 0 )
goto err ;
return 0 ;
err :
dev_err ( & client - > dev , " %s: Error %d " , __func__ , ret ) ;
ov2640_reset ( client ) ;
return ret ;
}
2015-04-09 10:02:34 +03:00
static int ov2640_get_fmt ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_format * format )
2010-12-02 17:53:28 +03:00
{
2015-04-09 10:02:34 +03:00
struct v4l2_mbus_framefmt * mf = & format - > format ;
2010-12-02 17:53:28 +03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct ov2640_priv * priv = to_ov2640 ( client ) ;
2015-04-09 10:02:34 +03:00
if ( format - > pad )
return - EINVAL ;
2018-12-09 08:20:32 +03:00
if ( format - > which = = V4L2_SUBDEV_FORMAT_TRY ) {
# ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
mf = v4l2_subdev_get_try_format ( sd , cfg , 0 ) ;
format - > format = * mf ;
return 0 ;
# else
return - ENOTTY ;
# endif
}
2010-12-02 17:53:28 +03:00
mf - > width = priv - > win - > width ;
mf - > height = priv - > win - > height ;
mf - > code = priv - > cfmt_code ;
2016-08-16 22:56:43 +03:00
mf - > colorspace = V4L2_COLORSPACE_SRGB ;
2010-12-02 17:53:28 +03:00
mf - > field = V4L2_FIELD_NONE ;
2018-12-09 08:20:33 +03:00
mf - > ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT ;
mf - > quantization = V4L2_QUANTIZATION_DEFAULT ;
mf - > xfer_func = V4L2_XFER_FUNC_DEFAULT ;
2010-12-02 17:53:28 +03:00
return 0 ;
}
2015-04-09 12:24:36 +03:00
static int ov2640_set_fmt ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_format * format )
2010-12-02 17:53:28 +03:00
{
2015-04-09 12:24:36 +03:00
struct v4l2_mbus_framefmt * mf = & format - > format ;
2010-12-02 17:53:28 +03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2018-02-10 18:28:37 +03:00
struct ov2640_priv * priv = to_ov2640 ( client ) ;
2015-05-04 13:25:51 +03:00
const struct ov2640_win_size * win ;
2018-02-10 18:28:37 +03:00
int ret = 0 ;
2010-12-02 17:53:28 +03:00
2015-04-09 12:24:36 +03:00
if ( format - > pad )
return - EINVAL ;
2010-12-02 17:53:28 +03:00
2018-02-10 18:28:37 +03:00
mutex_lock ( & priv - > lock ) ;
2015-05-04 13:25:51 +03:00
/* select suitable win */
2017-04-16 20:35:44 +03:00
win = ov2640_select_win ( mf - > width , mf - > height ) ;
mf - > width = win - > width ;
mf - > height = win - > height ;
2010-12-02 17:53:28 +03:00
mf - > field = V4L2_FIELD_NONE ;
2016-08-16 22:56:43 +03:00
mf - > colorspace = V4L2_COLORSPACE_SRGB ;
2018-12-09 08:20:33 +03:00
mf - > ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT ;
mf - > quantization = V4L2_QUANTIZATION_DEFAULT ;
mf - > xfer_func = V4L2_XFER_FUNC_DEFAULT ;
2010-12-02 17:53:28 +03:00
switch ( mf - > code ) {
2014-11-10 20:28:29 +03:00
case MEDIA_BUS_FMT_RGB565_2X8_BE :
case MEDIA_BUS_FMT_RGB565_2X8_LE :
2016-08-16 22:56:43 +03:00
case MEDIA_BUS_FMT_YUYV8_2X8 :
case MEDIA_BUS_FMT_UYVY8_2X8 :
2017-04-16 20:35:46 +03:00
case MEDIA_BUS_FMT_YVYU8_2X8 :
case MEDIA_BUS_FMT_VYUY8_2X8 :
2010-12-02 17:53:28 +03:00
break ;
default :
2014-11-10 20:28:29 +03:00
mf - > code = MEDIA_BUS_FMT_UYVY8_2X8 ;
2016-08-16 22:56:43 +03:00
break ;
2010-12-02 17:53:28 +03:00
}
2018-02-10 18:28:37 +03:00
if ( format - > which = = V4L2_SUBDEV_FORMAT_ACTIVE ) {
struct ov2640_priv * priv = to_ov2640 ( client ) ;
if ( priv - > streaming ) {
ret = - EBUSY ;
goto out ;
}
/* select win */
priv - > win = win ;
/* select format */
priv - > cfmt_code = mf - > code ;
} else {
cfg - > try_fmt = * mf ;
}
out :
mutex_unlock ( & priv - > lock ) ;
return ret ;
2010-12-02 17:53:28 +03:00
}
2018-12-09 08:20:32 +03:00
static int ov2640_init_cfg ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg )
{
# ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct ov2640_priv * priv = to_ov2640 ( client ) ;
struct v4l2_mbus_framefmt * try_fmt =
v4l2_subdev_get_try_format ( sd , cfg , 0 ) ;
try_fmt - > width = priv - > win - > width ;
try_fmt - > height = priv - > win - > height ;
try_fmt - > code = priv - > cfmt_code ;
try_fmt - > colorspace = V4L2_COLORSPACE_SRGB ;
try_fmt - > field = V4L2_FIELD_NONE ;
try_fmt - > ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT ;
try_fmt - > quantization = V4L2_QUANTIZATION_DEFAULT ;
try_fmt - > xfer_func = V4L2_XFER_FUNC_DEFAULT ;
# endif
return 0 ;
}
2015-04-09 10:01:33 +03:00
static int ov2640_enum_mbus_code ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_mbus_code_enum * code )
2010-12-02 17:53:28 +03:00
{
2015-04-09 10:01:33 +03:00
if ( code - > pad | | code - > index > = ARRAY_SIZE ( ov2640_codes ) )
2010-12-02 17:53:28 +03:00
return - EINVAL ;
2015-04-09 10:01:33 +03:00
code - > code = ov2640_codes [ code - > index ] ;
2010-12-02 17:53:28 +03:00
return 0 ;
}
2015-12-14 13:25:32 +03:00
static int ov2640_get_selection ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_selection * sel )
2010-12-02 17:53:28 +03:00
{
2015-12-14 13:25:32 +03:00
if ( sel - > which ! = V4L2_SUBDEV_FORMAT_ACTIVE )
return - EINVAL ;
2010-12-02 17:53:28 +03:00
2015-12-14 13:25:32 +03:00
switch ( sel - > target ) {
case V4L2_SEL_TGT_CROP_BOUNDS :
case V4L2_SEL_TGT_CROP :
sel - > r . left = 0 ;
sel - > r . top = 0 ;
sel - > r . width = UXGA_WIDTH ;
sel - > r . height = UXGA_HEIGHT ;
return 0 ;
default :
return - EINVAL ;
}
2010-12-02 17:53:28 +03:00
}
2018-02-10 18:28:37 +03:00
static int ov2640_s_stream ( struct v4l2_subdev * sd , int on )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct ov2640_priv * priv = to_ov2640 ( client ) ;
int ret = 0 ;
mutex_lock ( & priv - > lock ) ;
if ( priv - > streaming = = ! on ) {
if ( on ) {
ret = ov2640_set_params ( client , priv - > win ,
priv - > cfmt_code ) ;
2018-02-10 18:28:38 +03:00
if ( ! ret )
ret = __v4l2_ctrl_handler_setup ( & priv - > hdl ) ;
2018-02-10 18:28:37 +03:00
}
}
if ( ! ret )
priv - > streaming = on ;
mutex_unlock ( & priv - > lock ) ;
return ret ;
}
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 ;
2012-07-18 17:54:04 +04:00
ret = ov2640_s_power ( & priv - > subdev , 1 ) ;
if ( ret < 0 )
return ret ;
2010-12-02 17:53:28 +03:00
/*
* 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 " ;
break ;
default :
dev_err ( & client - > dev ,
" Product ID error %x:%x \n " , pid , ver ) ;
ret = - ENODEV ;
2012-07-18 17:54:04 +04:00
goto done ;
2010-12-02 17:53:28 +03:00
}
dev_info ( & client - > dev ,
" %s Product ID %0x:%0x Manufacturer ID %x:%x \n " ,
devname , pid , ver , midh , midl ) ;
2012-07-18 17:54:04 +04:00
done :
ov2640_s_power ( & priv - > subdev , 0 ) ;
2010-12-02 17:53:28 +03:00
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
} ;
2016-12-12 16:59:42 +03:00
static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
2018-11-12 19:00:51 +03:00
. log_status = v4l2_ctrl_subdev_log_status ,
. subscribe_event = v4l2_ctrl_subdev_subscribe_event ,
. unsubscribe_event = v4l2_event_subdev_unsubscribe ,
2010-12-02 17:53:28 +03:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
. g_register = ov2640_g_register ,
. s_register = ov2640_s_register ,
# endif
2012-07-20 17:19:50 +04:00
. s_power = ov2640_s_power ,
2010-12-02 17:53:28 +03:00
} ;
2015-04-09 10:01:33 +03:00
static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = {
2018-12-09 08:20:32 +03:00
. init_cfg = ov2640_init_cfg ,
2015-04-09 10:01:33 +03:00
. enum_mbus_code = ov2640_enum_mbus_code ,
2015-12-14 13:25:32 +03:00
. get_selection = ov2640_get_selection ,
2015-04-09 10:02:34 +03:00
. get_fmt = ov2640_get_fmt ,
2015-04-09 12:24:36 +03:00
. set_fmt = ov2640_set_fmt ,
2015-04-09 10:01:33 +03:00
} ;
2018-02-10 18:28:37 +03:00
static const struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
. s_stream = ov2640_s_stream ,
} ;
2016-12-12 16:59:42 +03:00
static const struct v4l2_subdev_ops ov2640_subdev_ops = {
2010-12-02 17:53:28 +03:00
. core = & ov2640_subdev_core_ops ,
2015-04-09 10:01:33 +03:00
. pad = & ov2640_subdev_pad_ops ,
2018-02-10 18:28:37 +03:00
. video = & ov2640_subdev_video_ops ,
2010-12-02 17:53:28 +03:00
} ;
2015-02-10 12:31:35 +03:00
static int ov2640_probe_dt ( struct i2c_client * client ,
struct ov2640_priv * priv )
{
2017-04-19 15:58:22 +03:00
int ret ;
2015-02-10 12:31:35 +03:00
/* Request the reset GPIO deasserted */
priv - > resetb_gpio = devm_gpiod_get_optional ( & client - > dev , " resetb " ,
GPIOD_OUT_LOW ) ;
2017-04-19 15:58:22 +03:00
2015-02-10 12:31:35 +03:00
if ( ! priv - > resetb_gpio )
dev_dbg ( & client - > dev , " resetb gpio is not assigned! \n " ) ;
2017-04-19 15:58:22 +03:00
ret = PTR_ERR_OR_ZERO ( priv - > resetb_gpio ) ;
if ( ret & & ret ! = - ENOSYS ) {
dev_dbg ( & client - > dev ,
" Error %d while getting resetb gpio \n " , ret ) ;
return ret ;
}
2015-02-10 12:31:35 +03:00
/* Request the power down GPIO asserted */
priv - > pwdn_gpio = devm_gpiod_get_optional ( & client - > dev , " pwdn " ,
GPIOD_OUT_HIGH ) ;
2017-04-19 15:58:22 +03:00
2015-02-10 12:31:35 +03:00
if ( ! priv - > pwdn_gpio )
dev_dbg ( & client - > dev , " pwdn gpio is not assigned! \n " ) ;
2017-04-19 15:58:22 +03:00
ret = PTR_ERR_OR_ZERO ( priv - > pwdn_gpio ) ;
if ( ret & & ret ! = - ENOSYS ) {
dev_dbg ( & client - > dev ,
" Error %d while getting pwdn gpio \n " , ret ) ;
return ret ;
}
2015-02-10 12:31:35 +03:00
return 0 ;
}
2010-12-02 17:53:28 +03:00
/*
* 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 i2c_adapter * adapter = to_i2c_adapter ( client - > dev . parent ) ;
int ret ;
2010-12-02 17:53:28 +03:00
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) ) {
dev_err ( & adapter - > dev ,
" OV2640: I2C-Adapter doesn't support SMBUS \n " ) ;
return - EIO ;
}
2017-09-02 17:09:35 +03:00
priv = devm_kzalloc ( & client - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2017-09-02 17:07:31 +03:00
if ( ! priv )
2010-12-02 17:53:28 +03:00
return - ENOMEM ;
2016-11-24 10:41:42 +03:00
if ( client - > dev . of_node ) {
priv - > clk = devm_clk_get ( & client - > dev , " xvclk " ) ;
if ( IS_ERR ( priv - > clk ) )
2017-08-27 19:30:37 +03:00
return PTR_ERR ( priv - > clk ) ;
2017-08-27 19:30:38 +03:00
ret = clk_prepare_enable ( priv - > clk ) ;
if ( ret )
return ret ;
2015-02-10 12:31:35 +03:00
}
2016-11-22 19:44:37 +03:00
ret = ov2640_probe_dt ( client , priv ) ;
if ( ret )
goto err_clk ;
2015-02-10 12:31:35 +03:00
2018-12-09 08:20:31 +03:00
priv - > win = ov2640_select_win ( SVGA_WIDTH , SVGA_HEIGHT ) ;
priv - > cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8 ;
2010-12-02 17:53:28 +03:00
v4l2_i2c_subdev_init ( & priv - > subdev , client , & ov2640_subdev_ops ) ;
2018-11-12 19:00:51 +03:00
priv - > subdev . flags | = V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_HAS_EVENTS ;
2018-02-10 18:28:37 +03:00
mutex_init ( & priv - > lock ) ;
2018-11-12 19:00:50 +03:00
v4l2_ctrl_handler_init ( & priv - > hdl , 3 ) ;
2018-02-10 18:28:38 +03:00
priv - > hdl . lock = & priv - > lock ;
2011-09-07 12:43:05 +04:00
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 ) ;
2018-11-12 19:00:50 +03:00
v4l2_ctrl_new_std_menu_items ( & priv - > hdl , & ov2640_ctrl_ops ,
V4L2_CID_TEST_PATTERN ,
ARRAY_SIZE ( ov2640_test_pattern_menu ) - 1 , 0 , 0 ,
ov2640_test_pattern_menu ) ;
2011-09-07 12:43:05 +04:00
priv - > subdev . ctrl_handler = & priv - > hdl ;
2015-03-02 04:52:38 +03:00
if ( priv - > hdl . error ) {
ret = priv - > hdl . error ;
2016-11-24 10:41:42 +03:00
goto err_hdl ;
2012-12-21 20:01:55 +04:00
}
2017-01-30 16:50:45 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
priv - > pad . flags = MEDIA_PAD_FL_SOURCE ;
priv - > subdev . entity . function = MEDIA_ENT_F_CAM_SENSOR ;
ret = media_entity_pads_init ( & priv - > subdev . entity , 1 , & priv - > pad ) ;
if ( ret < 0 )
goto err_hdl ;
# endif
2012-12-21 20:01:55 +04:00
2011-09-21 22:16:30 +04:00
ret = ov2640_video_probe ( client ) ;
2015-03-02 04:52:38 +03:00
if ( ret < 0 )
2017-01-30 16:50:45 +03:00
goto err_videoprobe ;
2010-12-02 17:53:28 +03:00
2015-03-02 04:52:38 +03:00
ret = v4l2_async_register_subdev ( & priv - > subdev ) ;
if ( ret < 0 )
2017-01-30 16:50:45 +03:00
goto err_videoprobe ;
2015-03-02 04:52:38 +03:00
dev_info ( & adapter - > dev , " OV2640 Probed \n " ) ;
return 0 ;
2017-01-30 16:50:45 +03:00
err_videoprobe :
media_entity_cleanup ( & priv - > subdev . entity ) ;
2016-11-24 10:41:42 +03:00
err_hdl :
2015-03-02 04:52:38 +03:00
v4l2_ctrl_handler_free ( & priv - > hdl ) ;
2018-02-10 18:28:37 +03:00
mutex_destroy ( & priv - > lock ) ;
2015-03-02 04:52:38 +03:00
err_clk :
2016-11-24 10:41:42 +03:00
clk_disable_unprepare ( priv - > clk ) ;
2010-12-02 17:53:28 +03:00
return ret ;
}
static int ov2640_remove ( struct i2c_client * client )
{
struct ov2640_priv * priv = to_ov2640 ( client ) ;
2015-03-02 04:52:38 +03:00
v4l2_async_unregister_subdev ( & priv - > subdev ) ;
2011-09-07 12:43:05 +04:00
v4l2_ctrl_handler_free ( & priv - > hdl ) ;
2018-02-10 18:28:37 +03:00
mutex_destroy ( & priv - > lock ) ;
2017-01-30 16:50:45 +03:00
media_entity_cleanup ( & priv - > subdev . entity ) ;
2016-11-24 10:41:42 +03:00
v4l2_device_unregister_subdev ( & priv - > subdev ) ;
clk_disable_unprepare ( priv - > clk ) ;
2010-12-02 17:53:28 +03:00
return 0 ;
}
static const struct i2c_device_id ov2640_id [ ] = {
{ " ov2640 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ov2640_id ) ;
2015-02-10 12:31:35 +03:00
static const struct of_device_id ov2640_of_match [ ] = {
{ . compatible = " ovti,ov2640 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ov2640_of_match ) ;
2010-12-02 17:53:28 +03:00
static struct i2c_driver ov2640_i2c_driver = {
. driver = {
. name = " ov2640 " ,
2015-02-10 12:31:35 +03:00
. of_match_table = of_match_ptr ( ov2640_of_match ) ,
2010-12-02 17:53:28 +03:00
} ,
. 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
2016-11-22 19:44:37 +03:00
MODULE_DESCRIPTION ( " Driver for Omni Vision 2640 sensor " ) ;
2010-12-02 17:53:28 +03:00
MODULE_AUTHOR ( " Alberto Panizzo " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;