2017-06-07 21:33:56 +03:00
/*
* Copyright ( C ) 2011 - 2013 Freescale Semiconductor , Inc . All Rights Reserved .
* Copyright ( C ) 2014 - 2017 Mentor Graphics Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/clk.h>
# include <linux/clk-provider.h>
# include <linux/clkdev.h>
# include <linux/ctype.h>
# include <linux/delay.h>
# include <linux/device.h>
2018-02-01 11:44:06 +03:00
# include <linux/gpio/consumer.h>
2017-06-07 21:33:56 +03:00
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/of_device.h>
2018-02-01 11:44:06 +03:00
# include <linux/regulator/consumer.h>
2017-06-07 21:33:56 +03:00
# include <linux/slab.h>
# include <linux/types.h>
# include <media/v4l2-async.h>
# include <media/v4l2-ctrls.h>
# include <media/v4l2-device.h>
2018-11-12 19:00:52 +03:00
# include <media/v4l2-event.h>
2017-06-07 21:33:56 +03:00
# include <media/v4l2-fwnode.h>
# include <media/v4l2-subdev.h>
/* min/typical/max system clock (xclk) frequencies */
# define OV5640_XCLK_MIN 6000000
2018-06-06 12:11:38 +03:00
# define OV5640_XCLK_MAX 54000000
2017-06-07 21:33:56 +03:00
# define OV5640_DEFAULT_SLAVE_ID 0x3c
2018-01-31 12:08:10 +03:00
# define OV5640_REG_SYS_RESET02 0x3002
# define OV5640_REG_SYS_CLOCK_ENABLE02 0x3006
2018-01-03 12:57:31 +03:00
# define OV5640_REG_SYS_CTRL0 0x3008
2017-06-07 21:33:56 +03:00
# define OV5640_REG_CHIP_ID 0x300a
2018-01-03 12:57:31 +03:00
# define OV5640_REG_IO_MIPI_CTRL00 0x300e
# define OV5640_REG_PAD_OUTPUT_ENABLE01 0x3017
# define OV5640_REG_PAD_OUTPUT_ENABLE02 0x3018
2017-06-07 21:33:56 +03:00
# define OV5640_REG_PAD_OUTPUT00 0x3019
2018-01-03 12:57:31 +03:00
# define OV5640_REG_SYSTEM_CONTROL1 0x302e
2017-06-07 21:33:56 +03:00
# define OV5640_REG_SC_PLL_CTRL0 0x3034
# define OV5640_REG_SC_PLL_CTRL1 0x3035
# define OV5640_REG_SC_PLL_CTRL2 0x3036
# define OV5640_REG_SC_PLL_CTRL3 0x3037
# define OV5640_REG_SLAVE_ID 0x3100
2018-01-03 12:57:31 +03:00
# define OV5640_REG_SCCB_SYS_CTRL1 0x3103
2017-06-07 21:33:56 +03:00
# define OV5640_REG_SYS_ROOT_DIVIDER 0x3108
# define OV5640_REG_AWB_R_GAIN 0x3400
# define OV5640_REG_AWB_G_GAIN 0x3402
# define OV5640_REG_AWB_B_GAIN 0x3404
# define OV5640_REG_AWB_MANUAL_CTRL 0x3406
# define OV5640_REG_AEC_PK_EXPOSURE_HI 0x3500
# define OV5640_REG_AEC_PK_EXPOSURE_MED 0x3501
# define OV5640_REG_AEC_PK_EXPOSURE_LO 0x3502
# define OV5640_REG_AEC_PK_MANUAL 0x3503
# define OV5640_REG_AEC_PK_REAL_GAIN 0x350a
# define OV5640_REG_AEC_PK_VTS 0x350c
2018-04-16 15:36:56 +03:00
# define OV5640_REG_TIMING_DVPHO 0x3808
# define OV5640_REG_TIMING_DVPVO 0x380a
2017-06-07 21:33:56 +03:00
# define OV5640_REG_TIMING_HTS 0x380c
# define OV5640_REG_TIMING_VTS 0x380e
2018-06-18 13:29:17 +03:00
# define OV5640_REG_TIMING_TC_REG20 0x3820
2017-06-07 21:33:56 +03:00
# define OV5640_REG_TIMING_TC_REG21 0x3821
# define OV5640_REG_AEC_CTRL00 0x3a00
# define OV5640_REG_AEC_B50_STEP 0x3a08
# define OV5640_REG_AEC_B60_STEP 0x3a0a
# define OV5640_REG_AEC_CTRL0D 0x3a0d
# define OV5640_REG_AEC_CTRL0E 0x3a0e
# define OV5640_REG_AEC_CTRL0F 0x3a0f
# define OV5640_REG_AEC_CTRL10 0x3a10
# define OV5640_REG_AEC_CTRL11 0x3a11
# define OV5640_REG_AEC_CTRL1B 0x3a1b
# define OV5640_REG_AEC_CTRL1E 0x3a1e
# define OV5640_REG_AEC_CTRL1F 0x3a1f
# define OV5640_REG_HZ5060_CTRL00 0x3c00
# define OV5640_REG_HZ5060_CTRL01 0x3c01
# define OV5640_REG_SIGMADELTA_CTRL0C 0x3c0c
# define OV5640_REG_FRAME_CTRL01 0x4202
2018-01-03 12:57:32 +03:00
# define OV5640_REG_FORMAT_CONTROL00 0x4300
2018-01-03 12:57:31 +03:00
# define OV5640_REG_POLARITY_CTRL00 0x4740
2017-06-07 21:33:56 +03:00
# define OV5640_REG_MIPI_CTRL00 0x4800
# define OV5640_REG_DEBUG_MODE 0x4814
2018-01-03 12:57:32 +03:00
# define OV5640_REG_ISP_FORMAT_MUX_CTRL 0x501f
2017-06-07 21:33:56 +03:00
# define OV5640_REG_PRE_ISP_TEST_SET1 0x503d
# define OV5640_REG_SDE_CTRL0 0x5580
# define OV5640_REG_SDE_CTRL1 0x5581
# define OV5640_REG_SDE_CTRL3 0x5583
# define OV5640_REG_SDE_CTRL4 0x5584
# define OV5640_REG_SDE_CTRL5 0x5585
# define OV5640_REG_AVG_READOUT 0x56a1
2018-04-16 15:36:53 +03:00
# define OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT 1
# define OV5640_SCLK_ROOT_DIVIDER_DEFAULT 2
2017-06-07 21:33:56 +03:00
enum ov5640_mode_id {
OV5640_MODE_QCIF_176_144 = 0 ,
OV5640_MODE_QVGA_320_240 ,
OV5640_MODE_VGA_640_480 ,
OV5640_MODE_NTSC_720_480 ,
OV5640_MODE_PAL_720_576 ,
OV5640_MODE_XGA_1024_768 ,
OV5640_MODE_720P_1280_720 ,
OV5640_MODE_1080P_1920_1080 ,
OV5640_MODE_QSXGA_2592_1944 ,
OV5640_NUM_MODES ,
} ;
enum ov5640_frame_rate {
OV5640_15_FPS = 0 ,
OV5640_30_FPS ,
OV5640_NUM_FRAMERATES ,
} ;
2018-01-03 12:57:32 +03:00
struct ov5640_pixfmt {
u32 code ;
u32 colorspace ;
} ;
static const struct ov5640_pixfmt ov5640_formats [ ] = {
2018-01-31 12:08:10 +03:00
{ MEDIA_BUS_FMT_JPEG_1X8 , V4L2_COLORSPACE_JPEG , } ,
2018-01-03 12:57:32 +03:00
{ MEDIA_BUS_FMT_UYVY8_2X8 , V4L2_COLORSPACE_SRGB , } ,
{ MEDIA_BUS_FMT_YUYV8_2X8 , V4L2_COLORSPACE_SRGB , } ,
{ MEDIA_BUS_FMT_RGB565_2X8_LE , V4L2_COLORSPACE_SRGB , } ,
{ MEDIA_BUS_FMT_RGB565_2X8_BE , V4L2_COLORSPACE_SRGB , } ,
} ;
2017-06-07 21:33:56 +03:00
/*
* FIXME : remove this when a subdev API becomes available
* to set the MIPI CSI - 2 virtual channel .
*/
static unsigned int virtual_channel ;
2018-02-06 16:24:09 +03:00
module_param ( virtual_channel , uint , 0444 ) ;
2017-06-07 21:33:56 +03:00
MODULE_PARM_DESC ( virtual_channel ,
" MIPI CSI-2 virtual channel (0..3), default 0 " ) ;
static const int ov5640_framerates [ ] = {
[ OV5640_15_FPS ] = 15 ,
[ OV5640_30_FPS ] = 30 ,
} ;
/* regulator supplies */
static const char * const ov5640_supply_name [ ] = {
2018-02-01 11:44:06 +03:00
" DOVDD " , /* Digital I/O (1.8V) supply */
2017-06-07 21:33:56 +03:00
" DVDD " , /* Digital Core (1.5V) supply */
" AVDD " , /* Analog (2.8V) supply */
} ;
# define OV5640_NUM_SUPPLIES ARRAY_SIZE(ov5640_supply_name)
/*
* Image size under 1280 * 960 are SUBSAMPLING
* Image size upper 1280 * 960 are SCALING
*/
enum ov5640_downsize_mode {
SUBSAMPLING ,
SCALING ,
} ;
struct reg_value {
u16 reg_addr ;
u8 val ;
u8 mask ;
u32 delay_ms ;
} ;
struct ov5640_mode_info {
enum ov5640_mode_id id ;
enum ov5640_downsize_mode dn_mode ;
2018-04-16 15:36:54 +03:00
u32 hact ;
2018-04-16 15:36:55 +03:00
u32 htot ;
2018-04-16 15:36:54 +03:00
u32 vact ;
2018-04-16 15:36:55 +03:00
u32 vtot ;
2017-06-07 21:33:56 +03:00
const struct reg_value * reg_data ;
u32 reg_data_size ;
} ;
struct ov5640_ctrls {
struct v4l2_ctrl_handler handler ;
struct {
struct v4l2_ctrl * auto_exp ;
struct v4l2_ctrl * exposure ;
} ;
struct {
struct v4l2_ctrl * auto_wb ;
struct v4l2_ctrl * blue_balance ;
struct v4l2_ctrl * red_balance ;
} ;
struct {
struct v4l2_ctrl * auto_gain ;
struct v4l2_ctrl * gain ;
} ;
struct v4l2_ctrl * brightness ;
2018-04-16 15:36:51 +03:00
struct v4l2_ctrl * light_freq ;
2017-06-07 21:33:56 +03:00
struct v4l2_ctrl * saturation ;
struct v4l2_ctrl * contrast ;
struct v4l2_ctrl * hue ;
struct v4l2_ctrl * test_pattern ;
2018-06-18 13:29:17 +03:00
struct v4l2_ctrl * hflip ;
struct v4l2_ctrl * vflip ;
2017-06-07 21:33:56 +03:00
} ;
struct ov5640_dev {
struct i2c_client * i2c_client ;
struct v4l2_subdev sd ;
struct media_pad pad ;
struct v4l2_fwnode_endpoint ep ; /* the parsed DT endpoint info */
struct clk * xclk ; /* system clock to OV5640 */
u32 xclk_freq ;
struct regulator_bulk_data supplies [ OV5640_NUM_SUPPLIES ] ;
struct gpio_desc * reset_gpio ;
struct gpio_desc * pwdn_gpio ;
2018-06-18 13:29:19 +03:00
bool upside_down ;
2017-06-07 21:33:56 +03:00
/* lock to protect all members below */
struct mutex lock ;
int power_count ;
struct v4l2_mbus_framefmt fmt ;
2018-08-16 12:46:53 +03:00
bool pending_fmt_change ;
2017-06-07 21:33:56 +03:00
const struct ov5640_mode_info * current_mode ;
2018-09-11 16:48:21 +03:00
const struct ov5640_mode_info * last_mode ;
2017-06-07 21:33:56 +03:00
enum ov5640_frame_rate current_fr ;
struct v4l2_fract frame_interval ;
struct ov5640_ctrls ctrls ;
u32 prev_sysclk , prev_hts ;
u32 ae_low , ae_high , ae_target ;
bool pending_mode_change ;
bool streaming ;
} ;
static inline struct ov5640_dev * to_ov5640_dev ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct ov5640_dev , sd ) ;
}
static inline struct v4l2_subdev * ctrl_to_sd ( struct v4l2_ctrl * ctrl )
{
return & container_of ( ctrl - > handler , struct ov5640_dev ,
ctrls . handler ) - > sd ;
}
/*
* FIXME : all of these register tables are likely filled with
* entries that set the register to their power - on default values ,
* and which are otherwise not touched by this driver . Those entries
* should be identified and removed to speed register load time
* over i2c .
*/
2018-08-16 12:46:53 +03:00
/* YUV422 UYVY VGA@30fps */
2017-06-07 21:33:56 +03:00
static const struct reg_value ov5640_init_setting_30fps_VGA [ ] = {
{ 0x3103 , 0x11 , 0 , 0 } , { 0x3008 , 0x82 , 0 , 5 } , { 0x3008 , 0x42 , 0 , 0 } ,
{ 0x3103 , 0x03 , 0 , 0 } , { 0x3017 , 0x00 , 0 , 0 } , { 0x3018 , 0x00 , 0 , 0 } ,
{ 0x3034 , 0x18 , 0 , 0 } , { 0x3035 , 0x14 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } ,
2018-04-16 15:36:53 +03:00
{ 0x3037 , 0x13 , 0 , 0 } , { 0x3630 , 0x36 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3631 , 0x0e , 0 , 0 } , { 0x3632 , 0xe2 , 0 , 0 } , { 0x3633 , 0x12 , 0 , 0 } ,
{ 0x3621 , 0xe0 , 0 , 0 } , { 0x3704 , 0xa0 , 0 , 0 } , { 0x3703 , 0x5a , 0 , 0 } ,
{ 0x3715 , 0x78 , 0 , 0 } , { 0x3717 , 0x01 , 0 , 0 } , { 0x370b , 0x60 , 0 , 0 } ,
{ 0x3705 , 0x1a , 0 , 0 } , { 0x3905 , 0x02 , 0 , 0 } , { 0x3906 , 0x10 , 0 , 0 } ,
{ 0x3901 , 0x0a , 0 , 0 } , { 0x3731 , 0x12 , 0 , 0 } , { 0x3600 , 0x08 , 0 , 0 } ,
{ 0x3601 , 0x33 , 0 , 0 } , { 0x302d , 0x60 , 0 , 0 } , { 0x3620 , 0x52 , 0 , 0 } ,
{ 0x371b , 0x20 , 0 , 0 } , { 0x471c , 0x50 , 0 , 0 } , { 0x3a13 , 0x43 , 0 , 0 } ,
{ 0x3a18 , 0x00 , 0 , 0 } , { 0x3a19 , 0xf8 , 0 , 0 } , { 0x3635 , 0x13 , 0 , 0 } ,
{ 0x3636 , 0x03 , 0 , 0 } , { 0x3634 , 0x40 , 0 , 0 } , { 0x3622 , 0x01 , 0 , 0 } ,
{ 0x3c01 , 0xa4 , 0 , 0 } , { 0x3c04 , 0x28 , 0 , 0 } , { 0x3c05 , 0x98 , 0 , 0 } ,
{ 0x3c06 , 0x00 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } , { 0x3c08 , 0x00 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
{ 0x3820 , 0x41 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , { 0x3814 , 0x31 , 0 , 0 } ,
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x3000 , 0x00 , 0 , 0 } ,
{ 0x3002 , 0x1c , 0 , 0 } , { 0x3004 , 0xff , 0 , 0 } , { 0x3006 , 0xc3 , 0 , 0 } ,
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 12:51:52 +03:00
{ 0x302e , 0x08 , 0 , 0 } , { 0x4300 , 0x3f , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x501f , 0x00 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } , { 0x4407 , 0x04 , 0 , 0 } ,
{ 0x440e , 0x00 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 12:51:52 +03:00
{ 0x4837 , 0x0a , 0 , 0 } , { 0x3824 , 0x02 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x5000 , 0xa7 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } , { 0x5180 , 0xff , 0 , 0 } ,
{ 0x5181 , 0xf2 , 0 , 0 } , { 0x5182 , 0x00 , 0 , 0 } , { 0x5183 , 0x14 , 0 , 0 } ,
{ 0x5184 , 0x25 , 0 , 0 } , { 0x5185 , 0x24 , 0 , 0 } , { 0x5186 , 0x09 , 0 , 0 } ,
{ 0x5187 , 0x09 , 0 , 0 } , { 0x5188 , 0x09 , 0 , 0 } , { 0x5189 , 0x88 , 0 , 0 } ,
{ 0x518a , 0x54 , 0 , 0 } , { 0x518b , 0xee , 0 , 0 } , { 0x518c , 0xb2 , 0 , 0 } ,
{ 0x518d , 0x50 , 0 , 0 } , { 0x518e , 0x34 , 0 , 0 } , { 0x518f , 0x6b , 0 , 0 } ,
{ 0x5190 , 0x46 , 0 , 0 } , { 0x5191 , 0xf8 , 0 , 0 } , { 0x5192 , 0x04 , 0 , 0 } ,
{ 0x5193 , 0x70 , 0 , 0 } , { 0x5194 , 0xf0 , 0 , 0 } , { 0x5195 , 0xf0 , 0 , 0 } ,
{ 0x5196 , 0x03 , 0 , 0 } , { 0x5197 , 0x01 , 0 , 0 } , { 0x5198 , 0x04 , 0 , 0 } ,
{ 0x5199 , 0x6c , 0 , 0 } , { 0x519a , 0x04 , 0 , 0 } , { 0x519b , 0x00 , 0 , 0 } ,
{ 0x519c , 0x09 , 0 , 0 } , { 0x519d , 0x2b , 0 , 0 } , { 0x519e , 0x38 , 0 , 0 } ,
{ 0x5381 , 0x1e , 0 , 0 } , { 0x5382 , 0x5b , 0 , 0 } , { 0x5383 , 0x08 , 0 , 0 } ,
{ 0x5384 , 0x0a , 0 , 0 } , { 0x5385 , 0x7e , 0 , 0 } , { 0x5386 , 0x88 , 0 , 0 } ,
{ 0x5387 , 0x7c , 0 , 0 } , { 0x5388 , 0x6c , 0 , 0 } , { 0x5389 , 0x10 , 0 , 0 } ,
{ 0x538a , 0x01 , 0 , 0 } , { 0x538b , 0x98 , 0 , 0 } , { 0x5300 , 0x08 , 0 , 0 } ,
{ 0x5301 , 0x30 , 0 , 0 } , { 0x5302 , 0x10 , 0 , 0 } , { 0x5303 , 0x00 , 0 , 0 } ,
{ 0x5304 , 0x08 , 0 , 0 } , { 0x5305 , 0x30 , 0 , 0 } , { 0x5306 , 0x08 , 0 , 0 } ,
{ 0x5307 , 0x16 , 0 , 0 } , { 0x5309 , 0x08 , 0 , 0 } , { 0x530a , 0x30 , 0 , 0 } ,
{ 0x530b , 0x04 , 0 , 0 } , { 0x530c , 0x06 , 0 , 0 } , { 0x5480 , 0x01 , 0 , 0 } ,
{ 0x5481 , 0x08 , 0 , 0 } , { 0x5482 , 0x14 , 0 , 0 } , { 0x5483 , 0x28 , 0 , 0 } ,
{ 0x5484 , 0x51 , 0 , 0 } , { 0x5485 , 0x65 , 0 , 0 } , { 0x5486 , 0x71 , 0 , 0 } ,
{ 0x5487 , 0x7d , 0 , 0 } , { 0x5488 , 0x87 , 0 , 0 } , { 0x5489 , 0x91 , 0 , 0 } ,
{ 0x548a , 0x9a , 0 , 0 } , { 0x548b , 0xaa , 0 , 0 } , { 0x548c , 0xb8 , 0 , 0 } ,
{ 0x548d , 0xcd , 0 , 0 } , { 0x548e , 0xdd , 0 , 0 } , { 0x548f , 0xea , 0 , 0 } ,
{ 0x5490 , 0x1d , 0 , 0 } , { 0x5580 , 0x02 , 0 , 0 } , { 0x5583 , 0x40 , 0 , 0 } ,
{ 0x5584 , 0x10 , 0 , 0 } , { 0x5589 , 0x10 , 0 , 0 } , { 0x558a , 0x00 , 0 , 0 } ,
{ 0x558b , 0xf8 , 0 , 0 } , { 0x5800 , 0x23 , 0 , 0 } , { 0x5801 , 0x14 , 0 , 0 } ,
{ 0x5802 , 0x0f , 0 , 0 } , { 0x5803 , 0x0f , 0 , 0 } , { 0x5804 , 0x12 , 0 , 0 } ,
{ 0x5805 , 0x26 , 0 , 0 } , { 0x5806 , 0x0c , 0 , 0 } , { 0x5807 , 0x08 , 0 , 0 } ,
{ 0x5808 , 0x05 , 0 , 0 } , { 0x5809 , 0x05 , 0 , 0 } , { 0x580a , 0x08 , 0 , 0 } ,
{ 0x580b , 0x0d , 0 , 0 } , { 0x580c , 0x08 , 0 , 0 } , { 0x580d , 0x03 , 0 , 0 } ,
{ 0x580e , 0x00 , 0 , 0 } , { 0x580f , 0x00 , 0 , 0 } , { 0x5810 , 0x03 , 0 , 0 } ,
{ 0x5811 , 0x09 , 0 , 0 } , { 0x5812 , 0x07 , 0 , 0 } , { 0x5813 , 0x03 , 0 , 0 } ,
{ 0x5814 , 0x00 , 0 , 0 } , { 0x5815 , 0x01 , 0 , 0 } , { 0x5816 , 0x03 , 0 , 0 } ,
{ 0x5817 , 0x08 , 0 , 0 } , { 0x5818 , 0x0d , 0 , 0 } , { 0x5819 , 0x08 , 0 , 0 } ,
{ 0x581a , 0x05 , 0 , 0 } , { 0x581b , 0x06 , 0 , 0 } , { 0x581c , 0x08 , 0 , 0 } ,
{ 0x581d , 0x0e , 0 , 0 } , { 0x581e , 0x29 , 0 , 0 } , { 0x581f , 0x17 , 0 , 0 } ,
{ 0x5820 , 0x11 , 0 , 0 } , { 0x5821 , 0x11 , 0 , 0 } , { 0x5822 , 0x15 , 0 , 0 } ,
{ 0x5823 , 0x28 , 0 , 0 } , { 0x5824 , 0x46 , 0 , 0 } , { 0x5825 , 0x26 , 0 , 0 } ,
{ 0x5826 , 0x08 , 0 , 0 } , { 0x5827 , 0x26 , 0 , 0 } , { 0x5828 , 0x64 , 0 , 0 } ,
{ 0x5829 , 0x26 , 0 , 0 } , { 0x582a , 0x24 , 0 , 0 } , { 0x582b , 0x22 , 0 , 0 } ,
{ 0x582c , 0x24 , 0 , 0 } , { 0x582d , 0x24 , 0 , 0 } , { 0x582e , 0x06 , 0 , 0 } ,
{ 0x582f , 0x22 , 0 , 0 } , { 0x5830 , 0x40 , 0 , 0 } , { 0x5831 , 0x42 , 0 , 0 } ,
{ 0x5832 , 0x24 , 0 , 0 } , { 0x5833 , 0x26 , 0 , 0 } , { 0x5834 , 0x24 , 0 , 0 } ,
{ 0x5835 , 0x22 , 0 , 0 } , { 0x5836 , 0x22 , 0 , 0 } , { 0x5837 , 0x26 , 0 , 0 } ,
{ 0x5838 , 0x44 , 0 , 0 } , { 0x5839 , 0x24 , 0 , 0 } , { 0x583a , 0x26 , 0 , 0 } ,
{ 0x583b , 0x28 , 0 , 0 } , { 0x583c , 0x42 , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } ,
{ 0x5025 , 0x00 , 0 , 0 } , { 0x3a0f , 0x30 , 0 , 0 } , { 0x3a10 , 0x28 , 0 , 0 } ,
{ 0x3a1b , 0x30 , 0 , 0 } , { 0x3a1e , 0x26 , 0 , 0 } , { 0x3a11 , 0x60 , 0 , 0 } ,
{ 0x3a1f , 0x14 , 0 , 0 } , { 0x3008 , 0x02 , 0 , 0 } , { 0x3c00 , 0x04 , 0 , 300 } ,
} ;
static const struct reg_value ov5640_setting_30fps_VGA_640_480 [ ] = {
{ 0x3035 , 0x14 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x0e , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } , { 0x3503 , 0x00 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_15fps_VGA_640_480 [ ] = {
{ 0x3035 , 0x22 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_30fps_XGA_1024_768 [ ] = {
{ 0x3035 , 0x14 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x0e , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } , { 0x3503 , 0x00 , 0 , 0 } ,
2018-04-16 15:36:56 +03:00
{ 0x3035 , 0x12 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
} ;
static const struct reg_value ov5640_setting_15fps_XGA_1024_768 [ ] = {
{ 0x3035 , 0x22 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
2018-04-16 15:36:56 +03:00
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
} ;
static const struct reg_value ov5640_setting_30fps_QVGA_320_240 [ ] = {
{ 0x3035 , 0x14 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_15fps_QVGA_320_240 [ ] = {
{ 0x3035 , 0x22 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_30fps_QCIF_176_144 [ ] = {
{ 0x3035 , 0x14 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } ,
} ;
2018-02-01 11:44:06 +03:00
2017-06-07 21:33:56 +03:00
static const struct reg_value ov5640_setting_15fps_QCIF_176_144 [ ] = {
{ 0x3035 , 0x22 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_30fps_NTSC_720_480 [ ] = {
{ 0x3035 , 0x12 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x3c , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_15fps_NTSC_720_480 [ ] = {
{ 0x3035 , 0x22 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x3c , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_30fps_PAL_720_576 [ ] = {
{ 0x3035 , 0x12 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x38 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_15fps_PAL_720_576 [ ] = {
{ 0x3035 , 0x22 , 0 , 0 } , { 0x3036 , 0x38 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x04 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9b , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x38 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x06 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0xa3 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_30fps_720P_1280_720 [ ] = {
{ 0x3008 , 0x42 , 0 , 0 } ,
{ 0x3035 , 0x21 , 0 , 0 } , { 0x3036 , 0x54 , 0 , 0 } , { 0x3c07 , 0x07 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0xfa , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x06 , 0 , 0 } , { 0x3807 , 0xa9 , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x04 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x02 , 0 , 0 } ,
{ 0x3a03 , 0xe4 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0xbc , 0 , 0 } ,
{ 0x3a0a , 0x01 , 0 , 0 } , { 0x3a0b , 0x72 , 0 , 0 } , { 0x3a0e , 0x01 , 0 , 0 } ,
{ 0x3a0d , 0x02 , 0 , 0 } , { 0x3a14 , 0x02 , 0 , 0 } , { 0x3a15 , 0xe4 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x02 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x37 , 0 , 0 } , { 0x460c , 0x20 , 0 , 0 } ,
{ 0x3824 , 0x04 , 0 , 0 } , { 0x5001 , 0x83 , 0 , 0 } , { 0x4005 , 0x1a , 0 , 0 } ,
{ 0x3008 , 0x02 , 0 , 0 } , { 0x3503 , 0 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_15fps_720P_1280_720 [ ] = {
{ 0x3035 , 0x41 , 0 , 0 } , { 0x3036 , 0x54 , 0 , 0 } , { 0x3c07 , 0x07 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x31 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x31 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0xfa , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x06 , 0 , 0 } , { 0x3807 , 0xa9 , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x04 , 0 , 0 } ,
{ 0x3618 , 0x00 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
{ 0x3709 , 0x52 , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , { 0x3a02 , 0x02 , 0 , 0 } ,
{ 0x3a03 , 0xe4 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0xbc , 0 , 0 } ,
{ 0x3a0a , 0x01 , 0 , 0 } , { 0x3a0b , 0x72 , 0 , 0 } , { 0x3a0e , 0x01 , 0 , 0 } ,
{ 0x3a0d , 0x02 , 0 , 0 } , { 0x3a14 , 0x02 , 0 , 0 } , { 0x3a15 , 0xe4 , 0 , 0 } ,
2018-09-24 16:33:27 +03:00
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x37 , 0 , 0 } , { 0x460c , 0x20 , 0 , 0 } ,
{ 0x3824 , 0x04 , 0 , 0 } , { 0x5001 , 0x83 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_30fps_1080P_1920_1080 [ ] = {
{ 0x3008 , 0x42 , 0 , 0 } ,
{ 0x3035 , 0x21 , 0 , 0 } , { 0x3036 , 0x54 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x11 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x11 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x00 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9f , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x04 , 0 , 0 } ,
{ 0x3618 , 0x04 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x21 , 0 , 0 } ,
{ 0x3709 , 0x12 , 0 , 0 } , { 0x370c , 0x00 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x06 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0x83 , 0 , 0 } , { 0x3035 , 0x11 , 0 , 0 } ,
{ 0x3036 , 0x54 , 0 , 0 } , { 0x3c07 , 0x07 , 0 , 0 } , { 0x3c08 , 0x00 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
{ 0x3800 , 0x01 , 0 , 0 } , { 0x3801 , 0x50 , 0 , 0 } , { 0x3802 , 0x01 , 0 , 0 } ,
{ 0x3803 , 0xb2 , 0 , 0 } , { 0x3804 , 0x08 , 0 , 0 } , { 0x3805 , 0xef , 0 , 0 } ,
2018-04-16 15:36:56 +03:00
{ 0x3806 , 0x05 , 0 , 0 } , { 0x3807 , 0xf1 , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3612 , 0x2b , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3a02 , 0x04 , 0 , 0 } , { 0x3a03 , 0x60 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } ,
{ 0x3a09 , 0x50 , 0 , 0 } , { 0x3a0a , 0x01 , 0 , 0 } , { 0x3a0b , 0x18 , 0 , 0 } ,
{ 0x3a0e , 0x03 , 0 , 0 } , { 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x04 , 0 , 0 } ,
{ 0x3a15 , 0x60 , 0 , 0 } , { 0x4713 , 0x02 , 0 , 0 } , { 0x4407 , 0x04 , 0 , 0 } ,
{ 0x460b , 0x37 , 0 , 0 } , { 0x460c , 0x20 , 0 , 0 } , { 0x3824 , 0x04 , 0 , 0 } ,
{ 0x4005 , 0x1a , 0 , 0 } , { 0x3008 , 0x02 , 0 , 0 } ,
{ 0x3503 , 0 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_15fps_1080P_1920_1080 [ ] = {
{ 0x3008 , 0x42 , 0 , 0 } ,
{ 0x3035 , 0x21 , 0 , 0 } , { 0x3036 , 0x54 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x11 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x11 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x00 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9f , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x04 , 0 , 0 } ,
{ 0x3618 , 0x04 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x21 , 0 , 0 } ,
{ 0x3709 , 0x12 , 0 , 0 } , { 0x370c , 0x00 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x06 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0x83 , 0 , 0 } , { 0x3035 , 0x21 , 0 , 0 } ,
{ 0x3036 , 0x54 , 0 , 1 } , { 0x3c07 , 0x07 , 0 , 0 } , { 0x3c08 , 0x00 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
{ 0x3800 , 0x01 , 0 , 0 } , { 0x3801 , 0x50 , 0 , 0 } , { 0x3802 , 0x01 , 0 , 0 } ,
{ 0x3803 , 0xb2 , 0 , 0 } , { 0x3804 , 0x08 , 0 , 0 } , { 0x3805 , 0xef , 0 , 0 } ,
2018-04-16 15:36:56 +03:00
{ 0x3806 , 0x05 , 0 , 0 } , { 0x3807 , 0xf1 , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3612 , 0x2b , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3a02 , 0x04 , 0 , 0 } , { 0x3a03 , 0x60 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } ,
{ 0x3a09 , 0x50 , 0 , 0 } , { 0x3a0a , 0x01 , 0 , 0 } , { 0x3a0b , 0x18 , 0 , 0 } ,
{ 0x3a0e , 0x03 , 0 , 0 } , { 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x04 , 0 , 0 } ,
{ 0x3a15 , 0x60 , 0 , 0 } , { 0x4713 , 0x02 , 0 , 0 } , { 0x4407 , 0x04 , 0 , 0 } ,
{ 0x460b , 0x37 , 0 , 0 } , { 0x460c , 0x20 , 0 , 0 } , { 0x3824 , 0x04 , 0 , 0 } ,
{ 0x4005 , 0x1a , 0 , 0 } , { 0x3008 , 0x02 , 0 , 0 } , { 0x3503 , 0 , 0 , 0 } ,
} ;
static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944 [ ] = {
{ 0x3035 , 0x21 , 0 , 0 } , { 0x3036 , 0x54 , 0 , 0 } , { 0x3c07 , 0x08 , 0 , 0 } ,
{ 0x3c09 , 0x1c , 0 , 0 } , { 0x3c0a , 0x9c , 0 , 0 } , { 0x3c0b , 0x40 , 0 , 0 } ,
2018-06-18 13:29:17 +03:00
{ 0x3814 , 0x11 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3815 , 0x11 , 0 , 0 } , { 0x3800 , 0x00 , 0 , 0 } , { 0x3801 , 0x00 , 0 , 0 } ,
{ 0x3802 , 0x00 , 0 , 0 } , { 0x3803 , 0x00 , 0 , 0 } , { 0x3804 , 0x0a , 0 , 0 } ,
{ 0x3805 , 0x3f , 0 , 0 } , { 0x3806 , 0x07 , 0 , 0 } , { 0x3807 , 0x9f , 0 , 0 } ,
2018-04-16 15:36:55 +03:00
{ 0x3810 , 0x00 , 0 , 0 } ,
2017-06-07 21:33:56 +03:00
{ 0x3811 , 0x10 , 0 , 0 } , { 0x3812 , 0x00 , 0 , 0 } , { 0x3813 , 0x04 , 0 , 0 } ,
{ 0x3618 , 0x04 , 0 , 0 } , { 0x3612 , 0x29 , 0 , 0 } , { 0x3708 , 0x21 , 0 , 0 } ,
{ 0x3709 , 0x12 , 0 , 0 } , { 0x370c , 0x00 , 0 , 0 } , { 0x3a02 , 0x03 , 0 , 0 } ,
{ 0x3a03 , 0xd8 , 0 , 0 } , { 0x3a08 , 0x01 , 0 , 0 } , { 0x3a09 , 0x27 , 0 , 0 } ,
{ 0x3a0a , 0x00 , 0 , 0 } , { 0x3a0b , 0xf6 , 0 , 0 } , { 0x3a0e , 0x03 , 0 , 0 } ,
{ 0x3a0d , 0x04 , 0 , 0 } , { 0x3a14 , 0x03 , 0 , 0 } , { 0x3a15 , 0xd8 , 0 , 0 } ,
{ 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x06 , 0 , 0 } , { 0x4713 , 0x03 , 0 , 0 } ,
{ 0x4407 , 0x04 , 0 , 0 } , { 0x460b , 0x35 , 0 , 0 } , { 0x460c , 0x22 , 0 , 0 } ,
{ 0x3824 , 0x02 , 0 , 0 } , { 0x5001 , 0x83 , 0 , 70 } ,
} ;
/* power-on sensor init reg table */
static const struct ov5640_mode_info ov5640_mode_init_data = {
2018-04-16 15:36:55 +03:00
0 , SUBSAMPLING , 640 , 1896 , 480 , 984 ,
ov5640_init_setting_30fps_VGA ,
2017-06-07 21:33:56 +03:00
ARRAY_SIZE ( ov5640_init_setting_30fps_VGA ) ,
} ;
static const struct ov5640_mode_info
ov5640_mode_data [ OV5640_NUM_FRAMERATES ] [ OV5640_NUM_MODES ] = {
{
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_QCIF_176_144 , SUBSAMPLING ,
176 , 1896 , 144 , 984 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_15fps_QCIF_176_144 ,
ARRAY_SIZE ( ov5640_setting_15fps_QCIF_176_144 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_QVGA_320_240 , SUBSAMPLING ,
320 , 1896 , 240 , 984 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_15fps_QVGA_320_240 ,
ARRAY_SIZE ( ov5640_setting_15fps_QVGA_320_240 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_VGA_640_480 , SUBSAMPLING ,
640 , 1896 , 480 , 1080 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_15fps_VGA_640_480 ,
ARRAY_SIZE ( ov5640_setting_15fps_VGA_640_480 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_NTSC_720_480 , SUBSAMPLING ,
720 , 1896 , 480 , 984 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_15fps_NTSC_720_480 ,
ARRAY_SIZE ( ov5640_setting_15fps_NTSC_720_480 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_PAL_720_576 , SUBSAMPLING ,
720 , 1896 , 576 , 984 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_15fps_PAL_720_576 ,
ARRAY_SIZE ( ov5640_setting_15fps_PAL_720_576 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_XGA_1024_768 , SUBSAMPLING ,
1024 , 1896 , 768 , 1080 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_15fps_XGA_1024_768 ,
ARRAY_SIZE ( ov5640_setting_15fps_XGA_1024_768 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_720P_1280_720 , SUBSAMPLING ,
1280 , 1892 , 720 , 740 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_15fps_720P_1280_720 ,
ARRAY_SIZE ( ov5640_setting_15fps_720P_1280_720 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_1080P_1920_1080 , SCALING ,
1920 , 2500 , 1080 , 1120 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_15fps_1080P_1920_1080 ,
ARRAY_SIZE ( ov5640_setting_15fps_1080P_1920_1080 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_QSXGA_2592_1944 , SCALING ,
2592 , 2844 , 1944 , 1968 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_15fps_QSXGA_2592_1944 ,
ARRAY_SIZE ( ov5640_setting_15fps_QSXGA_2592_1944 ) } ,
} , {
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_QCIF_176_144 , SUBSAMPLING ,
176 , 1896 , 144 , 984 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_30fps_QCIF_176_144 ,
ARRAY_SIZE ( ov5640_setting_30fps_QCIF_176_144 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_QVGA_320_240 , SUBSAMPLING ,
320 , 1896 , 240 , 984 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_30fps_QVGA_320_240 ,
ARRAY_SIZE ( ov5640_setting_30fps_QVGA_320_240 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_VGA_640_480 , SUBSAMPLING ,
640 , 1896 , 480 , 1080 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_30fps_VGA_640_480 ,
ARRAY_SIZE ( ov5640_setting_30fps_VGA_640_480 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_NTSC_720_480 , SUBSAMPLING ,
720 , 1896 , 480 , 984 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_30fps_NTSC_720_480 ,
ARRAY_SIZE ( ov5640_setting_30fps_NTSC_720_480 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_PAL_720_576 , SUBSAMPLING ,
720 , 1896 , 576 , 984 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_30fps_PAL_720_576 ,
ARRAY_SIZE ( ov5640_setting_30fps_PAL_720_576 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_XGA_1024_768 , SUBSAMPLING ,
1024 , 1896 , 768 , 1080 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_30fps_XGA_1024_768 ,
ARRAY_SIZE ( ov5640_setting_30fps_XGA_1024_768 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_720P_1280_720 , SUBSAMPLING ,
1280 , 1892 , 720 , 740 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_30fps_720P_1280_720 ,
ARRAY_SIZE ( ov5640_setting_30fps_720P_1280_720 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_1080P_1920_1080 , SCALING ,
1920 , 2500 , 1080 , 1120 ,
2017-06-07 21:33:56 +03:00
ov5640_setting_30fps_1080P_1920_1080 ,
ARRAY_SIZE ( ov5640_setting_30fps_1080P_1920_1080 ) } ,
2018-04-16 15:36:55 +03:00
{ OV5640_MODE_QSXGA_2592_1944 , - 1 , 0 , 0 , 0 , 0 , NULL , 0 } ,
2017-06-07 21:33:56 +03:00
} ,
} ;
static int ov5640_init_slave_id ( struct ov5640_dev * sensor )
{
struct i2c_client * client = sensor - > i2c_client ;
struct i2c_msg msg ;
u8 buf [ 3 ] ;
int ret ;
if ( client - > addr = = OV5640_DEFAULT_SLAVE_ID )
return 0 ;
buf [ 0 ] = OV5640_REG_SLAVE_ID > > 8 ;
buf [ 1 ] = OV5640_REG_SLAVE_ID & 0xff ;
buf [ 2 ] = client - > addr < < 1 ;
msg . addr = OV5640_DEFAULT_SLAVE_ID ;
msg . flags = 0 ;
msg . buf = buf ;
msg . len = sizeof ( buf ) ;
ret = i2c_transfer ( client - > adapter , & msg , 1 ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " %s: failed with %d \n " , __func__ , ret ) ;
return ret ;
}
return 0 ;
}
static int ov5640_write_reg ( struct ov5640_dev * sensor , u16 reg , u8 val )
{
struct i2c_client * client = sensor - > i2c_client ;
struct i2c_msg msg ;
u8 buf [ 3 ] ;
int ret ;
buf [ 0 ] = reg > > 8 ;
buf [ 1 ] = reg & 0xff ;
buf [ 2 ] = val ;
msg . addr = client - > addr ;
msg . flags = client - > flags ;
msg . buf = buf ;
msg . len = sizeof ( buf ) ;
ret = i2c_transfer ( client - > adapter , & msg , 1 ) ;
if ( ret < 0 ) {
2018-01-31 15:46:17 +03:00
dev_err ( & client - > dev , " %s: error: reg=%x, val=%x \n " ,
2017-06-07 21:33:56 +03:00
__func__ , reg , val ) ;
return ret ;
}
return 0 ;
}
static int ov5640_read_reg ( struct ov5640_dev * sensor , u16 reg , u8 * val )
{
struct i2c_client * client = sensor - > i2c_client ;
struct i2c_msg msg [ 2 ] ;
u8 buf [ 2 ] ;
int ret ;
buf [ 0 ] = reg > > 8 ;
buf [ 1 ] = reg & 0xff ;
msg [ 0 ] . addr = client - > addr ;
msg [ 0 ] . flags = client - > flags ;
msg [ 0 ] . buf = buf ;
msg [ 0 ] . len = sizeof ( buf ) ;
msg [ 1 ] . addr = client - > addr ;
msg [ 1 ] . flags = client - > flags | I2C_M_RD ;
msg [ 1 ] . buf = buf ;
msg [ 1 ] . len = 1 ;
ret = i2c_transfer ( client - > adapter , msg , 2 ) ;
2018-01-31 15:46:17 +03:00
if ( ret < 0 ) {
dev_err ( & client - > dev , " %s: error: reg=%x \n " ,
__func__ , reg ) ;
2017-06-07 21:33:56 +03:00
return ret ;
2018-01-31 15:46:17 +03:00
}
2017-06-07 21:33:56 +03:00
* val = buf [ 0 ] ;
return 0 ;
}
static int ov5640_read_reg16 ( struct ov5640_dev * sensor , u16 reg , u16 * val )
{
u8 hi , lo ;
int ret ;
ret = ov5640_read_reg ( sensor , reg , & hi ) ;
if ( ret )
return ret ;
2018-02-01 11:44:06 +03:00
ret = ov5640_read_reg ( sensor , reg + 1 , & lo ) ;
2017-06-07 21:33:56 +03:00
if ( ret )
return ret ;
* val = ( ( u16 ) hi < < 8 ) | ( u16 ) lo ;
return 0 ;
}
static int ov5640_write_reg16 ( struct ov5640_dev * sensor , u16 reg , u16 val )
{
int ret ;
ret = ov5640_write_reg ( sensor , reg , val > > 8 ) ;
if ( ret )
return ret ;
return ov5640_write_reg ( sensor , reg + 1 , val & 0xff ) ;
}
static int ov5640_mod_reg ( struct ov5640_dev * sensor , u16 reg ,
u8 mask , u8 val )
{
u8 readval ;
int ret ;
ret = ov5640_read_reg ( sensor , reg , & readval ) ;
if ( ret )
return ret ;
readval & = ~ mask ;
val & = mask ;
val | = readval ;
return ov5640_write_reg ( sensor , reg , val ) ;
}
/* download ov5640 settings to sensor through i2c */
2018-07-18 13:06:23 +03:00
static int ov5640_set_timings ( struct ov5640_dev * sensor ,
const struct ov5640_mode_info * mode )
{
int ret ;
ret = ov5640_write_reg16 ( sensor , OV5640_REG_TIMING_DVPHO , mode - > hact ) ;
if ( ret < 0 )
return ret ;
ret = ov5640_write_reg16 ( sensor , OV5640_REG_TIMING_DVPVO , mode - > vact ) ;
if ( ret < 0 )
return ret ;
ret = ov5640_write_reg16 ( sensor , OV5640_REG_TIMING_HTS , mode - > htot ) ;
if ( ret < 0 )
return ret ;
return ov5640_write_reg16 ( sensor , OV5640_REG_TIMING_VTS , mode - > vtot ) ;
}
2017-06-07 21:33:56 +03:00
static int ov5640_load_regs ( struct ov5640_dev * sensor ,
const struct ov5640_mode_info * mode )
{
const struct reg_value * regs = mode - > reg_data ;
unsigned int i ;
u32 delay_ms ;
u16 reg_addr ;
u8 mask , val ;
int ret = 0 ;
for ( i = 0 ; i < mode - > reg_data_size ; + + i , + + regs ) {
delay_ms = regs - > delay_ms ;
reg_addr = regs - > reg_addr ;
val = regs - > val ;
mask = regs - > mask ;
if ( mask )
ret = ov5640_mod_reg ( sensor , reg_addr , mask , val ) ;
else
ret = ov5640_write_reg ( sensor , reg_addr , val ) ;
if ( ret )
break ;
if ( delay_ms )
2018-02-01 11:44:06 +03:00
usleep_range ( 1000 * delay_ms , 1000 * delay_ms + 100 ) ;
2017-06-07 21:33:56 +03:00
}
2018-07-18 13:06:23 +03:00
return ov5640_set_timings ( sensor , mode ) ;
2017-06-07 21:33:56 +03:00
}
2018-09-11 16:48:17 +03:00
static int ov5640_set_autoexposure ( struct ov5640_dev * sensor , bool on )
{
return ov5640_mod_reg ( sensor , OV5640_REG_AEC_PK_MANUAL ,
BIT ( 0 ) , on ? 0 : BIT ( 0 ) ) ;
}
2017-06-07 21:33:56 +03:00
/* read exposure, in number of line periods */
static int ov5640_get_exposure ( struct ov5640_dev * sensor )
{
int exp , ret ;
u8 temp ;
ret = ov5640_read_reg ( sensor , OV5640_REG_AEC_PK_EXPOSURE_HI , & temp ) ;
if ( ret )
return ret ;
exp = ( ( int ) temp & 0x0f ) < < 16 ;
ret = ov5640_read_reg ( sensor , OV5640_REG_AEC_PK_EXPOSURE_MED , & temp ) ;
if ( ret )
return ret ;
exp | = ( ( int ) temp < < 8 ) ;
ret = ov5640_read_reg ( sensor , OV5640_REG_AEC_PK_EXPOSURE_LO , & temp ) ;
if ( ret )
return ret ;
exp | = ( int ) temp ;
return exp > > 4 ;
}
/* write exposure, given number of line periods */
static int ov5640_set_exposure ( struct ov5640_dev * sensor , u32 exposure )
{
int ret ;
exposure < < = 4 ;
ret = ov5640_write_reg ( sensor ,
OV5640_REG_AEC_PK_EXPOSURE_LO ,
exposure & 0xff ) ;
if ( ret )
return ret ;
ret = ov5640_write_reg ( sensor ,
OV5640_REG_AEC_PK_EXPOSURE_MED ,
( exposure > > 8 ) & 0xff ) ;
if ( ret )
return ret ;
return ov5640_write_reg ( sensor ,
OV5640_REG_AEC_PK_EXPOSURE_HI ,
( exposure > > 16 ) & 0x0f ) ;
}
static int ov5640_get_gain ( struct ov5640_dev * sensor )
{
u16 gain ;
int ret ;
ret = ov5640_read_reg16 ( sensor , OV5640_REG_AEC_PK_REAL_GAIN , & gain ) ;
if ( ret )
return ret ;
return gain & 0x3ff ;
}
2018-09-11 16:48:18 +03:00
static int ov5640_set_gain ( struct ov5640_dev * sensor , int gain )
{
return ov5640_write_reg16 ( sensor , OV5640_REG_AEC_PK_REAL_GAIN ,
( u16 ) gain & 0x3ff ) ;
}
static int ov5640_set_autogain ( struct ov5640_dev * sensor , bool on )
{
return ov5640_mod_reg ( sensor , OV5640_REG_AEC_PK_MANUAL ,
BIT ( 1 ) , on ? 0 : BIT ( 1 ) ) ;
}
2018-01-03 12:57:31 +03:00
static int ov5640_set_stream_dvp ( struct ov5640_dev * sensor , bool on )
{
int ret ;
unsigned int flags = sensor - > ep . bus . parallel . flags ;
u8 pclk_pol = 0 ;
u8 hsync_pol = 0 ;
u8 vsync_pol = 0 ;
/*
* Note about parallel port configuration .
*
* When configured in parallel mode , the OV5640 will
* output 10 bits data on DVP data lines [ 9 : 0 ] .
* If only 8 bits data are wanted , the 8 bits data lines
* of the camera interface must be physically connected
* on the DVP data lines [ 9 : 2 ] .
*
* Control lines polarity can be configured through
* devicetree endpoint control lines properties .
* If no endpoint control lines properties are set ,
* polarity will be as below :
* - VSYNC : active high
* - HREF : active low
* - PCLK : active low
*/
if ( on ) {
/*
* reset MIPI PCLK / SERCLK divider
*
* SC PLL CONTRL1 0
* - [ 3. .0 ] : MIPI PCLK / SERCLK divider
*/
ret = ov5640_mod_reg ( sensor , OV5640_REG_SC_PLL_CTRL1 , 0x0f , 0 ) ;
if ( ret )
return ret ;
/*
* configure parallel port control lines polarity
*
* POLARITY CTRL0
* - [ 5 ] : PCLK polarity ( 0 : active low , 1 : active high )
* - [ 1 ] : HREF polarity ( 0 : active low , 1 : active high )
* - [ 0 ] : VSYNC polarity ( mismatch here between
* datasheet and hardware , 0 is active high
* and 1 is active low . . . )
*/
if ( flags & V4L2_MBUS_PCLK_SAMPLE_RISING )
pclk_pol = 1 ;
if ( flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH )
hsync_pol = 1 ;
if ( flags & V4L2_MBUS_VSYNC_ACTIVE_LOW )
vsync_pol = 1 ;
ret = ov5640_write_reg ( sensor ,
OV5640_REG_POLARITY_CTRL00 ,
( pclk_pol < < 5 ) |
( hsync_pol < < 1 ) |
vsync_pol ) ;
if ( ret )
return ret ;
}
/*
* powerdown MIPI TX / RX PHY & disable MIPI
*
* MIPI CONTROL 00
* 4 : PWDN PHY TX
* 3 : PWDN PHY RX
* 2 : MIPI enable
*/
ret = ov5640_write_reg ( sensor ,
OV5640_REG_IO_MIPI_CTRL00 , on ? 0x18 : 0 ) ;
if ( ret )
return ret ;
/*
* enable VSYNC / HREF / PCLK DVP control lines
* & D [ 9 : 6 ] DVP data lines
*
* PAD OUTPUT ENABLE 01
* - 6 : VSYNC output enable
* - 5 : HREF output enable
* - 4 : PCLK output enable
* - [ 3 : 0 ] : D [ 9 : 6 ] output enable
*/
ret = ov5640_write_reg ( sensor ,
OV5640_REG_PAD_OUTPUT_ENABLE01 ,
on ? 0x7f : 0 ) ;
if ( ret )
return ret ;
/*
* enable D [ 5 : 0 ] DVP data lines
*
* PAD OUTPUT ENABLE 02
* - [ 7 : 2 ] : D [ 5 : 0 ] output enable
*/
return ov5640_write_reg ( sensor ,
OV5640_REG_PAD_OUTPUT_ENABLE02 ,
on ? 0xfc : 0 ) ;
}
static int ov5640_set_stream_mipi ( struct ov5640_dev * sensor , bool on )
2017-06-07 21:33:56 +03:00
{
int ret ;
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 12:51:52 +03:00
/*
* Enable / disable the MIPI interface
*
* 0x300e = on ? 0x45 : 0x40
*
* FIXME : the sensor manual ( version 2.03 ) reports
* [ 7 : 5 ] = 000 : 1 data lane mode
* [ 7 : 5 ] = 001 : 2 data lanes mode
* But this settings do not work , while the following ones
* have been validated for 2 data lanes mode .
*
* [ 7 : 5 ] = 010 : 2 data lanes mode
* [ 4 ] = 0 : Power up MIPI HS Tx
* [ 3 ] = 0 : Power up MIPI LS Rx
* [ 2 ] = 1 / 0 : MIPI interface enable / disable
* [ 1 : 0 ] = 01 / 00 : FIXME : ' debug '
*/
ret = ov5640_write_reg ( sensor , OV5640_REG_IO_MIPI_CTRL00 ,
on ? 0x45 : 0x40 ) ;
2017-06-07 21:33:56 +03:00
if ( ret )
return ret ;
return ov5640_write_reg ( sensor , OV5640_REG_FRAME_CTRL01 ,
on ? 0x00 : 0x0f ) ;
}
static int ov5640_get_sysclk ( struct ov5640_dev * sensor )
{
/* calculate sysclk */
u32 xvclk = sensor - > xclk_freq / 10000 ;
u32 multiplier , prediv , VCO , sysdiv , pll_rdiv ;
u32 sclk_rdiv_map [ ] = { 1 , 2 , 4 , 8 } ;
u32 bit_div2x = 1 , sclk_rdiv , sysclk ;
u8 temp1 , temp2 ;
int ret ;
ret = ov5640_read_reg ( sensor , OV5640_REG_SC_PLL_CTRL0 , & temp1 ) ;
if ( ret )
return ret ;
temp2 = temp1 & 0x0f ;
if ( temp2 = = 8 | | temp2 = = 10 )
bit_div2x = temp2 / 2 ;
ret = ov5640_read_reg ( sensor , OV5640_REG_SC_PLL_CTRL1 , & temp1 ) ;
if ( ret )
return ret ;
sysdiv = temp1 > > 4 ;
if ( sysdiv = = 0 )
sysdiv = 16 ;
ret = ov5640_read_reg ( sensor , OV5640_REG_SC_PLL_CTRL2 , & temp1 ) ;
if ( ret )
return ret ;
multiplier = temp1 ;
ret = ov5640_read_reg ( sensor , OV5640_REG_SC_PLL_CTRL3 , & temp1 ) ;
if ( ret )
return ret ;
prediv = temp1 & 0x0f ;
pll_rdiv = ( ( temp1 > > 4 ) & 0x01 ) + 1 ;
ret = ov5640_read_reg ( sensor , OV5640_REG_SYS_ROOT_DIVIDER , & temp1 ) ;
if ( ret )
return ret ;
temp2 = temp1 & 0x03 ;
sclk_rdiv = sclk_rdiv_map [ temp2 ] ;
if ( ! prediv | | ! sysdiv | | ! pll_rdiv | | ! bit_div2x )
return - EINVAL ;
VCO = xvclk * multiplier / prediv ;
sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv ;
return sysclk ;
}
static int ov5640_set_night_mode ( struct ov5640_dev * sensor )
{
/* read HTS from register settings */
u8 mode ;
int ret ;
ret = ov5640_read_reg ( sensor , OV5640_REG_AEC_CTRL00 , & mode ) ;
if ( ret )
return ret ;
mode & = 0xfb ;
return ov5640_write_reg ( sensor , OV5640_REG_AEC_CTRL00 , mode ) ;
}
static int ov5640_get_hts ( struct ov5640_dev * sensor )
{
/* read HTS from register settings */
u16 hts ;
int ret ;
ret = ov5640_read_reg16 ( sensor , OV5640_REG_TIMING_HTS , & hts ) ;
if ( ret )
return ret ;
return hts ;
}
static int ov5640_get_vts ( struct ov5640_dev * sensor )
{
u16 vts ;
int ret ;
ret = ov5640_read_reg16 ( sensor , OV5640_REG_TIMING_VTS , & vts ) ;
if ( ret )
return ret ;
return vts ;
}
static int ov5640_set_vts ( struct ov5640_dev * sensor , int vts )
{
return ov5640_write_reg16 ( sensor , OV5640_REG_TIMING_VTS , vts ) ;
}
static int ov5640_get_light_freq ( struct ov5640_dev * sensor )
{
/* get banding filter value */
int ret , light_freq = 0 ;
u8 temp , temp1 ;
ret = ov5640_read_reg ( sensor , OV5640_REG_HZ5060_CTRL01 , & temp ) ;
if ( ret )
return ret ;
if ( temp & 0x80 ) {
/* manual */
ret = ov5640_read_reg ( sensor , OV5640_REG_HZ5060_CTRL00 ,
& temp1 ) ;
if ( ret )
return ret ;
if ( temp1 & 0x04 ) {
/* 50Hz */
light_freq = 50 ;
} else {
/* 60Hz */
light_freq = 60 ;
}
} else {
/* auto */
ret = ov5640_read_reg ( sensor , OV5640_REG_SIGMADELTA_CTRL0C ,
& temp1 ) ;
if ( ret )
return ret ;
if ( temp1 & 0x01 ) {
/* 50Hz */
light_freq = 50 ;
} else {
/* 60Hz */
}
}
return light_freq ;
}
static int ov5640_set_bandingfilter ( struct ov5640_dev * sensor )
{
u32 band_step60 , max_band60 , band_step50 , max_band50 , prev_vts ;
int ret ;
/* read preview PCLK */
ret = ov5640_get_sysclk ( sensor ) ;
if ( ret < 0 )
return ret ;
if ( ret = = 0 )
return - EINVAL ;
sensor - > prev_sysclk = ret ;
/* read preview HTS */
ret = ov5640_get_hts ( sensor ) ;
if ( ret < 0 )
return ret ;
if ( ret = = 0 )
return - EINVAL ;
sensor - > prev_hts = ret ;
/* read preview VTS */
ret = ov5640_get_vts ( sensor ) ;
if ( ret < 0 )
return ret ;
prev_vts = ret ;
/* calculate banding filter */
/* 60Hz */
band_step60 = sensor - > prev_sysclk * 100 / sensor - > prev_hts * 100 / 120 ;
ret = ov5640_write_reg16 ( sensor , OV5640_REG_AEC_B60_STEP , band_step60 ) ;
if ( ret )
return ret ;
if ( ! band_step60 )
return - EINVAL ;
max_band60 = ( int ) ( ( prev_vts - 4 ) / band_step60 ) ;
ret = ov5640_write_reg ( sensor , OV5640_REG_AEC_CTRL0D , max_band60 ) ;
if ( ret )
return ret ;
/* 50Hz */
band_step50 = sensor - > prev_sysclk * 100 / sensor - > prev_hts ;
ret = ov5640_write_reg16 ( sensor , OV5640_REG_AEC_B50_STEP , band_step50 ) ;
if ( ret )
return ret ;
if ( ! band_step50 )
return - EINVAL ;
max_band50 = ( int ) ( ( prev_vts - 4 ) / band_step50 ) ;
return ov5640_write_reg ( sensor , OV5640_REG_AEC_CTRL0E , max_band50 ) ;
}
static int ov5640_set_ae_target ( struct ov5640_dev * sensor , int target )
{
/* stable in high */
u32 fast_high , fast_low ;
int ret ;
sensor - > ae_low = target * 23 / 25 ; /* 0.92 */
sensor - > ae_high = target * 27 / 25 ; /* 1.08 */
fast_high = sensor - > ae_high < < 1 ;
if ( fast_high > 255 )
fast_high = 255 ;
fast_low = sensor - > ae_low > > 1 ;
ret = ov5640_write_reg ( sensor , OV5640_REG_AEC_CTRL0F , sensor - > ae_high ) ;
if ( ret )
return ret ;
ret = ov5640_write_reg ( sensor , OV5640_REG_AEC_CTRL10 , sensor - > ae_low ) ;
if ( ret )
return ret ;
ret = ov5640_write_reg ( sensor , OV5640_REG_AEC_CTRL1B , sensor - > ae_high ) ;
if ( ret )
return ret ;
ret = ov5640_write_reg ( sensor , OV5640_REG_AEC_CTRL1E , sensor - > ae_low ) ;
if ( ret )
return ret ;
ret = ov5640_write_reg ( sensor , OV5640_REG_AEC_CTRL11 , fast_high ) ;
if ( ret )
return ret ;
return ov5640_write_reg ( sensor , OV5640_REG_AEC_CTRL1F , fast_low ) ;
}
2018-09-11 16:48:19 +03:00
static int ov5640_get_binning ( struct ov5640_dev * sensor )
2017-06-07 21:33:56 +03:00
{
u8 temp ;
int ret ;
ret = ov5640_read_reg ( sensor , OV5640_REG_TIMING_TC_REG21 , & temp ) ;
if ( ret )
return ret ;
2018-09-11 16:48:19 +03:00
return temp & BIT ( 0 ) ;
2017-06-07 21:33:56 +03:00
}
2018-06-18 13:29:17 +03:00
static int ov5640_set_binning ( struct ov5640_dev * sensor , bool enable )
{
int ret ;
/*
* TIMING TC REG21 :
* - [ 0 ] : Horizontal binning enable
*/
ret = ov5640_mod_reg ( sensor , OV5640_REG_TIMING_TC_REG21 ,
BIT ( 0 ) , enable ? BIT ( 0 ) : 0 ) ;
if ( ret )
return ret ;
/*
* TIMING TC REG20 :
* - [ 0 ] : Undocumented , but hardcoded init sequences
* are always setting REG21 / REG20 bit 0 to same value . . .
*/
return ov5640_mod_reg ( sensor , OV5640_REG_TIMING_TC_REG20 ,
BIT ( 0 ) , enable ? BIT ( 0 ) : 0 ) ;
}
2017-06-07 21:33:56 +03:00
static int ov5640_set_virtual_channel ( struct ov5640_dev * sensor )
{
2018-02-06 16:24:09 +03:00
struct i2c_client * client = sensor - > i2c_client ;
2017-06-07 21:33:56 +03:00
u8 temp , channel = virtual_channel ;
int ret ;
2018-02-06 16:24:09 +03:00
if ( channel > 3 ) {
dev_err ( & client - > dev ,
" %s: wrong virtual_channel parameter, expected (0..3), got %d \n " ,
__func__ , channel ) ;
2017-06-07 21:33:56 +03:00
return - EINVAL ;
2018-02-06 16:24:09 +03:00
}
2017-06-07 21:33:56 +03:00
ret = ov5640_read_reg ( sensor , OV5640_REG_DEBUG_MODE , & temp ) ;
if ( ret )
return ret ;
temp & = ~ ( 3 < < 6 ) ;
temp | = ( channel < < 6 ) ;
return ov5640_write_reg ( sensor , OV5640_REG_DEBUG_MODE , temp ) ;
}
static const struct ov5640_mode_info *
ov5640_find_mode ( struct ov5640_dev * sensor , enum ov5640_frame_rate fr ,
int width , int height , bool nearest )
{
2018-06-20 11:40:57 +03:00
const struct ov5640_mode_info * mode ;
2017-06-07 21:33:56 +03:00
2018-06-20 11:40:57 +03:00
mode = v4l2_find_nearest_size ( ov5640_mode_data [ fr ] ,
ARRAY_SIZE ( ov5640_mode_data [ fr ] ) ,
hact , vact ,
width , height ) ;
2017-06-07 21:33:56 +03:00
2018-06-20 11:40:57 +03:00
if ( ! mode | |
( ! nearest & & ( mode - > hact ! = width | | mode - > vact ! = height ) ) )
return NULL ;
2017-06-07 21:33:56 +03:00
return mode ;
}
/*
* sensor changes between scaling and subsampling , go through
* exposure calculation
*/
2018-02-01 11:44:06 +03:00
static int ov5640_set_mode_exposure_calc ( struct ov5640_dev * sensor ,
const struct ov5640_mode_info * mode )
2017-06-07 21:33:56 +03:00
{
u32 prev_shutter , prev_gain16 ;
u32 cap_shutter , cap_gain16 ;
u32 cap_sysclk , cap_hts , cap_vts ;
u32 light_freq , cap_bandfilt , cap_maxband ;
u32 cap_gain16_shutter ;
u8 average ;
int ret ;
2018-02-01 11:44:06 +03:00
if ( ! mode - > reg_data )
2017-06-07 21:33:56 +03:00
return - EINVAL ;
/* read preview shutter */
ret = ov5640_get_exposure ( sensor ) ;
if ( ret < 0 )
return ret ;
prev_shutter = ret ;
2018-09-11 16:48:19 +03:00
ret = ov5640_get_binning ( sensor ) ;
2017-06-07 21:33:56 +03:00
if ( ret < 0 )
return ret ;
if ( ret & & mode - > id ! = OV5640_MODE_720P_1280_720 & &
mode - > id ! = OV5640_MODE_1080P_1920_1080 )
prev_shutter * = 2 ;
/* read preview gain */
ret = ov5640_get_gain ( sensor ) ;
if ( ret < 0 )
return ret ;
prev_gain16 = ret ;
/* get average */
ret = ov5640_read_reg ( sensor , OV5640_REG_AVG_READOUT , & average ) ;
if ( ret )
return ret ;
/* turn off night mode for capture */
ret = ov5640_set_night_mode ( sensor ) ;
if ( ret < 0 )
return ret ;
/* Write capture setting */
ret = ov5640_load_regs ( sensor , mode ) ;
if ( ret < 0 )
return ret ;
/* read capture VTS */
ret = ov5640_get_vts ( sensor ) ;
if ( ret < 0 )
return ret ;
cap_vts = ret ;
ret = ov5640_get_hts ( sensor ) ;
if ( ret < 0 )
return ret ;
if ( ret = = 0 )
return - EINVAL ;
cap_hts = ret ;
ret = ov5640_get_sysclk ( sensor ) ;
if ( ret < 0 )
return ret ;
if ( ret = = 0 )
return - EINVAL ;
cap_sysclk = ret ;
/* calculate capture banding filter */
ret = ov5640_get_light_freq ( sensor ) ;
if ( ret < 0 )
return ret ;
light_freq = ret ;
if ( light_freq = = 60 ) {
/* 60Hz */
cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120 ;
} else {
/* 50Hz */
cap_bandfilt = cap_sysclk * 100 / cap_hts ;
}
if ( ! sensor - > prev_sysclk ) {
ret = ov5640_get_sysclk ( sensor ) ;
if ( ret < 0 )
return ret ;
if ( ret = = 0 )
return - EINVAL ;
sensor - > prev_sysclk = ret ;
}
if ( ! cap_bandfilt )
return - EINVAL ;
cap_maxband = ( int ) ( ( cap_vts - 4 ) / cap_bandfilt ) ;
/* calculate capture shutter/gain16 */
if ( average > sensor - > ae_low & & average < sensor - > ae_high ) {
/* in stable range */
cap_gain16_shutter =
prev_gain16 * prev_shutter *
cap_sysclk / sensor - > prev_sysclk *
sensor - > prev_hts / cap_hts *
sensor - > ae_target / average ;
} else {
cap_gain16_shutter =
prev_gain16 * prev_shutter *
cap_sysclk / sensor - > prev_sysclk *
sensor - > prev_hts / cap_hts ;
}
/* gain to shutter */
if ( cap_gain16_shutter < ( cap_bandfilt * 16 ) ) {
/* shutter < 1/100 */
cap_shutter = cap_gain16_shutter / 16 ;
if ( cap_shutter < 1 )
cap_shutter = 1 ;
cap_gain16 = cap_gain16_shutter / cap_shutter ;
if ( cap_gain16 < 16 )
cap_gain16 = 16 ;
} else {
if ( cap_gain16_shutter > ( cap_bandfilt * cap_maxband * 16 ) ) {
/* exposure reach max */
cap_shutter = cap_bandfilt * cap_maxband ;
if ( ! cap_shutter )
return - EINVAL ;
cap_gain16 = cap_gain16_shutter / cap_shutter ;
} else {
/* 1/100 < (cap_shutter = n/100) =< max */
cap_shutter =
( ( int ) ( cap_gain16_shutter / 16 / cap_bandfilt ) )
* cap_bandfilt ;
if ( ! cap_shutter )
return - EINVAL ;
cap_gain16 = cap_gain16_shutter / cap_shutter ;
}
}
/* set capture gain */
2018-09-11 16:48:18 +03:00
ret = ov5640_set_gain ( sensor , cap_gain16 ) ;
2017-06-07 21:33:56 +03:00
if ( ret )
return ret ;
/* write capture shutter */
if ( cap_shutter > ( cap_vts - 4 ) ) {
cap_vts = cap_shutter + 4 ;
ret = ov5640_set_vts ( sensor , cap_vts ) ;
if ( ret < 0 )
return ret ;
}
/* set exposure */
2018-09-11 16:48:18 +03:00
return ov5640_set_exposure ( sensor , cap_shutter ) ;
2017-06-07 21:33:56 +03:00
}
/*
* if sensor changes inside scaling or subsampling
* change mode directly
*/
static int ov5640_set_mode_direct ( struct ov5640_dev * sensor ,
2018-09-11 16:48:18 +03:00
const struct ov5640_mode_info * mode )
2017-06-07 21:33:56 +03:00
{
2018-02-01 11:44:06 +03:00
if ( ! mode - > reg_data )
2017-06-07 21:33:56 +03:00
return - EINVAL ;
/* Write capture setting */
2018-09-11 16:48:18 +03:00
return ov5640_load_regs ( sensor , mode ) ;
2017-06-07 21:33:56 +03:00
}
2018-09-11 16:48:21 +03:00
static int ov5640_set_mode ( struct ov5640_dev * sensor )
2017-06-07 21:33:56 +03:00
{
const struct ov5640_mode_info * mode = sensor - > current_mode ;
2018-09-11 16:48:21 +03:00
const struct ov5640_mode_info * orig_mode = sensor - > last_mode ;
2017-06-07 21:33:56 +03:00
enum ov5640_downsize_mode dn_mode , orig_dn_mode ;
2018-09-11 16:48:18 +03:00
bool auto_gain = sensor - > ctrls . auto_gain - > val = = 1 ;
2018-09-11 16:48:17 +03:00
bool auto_exp = sensor - > ctrls . auto_exp - > val = = V4L2_EXPOSURE_AUTO ;
2017-06-07 21:33:56 +03:00
int ret ;
dn_mode = mode - > dn_mode ;
orig_dn_mode = orig_mode - > dn_mode ;
/* auto gain and exposure must be turned off when changing modes */
2018-09-11 16:48:18 +03:00
if ( auto_gain ) {
ret = ov5640_set_autogain ( sensor , false ) ;
if ( ret )
return ret ;
}
2018-04-16 15:36:52 +03:00
2018-09-11 16:48:18 +03:00
if ( auto_exp ) {
ret = ov5640_set_autoexposure ( sensor , false ) ;
if ( ret )
goto restore_auto_gain ;
}
2017-06-07 21:33:56 +03:00
if ( ( dn_mode = = SUBSAMPLING & & orig_dn_mode = = SCALING ) | |
( dn_mode = = SCALING & & orig_dn_mode = = SUBSAMPLING ) ) {
/*
* change between subsampling and scaling
2018-09-11 16:48:18 +03:00
* go through exposure calculation
2017-06-07 21:33:56 +03:00
*/
ret = ov5640_set_mode_exposure_calc ( sensor , mode ) ;
} else {
/*
* change inside subsampling or scaling
* download firmware directly
*/
2018-09-11 16:48:18 +03:00
ret = ov5640_set_mode_direct ( sensor , mode ) ;
2017-06-07 21:33:56 +03:00
}
if ( ret < 0 )
2018-09-11 16:48:18 +03:00
goto restore_auto_exp_gain ;
/* restore auto gain and exposure */
if ( auto_gain )
ov5640_set_autogain ( sensor , true ) ;
if ( auto_exp )
ov5640_set_autoexposure ( sensor , true ) ;
2017-06-07 21:33:56 +03:00
2018-06-18 13:29:17 +03:00
ret = ov5640_set_binning ( sensor , dn_mode ! = SCALING ) ;
if ( ret < 0 )
return ret ;
2017-06-07 21:33:56 +03:00
ret = ov5640_set_ae_target ( sensor , sensor - > ae_target ) ;
if ( ret < 0 )
return ret ;
ret = ov5640_get_light_freq ( sensor ) ;
if ( ret < 0 )
return ret ;
ret = ov5640_set_bandingfilter ( sensor ) ;
if ( ret < 0 )
return ret ;
ret = ov5640_set_virtual_channel ( sensor ) ;
if ( ret < 0 )
return ret ;
sensor - > pending_mode_change = false ;
2018-09-11 16:48:21 +03:00
sensor - > last_mode = mode ;
2017-06-07 21:33:56 +03:00
return 0 ;
2018-09-11 16:48:18 +03:00
restore_auto_exp_gain :
if ( auto_exp )
ov5640_set_autoexposure ( sensor , true ) ;
restore_auto_gain :
if ( auto_gain )
ov5640_set_autogain ( sensor , true ) ;
return ret ;
2017-06-07 21:33:56 +03:00
}
2018-03-11 18:34:41 +03:00
static int ov5640_set_framefmt ( struct ov5640_dev * sensor ,
struct v4l2_mbus_framefmt * format ) ;
2017-06-07 21:33:56 +03:00
/* restore the last set video mode after chip power-on */
static int ov5640_restore_mode ( struct ov5640_dev * sensor )
{
int ret ;
/* first load the initial register values */
ret = ov5640_load_regs ( sensor , & ov5640_mode_init_data ) ;
if ( ret < 0 )
return ret ;
2018-09-11 16:48:21 +03:00
sensor - > last_mode = & ov5640_mode_init_data ;
2017-06-07 21:33:56 +03:00
2018-04-16 15:36:53 +03:00
ret = ov5640_mod_reg ( sensor , OV5640_REG_SYS_ROOT_DIVIDER , 0x3f ,
( ilog2 ( OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT ) < < 2 ) |
ilog2 ( OV5640_SCLK_ROOT_DIVIDER_DEFAULT ) ) ;
if ( ret )
return ret ;
2017-06-07 21:33:56 +03:00
/* now restore the last capture mode */
2018-09-11 16:48:21 +03:00
ret = ov5640_set_mode ( sensor ) ;
2018-03-11 18:34:41 +03:00
if ( ret < 0 )
return ret ;
return ov5640_set_framefmt ( sensor , & sensor - > fmt ) ;
2017-06-07 21:33:56 +03:00
}
static void ov5640_power ( struct ov5640_dev * sensor , bool enable )
{
2018-01-03 12:57:28 +03:00
gpiod_set_value_cansleep ( sensor - > pwdn_gpio , enable ? 0 : 1 ) ;
2017-06-07 21:33:56 +03:00
}
static void ov5640_reset ( struct ov5640_dev * sensor )
{
if ( ! sensor - > reset_gpio )
return ;
2018-01-03 12:57:28 +03:00
gpiod_set_value_cansleep ( sensor - > reset_gpio , 0 ) ;
2017-06-07 21:33:56 +03:00
/* camera power cycle */
ov5640_power ( sensor , false ) ;
usleep_range ( 5000 , 10000 ) ;
ov5640_power ( sensor , true ) ;
usleep_range ( 5000 , 10000 ) ;
2018-01-03 12:57:28 +03:00
gpiod_set_value_cansleep ( sensor - > reset_gpio , 1 ) ;
2017-06-07 21:33:56 +03:00
usleep_range ( 1000 , 2000 ) ;
2018-01-03 12:57:28 +03:00
gpiod_set_value_cansleep ( sensor - > reset_gpio , 0 ) ;
2017-06-07 21:33:56 +03:00
usleep_range ( 5000 , 10000 ) ;
}
2018-01-03 12:57:29 +03:00
static int ov5640_set_power_on ( struct ov5640_dev * sensor )
2017-06-07 21:33:56 +03:00
{
2018-01-03 12:57:29 +03:00
struct i2c_client * client = sensor - > i2c_client ;
int ret ;
2017-06-07 21:33:56 +03:00
2018-01-03 12:57:29 +03:00
ret = clk_prepare_enable ( sensor - > xclk ) ;
if ( ret ) {
dev_err ( & client - > dev , " %s: failed to enable clock \n " ,
__func__ ) ;
return ret ;
}
2017-06-07 21:33:56 +03:00
2018-01-03 12:57:29 +03:00
ret = regulator_bulk_enable ( OV5640_NUM_SUPPLIES ,
sensor - > supplies ) ;
if ( ret ) {
dev_err ( & client - > dev , " %s: failed to enable regulators \n " ,
__func__ ) ;
goto xclk_off ;
}
ov5640_reset ( sensor ) ;
ov5640_power ( sensor , true ) ;
ret = ov5640_init_slave_id ( sensor ) ;
if ( ret )
goto power_off ;
return 0 ;
power_off :
ov5640_power ( sensor , false ) ;
regulator_bulk_disable ( OV5640_NUM_SUPPLIES , sensor - > supplies ) ;
xclk_off :
clk_disable_unprepare ( sensor - > xclk ) ;
return ret ;
}
static void ov5640_set_power_off ( struct ov5640_dev * sensor )
{
ov5640_power ( sensor , false ) ;
regulator_bulk_disable ( OV5640_NUM_SUPPLIES , sensor - > supplies ) ;
clk_disable_unprepare ( sensor - > xclk ) ;
}
2017-06-07 21:33:56 +03:00
2018-01-03 12:57:29 +03:00
static int ov5640_set_power ( struct ov5640_dev * sensor , bool on )
{
int ret = 0 ;
2017-06-07 21:33:56 +03:00
2018-01-03 12:57:29 +03:00
if ( on ) {
ret = ov5640_set_power_on ( sensor ) ;
2017-06-07 21:33:56 +03:00
if ( ret )
2018-01-03 12:57:29 +03:00
return ret ;
2017-06-07 21:33:56 +03:00
ret = ov5640_restore_mode ( sensor ) ;
if ( ret )
goto power_off ;
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 12:51:52 +03:00
/* We're done here for DVP bus, while CSI-2 needs setup. */
2018-07-04 00:19:27 +03:00
if ( sensor - > ep . bus_type ! = V4L2_MBUS_CSI2_DPHY )
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 12:51:52 +03:00
return 0 ;
/*
* Power up MIPI HS Tx and LS Rx ; 2 data lanes mode
*
* 0x300e = 0x40
* [ 7 : 5 ] = 010 : 2 data lanes mode ( see FIXME note in
* " ov5640_set_stream_mipi() " )
* [ 4 ] = 0 : Power up MIPI HS Tx
* [ 3 ] = 0 : Power up MIPI LS Rx
* [ 2 ] = 0 : MIPI interface disabled
*/
ret = ov5640_write_reg ( sensor ,
OV5640_REG_IO_MIPI_CTRL00 , 0x40 ) ;
if ( ret )
goto power_off ;
/*
* Gate clock and set LP11 in ' no packets mode ' ( idle )
*
* 0x4800 = 0x24
* [ 5 ] = 1 : Gate clock when ' no packets '
* [ 2 ] = 1 : MIPI bus in LP11 when ' no packets '
*/
ret = ov5640_write_reg ( sensor ,
OV5640_REG_MIPI_CTRL00 , 0x24 ) ;
if ( ret )
goto power_off ;
/*
* Set data lanes and clock in LP11 when ' sleeping '
*
* 0x3019 = 0x70
* [ 6 ] = 1 : MIPI data lane 2 in LP11 when ' sleeping '
* [ 5 ] = 1 : MIPI data lane 1 in LP11 when ' sleeping '
* [ 4 ] = 1 : MIPI clock lane in LP11 when ' sleeping '
*/
ret = ov5640_write_reg ( sensor ,
OV5640_REG_PAD_OUTPUT00 , 0x70 ) ;
if ( ret )
goto power_off ;
/* Give lanes some time to coax into LP11 state. */
usleep_range ( 500 , 1000 ) ;
} else {
2018-07-04 00:19:27 +03:00
if ( sensor - > ep . bus_type = = V4L2_MBUS_CSI2_DPHY ) {
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 12:51:52 +03:00
/* Reset MIPI bus settings to their default values. */
ov5640_write_reg ( sensor ,
OV5640_REG_IO_MIPI_CTRL00 , 0x58 ) ;
ov5640_write_reg ( sensor ,
OV5640_REG_MIPI_CTRL00 , 0x04 ) ;
ov5640_write_reg ( sensor ,
OV5640_REG_PAD_OUTPUT00 , 0x00 ) ;
2018-01-03 12:57:31 +03:00
}
2017-06-07 21:33:56 +03:00
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 12:51:52 +03:00
ov5640_set_power_off ( sensor ) ;
2017-06-07 21:33:56 +03:00
}
media: ov5640: Re-work MIPI startup sequence
Rework the MIPI interface startup sequence with the following changes:
- Remove MIPI bus initialization from the initial settings blob
- At set_power(1) time power up MIPI Tx/Rx and set data and clock lanes in
LP11 during 'sleep' and 'idle' with MIPI clock in non-continuous mode.
- At s_stream time enable/disable the MIPI interface output.
- Restore default settings at set_power(0) time.
Before this commit the sensor MIPI interface was initialized with settings
that require a start/stop sequence at power-up time in order to force lanes
into LP11 state, as they were initialized in LP00 when in 'sleep mode',
which is assumed to be the sensor manual definition for the D-PHY defined
stop mode.
The stream start/stop was performed by enabling disabling clock gating,
and had the side effect to change the lanes sleep mode configuration when
stream was stopped.
Clock gating/ungating:
- ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5),
- on ? 0 : BIT(5));
- if (ret)
Set lanes in LP11 when in 'sleep mode':
- ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00,
- on ? 0x00 : 0x70);
This commit fixes an issue reported by Jagan Teki on i.MX6 platforms that
prevents the host interface from powering up correctly:
https://lkml.org/lkml/2018/6/1/38
It also improves MIPI capture operations stability on my testing platform
where MIPI capture often failed and returned all-purple frames.
Fixes: f22996db44e2 ("media: ov5640: add support of DVP parallel interface")
Tested-by: Steve Longerbeam <slongerbeam@gmail.com> (i.MX6q SabreSD, CSI-2)
Tested-by: Loic Poulain <loic.poulain@linaro.org> (Dragonboard-410c, CSI-2)
Reported-by: Jagan Teki <jagan@amarulasolutions.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2018-07-06 12:51:52 +03:00
return 0 ;
2017-06-07 21:33:56 +03:00
power_off :
2018-01-03 12:57:29 +03:00
ov5640_set_power_off ( sensor ) ;
2017-06-07 21:33:56 +03:00
return ret ;
}
/* --------------- Subdev Operations --------------- */
static int ov5640_s_power ( struct v4l2_subdev * sd , int on )
{
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
int ret = 0 ;
mutex_lock ( & sensor - > lock ) ;
/*
* If the power count is modified from 0 to ! = 0 or from ! = 0 to 0 ,
* update the power state .
*/
if ( sensor - > power_count = = ! on ) {
ret = ov5640_set_power ( sensor , ! ! on ) ;
if ( ret )
goto out ;
}
/* Update the power count. */
sensor - > power_count + = on ? 1 : - 1 ;
WARN_ON ( sensor - > power_count < 0 ) ;
out :
mutex_unlock ( & sensor - > lock ) ;
if ( on & & ! ret & & sensor - > power_count = = 1 ) {
/* restore controls */
ret = v4l2_ctrl_handler_setup ( & sensor - > ctrls . handler ) ;
}
return ret ;
}
static int ov5640_try_frame_interval ( struct ov5640_dev * sensor ,
struct v4l2_fract * fi ,
u32 width , u32 height )
{
const struct ov5640_mode_info * mode ;
u32 minfps , maxfps , fps ;
int ret ;
minfps = ov5640_framerates [ OV5640_15_FPS ] ;
maxfps = ov5640_framerates [ OV5640_30_FPS ] ;
if ( fi - > numerator = = 0 ) {
fi - > denominator = maxfps ;
fi - > numerator = 1 ;
return OV5640_30_FPS ;
}
fps = DIV_ROUND_CLOSEST ( fi - > denominator , fi - > numerator ) ;
fi - > numerator = 1 ;
if ( fps > maxfps )
fi - > denominator = maxfps ;
else if ( fps < minfps )
fi - > denominator = minfps ;
else if ( 2 * fps > = 2 * minfps + ( maxfps - minfps ) )
fi - > denominator = maxfps ;
else
fi - > denominator = minfps ;
ret = ( fi - > denominator = = minfps ) ? OV5640_15_FPS : OV5640_30_FPS ;
mode = ov5640_find_mode ( sensor , ret , width , height , false ) ;
return mode ? ret : - EINVAL ;
}
static int ov5640_get_fmt ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_format * format )
{
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
struct v4l2_mbus_framefmt * fmt ;
if ( format - > pad ! = 0 )
return - EINVAL ;
mutex_lock ( & sensor - > lock ) ;
if ( format - > which = = V4L2_SUBDEV_FORMAT_TRY )
fmt = v4l2_subdev_get_try_format ( & sensor - > sd , cfg ,
format - > pad ) ;
else
fmt = & sensor - > fmt ;
format - > format = * fmt ;
mutex_unlock ( & sensor - > lock ) ;
return 0 ;
}
static int ov5640_try_fmt_internal ( struct v4l2_subdev * sd ,
struct v4l2_mbus_framefmt * fmt ,
enum ov5640_frame_rate fr ,
const struct ov5640_mode_info * * new_mode )
{
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
const struct ov5640_mode_info * mode ;
2018-01-03 12:57:32 +03:00
int i ;
2017-06-07 21:33:56 +03:00
mode = ov5640_find_mode ( sensor , fr , fmt - > width , fmt - > height , true ) ;
if ( ! mode )
return - EINVAL ;
2018-04-16 15:36:54 +03:00
fmt - > width = mode - > hact ;
fmt - > height = mode - > vact ;
2017-06-07 21:33:56 +03:00
if ( new_mode )
* new_mode = mode ;
2018-01-03 12:57:32 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( ov5640_formats ) ; i + + )
if ( ov5640_formats [ i ] . code = = fmt - > code )
break ;
if ( i > = ARRAY_SIZE ( ov5640_formats ) )
2018-03-06 20:04:39 +03:00
i = 0 ;
fmt - > code = ov5640_formats [ i ] . code ;
fmt - > colorspace = ov5640_formats [ i ] . colorspace ;
fmt - > ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT ( fmt - > colorspace ) ;
fmt - > quantization = V4L2_QUANTIZATION_FULL_RANGE ;
fmt - > xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT ( fmt - > colorspace ) ;
2018-01-03 12:57:32 +03:00
2017-06-07 21:33:56 +03:00
return 0 ;
}
static int ov5640_set_fmt ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_format * format )
{
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
const struct ov5640_mode_info * new_mode ;
2018-03-06 20:04:39 +03:00
struct v4l2_mbus_framefmt * mbus_fmt = & format - > format ;
2018-12-03 11:44:16 +03:00
struct v4l2_mbus_framefmt * fmt ;
2017-06-07 21:33:56 +03:00
int ret ;
if ( format - > pad ! = 0 )
return - EINVAL ;
mutex_lock ( & sensor - > lock ) ;
if ( sensor - > streaming ) {
ret = - EBUSY ;
goto out ;
}
2018-03-06 20:04:39 +03:00
ret = ov5640_try_fmt_internal ( sd , mbus_fmt ,
2017-06-07 21:33:56 +03:00
sensor - > current_fr , & new_mode ) ;
if ( ret )
goto out ;
2018-12-03 11:44:16 +03:00
if ( format - > which = = V4L2_SUBDEV_FORMAT_TRY )
fmt = v4l2_subdev_get_try_format ( sd , cfg , 0 ) ;
else
fmt = & sensor - > fmt ;
2017-06-07 21:33:56 +03:00
2018-12-03 11:44:16 +03:00
* fmt = * mbus_fmt ;
2017-06-07 21:33:56 +03:00
2018-07-04 16:04:38 +03:00
if ( new_mode ! = sensor - > current_mode ) {
sensor - > current_mode = new_mode ;
sensor - > pending_mode_change = true ;
}
2018-12-03 11:44:16 +03:00
if ( mbus_fmt - > code ! = sensor - > fmt . code )
2018-08-16 12:46:53 +03:00
sensor - > pending_fmt_change = true ;
2018-12-03 11:44:16 +03:00
2017-06-07 21:33:56 +03:00
out :
mutex_unlock ( & sensor - > lock ) ;
return ret ;
}
2018-01-03 12:57:32 +03:00
static int ov5640_set_framefmt ( struct ov5640_dev * sensor ,
struct v4l2_mbus_framefmt * format )
{
int ret = 0 ;
bool is_rgb = false ;
2018-01-31 12:08:10 +03:00
bool is_jpeg = false ;
2018-01-03 12:57:32 +03:00
u8 val ;
switch ( format - > code ) {
case MEDIA_BUS_FMT_UYVY8_2X8 :
/* YUV422, UYVY */
val = 0x3f ;
break ;
case MEDIA_BUS_FMT_YUYV8_2X8 :
/* YUV422, YUYV */
val = 0x30 ;
break ;
case MEDIA_BUS_FMT_RGB565_2X8_LE :
/* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */
val = 0x6F ;
is_rgb = true ;
break ;
case MEDIA_BUS_FMT_RGB565_2X8_BE :
/* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */
val = 0x61 ;
is_rgb = true ;
break ;
2018-01-31 12:08:10 +03:00
case MEDIA_BUS_FMT_JPEG_1X8 :
/* YUV422, YUYV */
val = 0x30 ;
is_jpeg = true ;
break ;
2018-01-03 12:57:32 +03:00
default :
return - EINVAL ;
}
/* FORMAT CONTROL00: YUV and RGB formatting */
ret = ov5640_write_reg ( sensor , OV5640_REG_FORMAT_CONTROL00 , val ) ;
if ( ret )
return ret ;
/* FORMAT MUX CONTROL: ISP YUV or RGB */
2018-01-31 12:08:10 +03:00
ret = ov5640_write_reg ( sensor , OV5640_REG_ISP_FORMAT_MUX_CTRL ,
is_rgb ? 0x01 : 0x00 ) ;
if ( ret )
return ret ;
/*
* TIMING TC REG21 :
* - [ 5 ] : JPEG enable
*/
ret = ov5640_mod_reg ( sensor , OV5640_REG_TIMING_TC_REG21 ,
BIT ( 5 ) , is_jpeg ? BIT ( 5 ) : 0 ) ;
if ( ret )
return ret ;
/*
* SYSTEM RESET02 :
* - [ 4 ] : Reset JFIFO
* - [ 3 ] : Reset SFIFO
* - [ 2 ] : Reset JPEG
*/
ret = ov5640_mod_reg ( sensor , OV5640_REG_SYS_RESET02 ,
BIT ( 4 ) | BIT ( 3 ) | BIT ( 2 ) ,
is_jpeg ? 0 : ( BIT ( 4 ) | BIT ( 3 ) | BIT ( 2 ) ) ) ;
if ( ret )
return ret ;
/*
* CLOCK ENABLE02 :
* - [ 5 ] : Enable JPEG 2 x clock
* - [ 3 ] : Enable JPEG clock
*/
return ov5640_mod_reg ( sensor , OV5640_REG_SYS_CLOCK_ENABLE02 ,
BIT ( 5 ) | BIT ( 3 ) ,
is_jpeg ? ( BIT ( 5 ) | BIT ( 3 ) ) : 0 ) ;
2018-01-03 12:57:32 +03:00
}
2017-06-07 21:33:56 +03:00
/*
* Sensor Controls .
*/
static int ov5640_set_ctrl_hue ( struct ov5640_dev * sensor , int value )
{
int ret ;
if ( value ) {
ret = ov5640_mod_reg ( sensor , OV5640_REG_SDE_CTRL0 ,
BIT ( 0 ) , BIT ( 0 ) ) ;
if ( ret )
return ret ;
ret = ov5640_write_reg16 ( sensor , OV5640_REG_SDE_CTRL1 , value ) ;
} else {
ret = ov5640_mod_reg ( sensor , OV5640_REG_SDE_CTRL0 , BIT ( 0 ) , 0 ) ;
}
return ret ;
}
static int ov5640_set_ctrl_contrast ( struct ov5640_dev * sensor , int value )
{
int ret ;
if ( value ) {
ret = ov5640_mod_reg ( sensor , OV5640_REG_SDE_CTRL0 ,
BIT ( 2 ) , BIT ( 2 ) ) ;
if ( ret )
return ret ;
ret = ov5640_write_reg ( sensor , OV5640_REG_SDE_CTRL5 ,
value & 0xff ) ;
} else {
ret = ov5640_mod_reg ( sensor , OV5640_REG_SDE_CTRL0 , BIT ( 2 ) , 0 ) ;
}
return ret ;
}
static int ov5640_set_ctrl_saturation ( struct ov5640_dev * sensor , int value )
{
int ret ;
if ( value ) {
ret = ov5640_mod_reg ( sensor , OV5640_REG_SDE_CTRL0 ,
BIT ( 1 ) , BIT ( 1 ) ) ;
if ( ret )
return ret ;
ret = ov5640_write_reg ( sensor , OV5640_REG_SDE_CTRL3 ,
value & 0xff ) ;
if ( ret )
return ret ;
ret = ov5640_write_reg ( sensor , OV5640_REG_SDE_CTRL4 ,
value & 0xff ) ;
} else {
ret = ov5640_mod_reg ( sensor , OV5640_REG_SDE_CTRL0 , BIT ( 1 ) , 0 ) ;
}
return ret ;
}
static int ov5640_set_ctrl_white_balance ( struct ov5640_dev * sensor , int awb )
{
int ret ;
ret = ov5640_mod_reg ( sensor , OV5640_REG_AWB_MANUAL_CTRL ,
BIT ( 0 ) , awb ? 0 : 1 ) ;
if ( ret )
return ret ;
if ( ! awb ) {
u16 red = ( u16 ) sensor - > ctrls . red_balance - > val ;
u16 blue = ( u16 ) sensor - > ctrls . blue_balance - > val ;
ret = ov5640_write_reg16 ( sensor , OV5640_REG_AWB_R_GAIN , red ) ;
if ( ret )
return ret ;
ret = ov5640_write_reg16 ( sensor , OV5640_REG_AWB_B_GAIN , blue ) ;
}
return ret ;
}
2018-09-11 16:48:18 +03:00
static int ov5640_set_ctrl_exposure ( struct ov5640_dev * sensor ,
enum v4l2_exposure_auto_type auto_exposure )
2017-06-07 21:33:56 +03:00
{
struct ov5640_ctrls * ctrls = & sensor - > ctrls ;
2018-09-11 16:48:18 +03:00
bool auto_exp = ( auto_exposure = = V4L2_EXPOSURE_AUTO ) ;
2017-06-07 21:33:56 +03:00
int ret = 0 ;
if ( ctrls - > auto_exp - > is_new ) {
2018-09-11 16:48:18 +03:00
ret = ov5640_set_autoexposure ( sensor , auto_exp ) ;
2017-06-07 21:33:56 +03:00
if ( ret )
return ret ;
}
2018-09-11 16:48:18 +03:00
if ( ! auto_exp & & ctrls - > exposure - > is_new ) {
2017-06-07 21:33:56 +03:00
u16 max_exp ;
ret = ov5640_read_reg16 ( sensor , OV5640_REG_AEC_PK_VTS ,
& max_exp ) ;
if ( ret )
return ret ;
ret = ov5640_get_vts ( sensor ) ;
if ( ret < 0 )
return ret ;
max_exp + = ret ;
2018-01-22 13:06:40 +03:00
ret = 0 ;
2017-06-07 21:33:56 +03:00
if ( ctrls - > exposure - > val < max_exp )
ret = ov5640_set_exposure ( sensor , ctrls - > exposure - > val ) ;
}
return ret ;
}
2018-09-11 16:48:18 +03:00
static int ov5640_set_ctrl_gain ( struct ov5640_dev * sensor , bool auto_gain )
2017-06-07 21:33:56 +03:00
{
struct ov5640_ctrls * ctrls = & sensor - > ctrls ;
int ret = 0 ;
if ( ctrls - > auto_gain - > is_new ) {
2018-09-11 16:48:18 +03:00
ret = ov5640_set_autogain ( sensor , auto_gain ) ;
2017-06-07 21:33:56 +03:00
if ( ret )
return ret ;
}
2018-09-11 16:48:18 +03:00
if ( ! auto_gain & & ctrls - > gain - > is_new )
ret = ov5640_set_gain ( sensor , ctrls - > gain - > val ) ;
2017-06-07 21:33:56 +03:00
return ret ;
}
static int ov5640_set_ctrl_test_pattern ( struct ov5640_dev * sensor , int value )
{
return ov5640_mod_reg ( sensor , OV5640_REG_PRE_ISP_TEST_SET1 ,
0xa4 , value ? 0xa4 : 0 ) ;
}
2018-04-16 15:36:51 +03:00
static int ov5640_set_ctrl_light_freq ( struct ov5640_dev * sensor , int value )
{
int ret ;
ret = ov5640_mod_reg ( sensor , OV5640_REG_HZ5060_CTRL01 , BIT ( 7 ) ,
( value = = V4L2_CID_POWER_LINE_FREQUENCY_AUTO ) ?
0 : BIT ( 7 ) ) ;
if ( ret )
return ret ;
return ov5640_mod_reg ( sensor , OV5640_REG_HZ5060_CTRL00 , BIT ( 2 ) ,
( value = = V4L2_CID_POWER_LINE_FREQUENCY_50HZ ) ?
BIT ( 2 ) : 0 ) ;
}
2018-06-18 13:29:17 +03:00
static int ov5640_set_ctrl_hflip ( struct ov5640_dev * sensor , int value )
{
/*
2018-06-18 13:29:19 +03:00
* If sensor is mounted upside down , mirror logic is inversed .
*
2018-06-18 13:29:17 +03:00
* Sensor is a BSI ( Back Side Illuminated ) one ,
* so image captured is physically mirrored .
* This is why mirror logic is inversed in
* order to cancel this mirror effect .
*/
/*
* TIMING TC REG21 :
* - [ 2 ] : ISP mirror
* - [ 1 ] : Sensor mirror
*/
return ov5640_mod_reg ( sensor , OV5640_REG_TIMING_TC_REG21 ,
BIT ( 2 ) | BIT ( 1 ) ,
2018-06-18 13:29:19 +03:00
( ! ( value ^ sensor - > upside_down ) ) ?
( BIT ( 2 ) | BIT ( 1 ) ) : 0 ) ;
2018-06-18 13:29:17 +03:00
}
static int ov5640_set_ctrl_vflip ( struct ov5640_dev * sensor , int value )
{
2018-06-18 13:29:19 +03:00
/* If sensor is mounted upside down, flip logic is inversed */
2018-06-18 13:29:17 +03:00
/*
* TIMING TC REG20 :
* - [ 2 ] : ISP vflip
* - [ 1 ] : Sensor vflip
*/
return ov5640_mod_reg ( sensor , OV5640_REG_TIMING_TC_REG20 ,
BIT ( 2 ) | BIT ( 1 ) ,
2018-06-18 13:29:19 +03:00
( value ^ sensor - > upside_down ) ?
( BIT ( 2 ) | BIT ( 1 ) ) : 0 ) ;
2018-06-18 13:29:17 +03:00
}
2017-06-07 21:33:56 +03:00
static int ov5640_g_volatile_ctrl ( struct v4l2_ctrl * ctrl )
{
struct v4l2_subdev * sd = ctrl_to_sd ( ctrl ) ;
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
int val ;
/* v4l2_ctrl_lock() locks our own mutex */
switch ( ctrl - > id ) {
case V4L2_CID_AUTOGAIN :
val = ov5640_get_gain ( sensor ) ;
if ( val < 0 )
return val ;
sensor - > ctrls . gain - > val = val ;
break ;
case V4L2_CID_EXPOSURE_AUTO :
val = ov5640_get_exposure ( sensor ) ;
if ( val < 0 )
return val ;
sensor - > ctrls . exposure - > val = val ;
break ;
}
return 0 ;
}
static int ov5640_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct v4l2_subdev * sd = ctrl_to_sd ( ctrl ) ;
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
int ret ;
/* 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 right after power - up .
*/
if ( sensor - > power_count = = 0 )
return 0 ;
switch ( ctrl - > id ) {
case V4L2_CID_AUTOGAIN :
ret = ov5640_set_ctrl_gain ( sensor , ctrl - > val ) ;
break ;
case V4L2_CID_EXPOSURE_AUTO :
ret = ov5640_set_ctrl_exposure ( sensor , ctrl - > val ) ;
break ;
case V4L2_CID_AUTO_WHITE_BALANCE :
ret = ov5640_set_ctrl_white_balance ( sensor , ctrl - > val ) ;
break ;
case V4L2_CID_HUE :
ret = ov5640_set_ctrl_hue ( sensor , ctrl - > val ) ;
break ;
case V4L2_CID_CONTRAST :
ret = ov5640_set_ctrl_contrast ( sensor , ctrl - > val ) ;
break ;
case V4L2_CID_SATURATION :
ret = ov5640_set_ctrl_saturation ( sensor , ctrl - > val ) ;
break ;
case V4L2_CID_TEST_PATTERN :
ret = ov5640_set_ctrl_test_pattern ( sensor , ctrl - > val ) ;
break ;
2018-04-16 15:36:51 +03:00
case V4L2_CID_POWER_LINE_FREQUENCY :
ret = ov5640_set_ctrl_light_freq ( sensor , ctrl - > val ) ;
break ;
2018-06-18 13:29:17 +03:00
case V4L2_CID_HFLIP :
ret = ov5640_set_ctrl_hflip ( sensor , ctrl - > val ) ;
break ;
case V4L2_CID_VFLIP :
ret = ov5640_set_ctrl_vflip ( sensor , ctrl - > val ) ;
break ;
2017-06-07 21:33:56 +03:00
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
. g_volatile_ctrl = ov5640_g_volatile_ctrl ,
. s_ctrl = ov5640_s_ctrl ,
} ;
static const char * const test_pattern_menu [ ] = {
" Disabled " ,
" Color bars " ,
} ;
static int ov5640_init_controls ( struct ov5640_dev * sensor )
{
const struct v4l2_ctrl_ops * ops = & ov5640_ctrl_ops ;
struct ov5640_ctrls * ctrls = & sensor - > ctrls ;
struct v4l2_ctrl_handler * hdl = & ctrls - > handler ;
int ret ;
v4l2_ctrl_handler_init ( hdl , 32 ) ;
/* we can use our own mutex for the ctrl lock */
hdl - > lock = & sensor - > lock ;
/* Auto/manual white balance */
ctrls - > auto_wb = v4l2_ctrl_new_std ( hdl , ops ,
V4L2_CID_AUTO_WHITE_BALANCE ,
0 , 1 , 1 , 1 ) ;
ctrls - > blue_balance = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_BLUE_BALANCE ,
0 , 4095 , 1 , 0 ) ;
ctrls - > red_balance = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_RED_BALANCE ,
0 , 4095 , 1 , 0 ) ;
/* Auto/manual exposure */
ctrls - > auto_exp = v4l2_ctrl_new_std_menu ( hdl , ops ,
V4L2_CID_EXPOSURE_AUTO ,
V4L2_EXPOSURE_MANUAL , 0 ,
V4L2_EXPOSURE_AUTO ) ;
ctrls - > exposure = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_EXPOSURE ,
0 , 65535 , 1 , 0 ) ;
/* Auto/manual gain */
ctrls - > auto_gain = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_AUTOGAIN ,
0 , 1 , 1 , 1 ) ;
ctrls - > gain = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_GAIN ,
0 , 1023 , 1 , 0 ) ;
ctrls - > saturation = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_SATURATION ,
0 , 255 , 1 , 64 ) ;
ctrls - > hue = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_HUE ,
0 , 359 , 1 , 0 ) ;
ctrls - > contrast = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_CONTRAST ,
0 , 255 , 1 , 0 ) ;
ctrls - > test_pattern =
v4l2_ctrl_new_std_menu_items ( hdl , ops , V4L2_CID_TEST_PATTERN ,
ARRAY_SIZE ( test_pattern_menu ) - 1 ,
0 , 0 , test_pattern_menu ) ;
2018-06-18 13:29:17 +03:00
ctrls - > hflip = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_HFLIP ,
0 , 1 , 1 , 0 ) ;
ctrls - > vflip = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_VFLIP ,
0 , 1 , 1 , 0 ) ;
2017-06-07 21:33:56 +03:00
2018-04-16 15:36:51 +03:00
ctrls - > light_freq =
v4l2_ctrl_new_std_menu ( hdl , ops ,
V4L2_CID_POWER_LINE_FREQUENCY ,
V4L2_CID_POWER_LINE_FREQUENCY_AUTO , 0 ,
V4L2_CID_POWER_LINE_FREQUENCY_50HZ ) ;
2017-06-07 21:33:56 +03:00
if ( hdl - > error ) {
ret = hdl - > error ;
goto free_ctrls ;
}
ctrls - > gain - > flags | = V4L2_CTRL_FLAG_VOLATILE ;
ctrls - > exposure - > flags | = V4L2_CTRL_FLAG_VOLATILE ;
v4l2_ctrl_auto_cluster ( 3 , & ctrls - > auto_wb , 0 , false ) ;
v4l2_ctrl_auto_cluster ( 2 , & ctrls - > auto_gain , 0 , true ) ;
v4l2_ctrl_auto_cluster ( 2 , & ctrls - > auto_exp , 1 , true ) ;
sensor - > sd . ctrl_handler = hdl ;
return 0 ;
free_ctrls :
v4l2_ctrl_handler_free ( hdl ) ;
return ret ;
}
static int ov5640_enum_frame_size ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_frame_size_enum * fse )
{
if ( fse - > pad ! = 0 )
return - EINVAL ;
if ( fse - > index > = OV5640_NUM_MODES )
return - EINVAL ;
2018-02-01 11:44:06 +03:00
fse - > min_width =
2018-04-16 15:36:54 +03:00
ov5640_mode_data [ 0 ] [ fse - > index ] . hact ;
2018-02-01 11:44:06 +03:00
fse - > max_width = fse - > min_width ;
fse - > min_height =
2018-04-16 15:36:54 +03:00
ov5640_mode_data [ 0 ] [ fse - > index ] . vact ;
2018-02-01 11:44:06 +03:00
fse - > max_height = fse - > min_height ;
2017-06-07 21:33:56 +03:00
return 0 ;
}
static int ov5640_enum_frame_interval (
struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_frame_interval_enum * fie )
{
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
struct v4l2_fract tpf ;
int ret ;
if ( fie - > pad ! = 0 )
return - EINVAL ;
if ( fie - > index > = OV5640_NUM_FRAMERATES )
return - EINVAL ;
tpf . numerator = 1 ;
tpf . denominator = ov5640_framerates [ fie - > index ] ;
ret = ov5640_try_frame_interval ( sensor , & tpf ,
fie - > width , fie - > height ) ;
if ( ret < 0 )
return - EINVAL ;
fie - > interval = tpf ;
return 0 ;
}
static int ov5640_g_frame_interval ( struct v4l2_subdev * sd ,
struct v4l2_subdev_frame_interval * fi )
{
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
mutex_lock ( & sensor - > lock ) ;
fi - > interval = sensor - > frame_interval ;
mutex_unlock ( & sensor - > lock ) ;
return 0 ;
}
static int ov5640_s_frame_interval ( struct v4l2_subdev * sd ,
struct v4l2_subdev_frame_interval * fi )
{
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
const struct ov5640_mode_info * mode ;
int frame_rate , ret = 0 ;
if ( fi - > pad ! = 0 )
return - EINVAL ;
mutex_lock ( & sensor - > lock ) ;
if ( sensor - > streaming ) {
ret = - EBUSY ;
goto out ;
}
mode = sensor - > current_mode ;
frame_rate = ov5640_try_frame_interval ( sensor , & fi - > interval ,
2018-04-16 15:36:54 +03:00
mode - > hact , mode - > vact ) ;
2017-06-07 21:33:56 +03:00
if ( frame_rate < 0 )
frame_rate = OV5640_15_FPS ;
2018-06-20 11:40:57 +03:00
mode = ov5640_find_mode ( sensor , frame_rate , mode - > hact ,
mode - > vact , true ) ;
if ( ! mode ) {
ret = - EINVAL ;
goto out ;
}
2018-10-04 14:21:57 +03:00
if ( mode ! = sensor - > current_mode | |
frame_rate ! = sensor - > current_fr ) {
sensor - > current_fr = frame_rate ;
sensor - > frame_interval = fi - > interval ;
2018-07-04 16:04:38 +03:00
sensor - > current_mode = mode ;
sensor - > pending_mode_change = true ;
}
2017-06-07 21:33:56 +03:00
out :
mutex_unlock ( & sensor - > lock ) ;
return ret ;
}
static int ov5640_enum_mbus_code ( struct v4l2_subdev * sd ,
2018-02-01 11:44:06 +03:00
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_mbus_code_enum * code )
2017-06-07 21:33:56 +03:00
{
if ( code - > pad ! = 0 )
return - EINVAL ;
2018-01-03 12:57:32 +03:00
if ( code - > index > = ARRAY_SIZE ( ov5640_formats ) )
2017-06-07 21:33:56 +03:00
return - EINVAL ;
2018-01-03 12:57:32 +03:00
code - > code = ov5640_formats [ code - > index ] . code ;
2017-06-07 21:33:56 +03:00
return 0 ;
}
static int ov5640_s_stream ( struct v4l2_subdev * sd , int enable )
{
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
int ret = 0 ;
mutex_lock ( & sensor - > lock ) ;
if ( sensor - > streaming = = ! enable ) {
if ( enable & & sensor - > pending_mode_change ) {
2018-09-11 16:48:21 +03:00
ret = ov5640_set_mode ( sensor ) ;
2017-06-07 21:33:56 +03:00
if ( ret )
goto out ;
2018-08-16 12:46:53 +03:00
}
2018-01-03 12:57:32 +03:00
2018-08-16 12:46:53 +03:00
if ( enable & & sensor - > pending_fmt_change ) {
2018-01-03 12:57:32 +03:00
ret = ov5640_set_framefmt ( sensor , & sensor - > fmt ) ;
if ( ret )
goto out ;
2018-08-16 12:46:53 +03:00
sensor - > pending_fmt_change = false ;
2017-06-07 21:33:56 +03:00
}
2018-07-04 00:19:27 +03:00
if ( sensor - > ep . bus_type = = V4L2_MBUS_CSI2_DPHY )
2018-01-03 12:57:31 +03:00
ret = ov5640_set_stream_mipi ( sensor , enable ) ;
else
ret = ov5640_set_stream_dvp ( sensor , enable ) ;
2017-06-07 21:33:56 +03:00
if ( ! ret )
sensor - > streaming = enable ;
}
out :
mutex_unlock ( & sensor - > lock ) ;
return ret ;
}
static const struct v4l2_subdev_core_ops ov5640_core_ops = {
. s_power = ov5640_s_power ,
2018-11-12 19:00:52 +03:00
. log_status = v4l2_ctrl_subdev_log_status ,
. subscribe_event = v4l2_ctrl_subdev_subscribe_event ,
. unsubscribe_event = v4l2_event_subdev_unsubscribe ,
2017-06-07 21:33:56 +03:00
} ;
static const struct v4l2_subdev_video_ops ov5640_video_ops = {
. g_frame_interval = ov5640_g_frame_interval ,
. s_frame_interval = ov5640_s_frame_interval ,
. s_stream = ov5640_s_stream ,
} ;
static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
. enum_mbus_code = ov5640_enum_mbus_code ,
. get_fmt = ov5640_get_fmt ,
. set_fmt = ov5640_set_fmt ,
. enum_frame_size = ov5640_enum_frame_size ,
. enum_frame_interval = ov5640_enum_frame_interval ,
} ;
static const struct v4l2_subdev_ops ov5640_subdev_ops = {
. core = & ov5640_core_ops ,
. video = & ov5640_video_ops ,
. pad = & ov5640_pad_ops ,
} ;
static int ov5640_get_regulators ( struct ov5640_dev * sensor )
{
int i ;
for ( i = 0 ; i < OV5640_NUM_SUPPLIES ; i + + )
sensor - > supplies [ i ] . supply = ov5640_supply_name [ i ] ;
return devm_regulator_bulk_get ( & sensor - > i2c_client - > dev ,
OV5640_NUM_SUPPLIES ,
sensor - > supplies ) ;
}
2018-01-03 12:57:29 +03:00
static int ov5640_check_chip_id ( struct ov5640_dev * sensor )
{
struct i2c_client * client = sensor - > i2c_client ;
int ret = 0 ;
u16 chip_id ;
ret = ov5640_set_power_on ( sensor ) ;
if ( ret )
return ret ;
ret = ov5640_read_reg16 ( sensor , OV5640_REG_CHIP_ID , & chip_id ) ;
if ( ret ) {
dev_err ( & client - > dev , " %s: failed to read chip identifier \n " ,
__func__ ) ;
goto power_off ;
}
if ( chip_id ! = 0x5640 ) {
dev_err ( & client - > dev , " %s: wrong chip identifier, expected 0x5640, got 0x%x \n " ,
__func__ , chip_id ) ;
ret = - ENXIO ;
}
power_off :
ov5640_set_power_off ( sensor ) ;
return ret ;
}
2017-06-07 21:33:56 +03:00
static int ov5640_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct device * dev = & client - > dev ;
struct fwnode_handle * endpoint ;
struct ov5640_dev * sensor ;
2018-03-06 20:04:39 +03:00
struct v4l2_mbus_framefmt * fmt ;
2018-06-18 13:29:19 +03:00
u32 rotation ;
2017-06-07 21:33:56 +03:00
int ret ;
sensor = devm_kzalloc ( dev , sizeof ( * sensor ) , GFP_KERNEL ) ;
if ( ! sensor )
return - ENOMEM ;
sensor - > i2c_client = client ;
2018-08-16 12:46:53 +03:00
/*
* default init sequence initialize sensor to
* YUV422 UYVY VGA @ 30f ps
*/
2018-03-06 20:04:39 +03:00
fmt = & sensor - > fmt ;
2018-08-16 12:46:53 +03:00
fmt - > code = MEDIA_BUS_FMT_UYVY8_2X8 ;
fmt - > colorspace = V4L2_COLORSPACE_SRGB ;
2018-03-06 20:04:39 +03:00
fmt - > ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT ( fmt - > colorspace ) ;
fmt - > quantization = V4L2_QUANTIZATION_FULL_RANGE ;
fmt - > xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT ( fmt - > colorspace ) ;
fmt - > width = 640 ;
fmt - > height = 480 ;
fmt - > field = V4L2_FIELD_NONE ;
2017-06-07 21:33:56 +03:00
sensor - > frame_interval . numerator = 1 ;
sensor - > frame_interval . denominator = ov5640_framerates [ OV5640_30_FPS ] ;
sensor - > current_fr = OV5640_30_FPS ;
sensor - > current_mode =
& ov5640_mode_data [ OV5640_30_FPS ] [ OV5640_MODE_VGA_640_480 ] ;
2018-09-11 16:48:21 +03:00
sensor - > last_mode = sensor - > current_mode ;
2017-06-07 21:33:56 +03:00
sensor - > ae_target = 52 ;
2018-06-18 13:29:19 +03:00
/* optional indication of physical rotation of sensor */
ret = fwnode_property_read_u32 ( dev_fwnode ( & client - > dev ) , " rotation " ,
& rotation ) ;
if ( ! ret ) {
switch ( rotation ) {
case 180 :
sensor - > upside_down = true ;
/* fall through */
case 0 :
break ;
default :
dev_warn ( dev , " %u degrees rotation is not supported, ignoring... \n " ,
rotation ) ;
}
}
2018-04-24 13:25:47 +03:00
endpoint = fwnode_graph_get_next_endpoint ( dev_fwnode ( & client - > dev ) ,
NULL ) ;
2017-06-07 21:33:56 +03:00
if ( ! endpoint ) {
dev_err ( dev , " endpoint node not found \n " ) ;
return - EINVAL ;
}
ret = v4l2_fwnode_endpoint_parse ( endpoint , & sensor - > ep ) ;
fwnode_handle_put ( endpoint ) ;
if ( ret ) {
dev_err ( dev , " Could not parse endpoint \n " ) ;
return ret ;
}
/* get system clock (xclk) */
sensor - > xclk = devm_clk_get ( dev , " xclk " ) ;
if ( IS_ERR ( sensor - > xclk ) ) {
dev_err ( dev , " failed to get xclk \n " ) ;
return PTR_ERR ( sensor - > xclk ) ;
}
sensor - > xclk_freq = clk_get_rate ( sensor - > xclk ) ;
if ( sensor - > xclk_freq < OV5640_XCLK_MIN | |
sensor - > xclk_freq > OV5640_XCLK_MAX ) {
dev_err ( dev , " xclk frequency out of range: %d Hz \n " ,
sensor - > xclk_freq ) ;
return - EINVAL ;
}
/* request optional power down pin */
sensor - > pwdn_gpio = devm_gpiod_get_optional ( dev , " powerdown " ,
GPIOD_OUT_HIGH ) ;
/* request optional reset pin */
sensor - > reset_gpio = devm_gpiod_get_optional ( dev , " reset " ,
GPIOD_OUT_HIGH ) ;
v4l2_i2c_subdev_init ( & sensor - > sd , client , & ov5640_subdev_ops ) ;
2018-11-12 19:00:52 +03:00
sensor - > sd . flags | = V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_HAS_EVENTS ;
2017-06-07 21:33:56 +03:00
sensor - > pad . flags = MEDIA_PAD_FL_SOURCE ;
sensor - > sd . entity . function = MEDIA_ENT_F_CAM_SENSOR ;
ret = media_entity_pads_init ( & sensor - > sd . entity , 1 , & sensor - > pad ) ;
if ( ret )
return ret ;
ret = ov5640_get_regulators ( sensor ) ;
if ( ret )
return ret ;
mutex_init ( & sensor - > lock ) ;
2018-01-03 12:57:29 +03:00
ret = ov5640_check_chip_id ( sensor ) ;
if ( ret )
goto entity_cleanup ;
2017-06-07 21:33:56 +03:00
ret = ov5640_init_controls ( sensor ) ;
if ( ret )
goto entity_cleanup ;
ret = v4l2_async_register_subdev ( & sensor - > sd ) ;
if ( ret )
goto free_ctrls ;
return 0 ;
free_ctrls :
v4l2_ctrl_handler_free ( & sensor - > ctrls . handler ) ;
entity_cleanup :
mutex_destroy ( & sensor - > lock ) ;
media_entity_cleanup ( & sensor - > sd . entity ) ;
return ret ;
}
static int ov5640_remove ( struct i2c_client * client )
{
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct ov5640_dev * sensor = to_ov5640_dev ( sd ) ;
v4l2_async_unregister_subdev ( & sensor - > sd ) ;
mutex_destroy ( & sensor - > lock ) ;
media_entity_cleanup ( & sensor - > sd . entity ) ;
v4l2_ctrl_handler_free ( & sensor - > ctrls . handler ) ;
return 0 ;
}
static const struct i2c_device_id ov5640_id [ ] = {
{ " ov5640 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , ov5640_id ) ;
static const struct of_device_id ov5640_dt_ids [ ] = {
{ . compatible = " ovti,ov5640 " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , ov5640_dt_ids ) ;
static struct i2c_driver ov5640_i2c_driver = {
. driver = {
. name = " ov5640 " ,
. of_match_table = ov5640_dt_ids ,
} ,
. id_table = ov5640_id ,
. probe = ov5640_probe ,
. remove = ov5640_remove ,
} ;
module_i2c_driver ( ov5640_i2c_driver ) ;
MODULE_DESCRIPTION ( " OV5640 MIPI Camera Subdev Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;