2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-10-11 20:33:57 +04:00
/*
* Driver for SiliconFile SR030PC30 VGA ( 1 / 10 - Inch ) Image Sensor with ISP
*
* Copyright ( C ) 2010 Samsung Electronics Co . , Ltd
* Author : Sylwester Nawrocki , s . nawrocki @ samsung . com
*
* Based on original driver authored by Dongsoo Nathaniel Kim
* and HeungJun Kim < riverful . kim @ samsung . com > .
*
* Based on mt9v011 Micron Digital Image Sensor driver
2014-02-07 14:03:07 +04:00
* Copyright ( c ) 2009 Mauro Carvalho Chehab
2010-10-11 20:33:57 +04:00
*/
# include <linux/i2c.h>
# include <linux/delay.h>
2010-10-18 00:14:15 +04:00
# include <linux/slab.h>
2011-07-03 22:03:12 +04:00
# include <linux/module.h>
2010-10-11 20:33:57 +04:00
# include <media/v4l2-device.h>
# include <media/v4l2-subdev.h>
# include <media/v4l2-mediabus.h>
2013-05-31 13:17:42 +04:00
# include <media/v4l2-ctrls.h>
2015-11-10 17:01:44 +03:00
# include <media/i2c/sr030pc30.h>
2010-10-11 20:33:57 +04:00
static int debug ;
module_param ( debug , int , 0644 ) ;
# define MODULE_NAME "SR030PC30"
/*
* Register offsets within a page
* b15 . . b8 - page id , b7 . . b0 - register address
*/
# define POWER_CTRL_REG 0x0001
# define PAGEMODE_REG 0x03
# define DEVICE_ID_REG 0x0004
# define NOON010PC30_ID 0x86
# define SR030PC30_ID 0x8C
# define VDO_CTL1_REG 0x0010
# define SUBSAMPL_NONE_VGA 0
# define SUBSAMPL_QVGA 0x10
# define SUBSAMPL_QQVGA 0x20
# define VDO_CTL2_REG 0x0011
# define SYNC_CTL_REG 0x0012
# define WIN_ROWH_REG 0x0020
# define WIN_ROWL_REG 0x0021
# define WIN_COLH_REG 0x0022
# define WIN_COLL_REG 0x0023
# define WIN_HEIGHTH_REG 0x0024
# define WIN_HEIGHTL_REG 0x0025
# define WIN_WIDTHH_REG 0x0026
# define WIN_WIDTHL_REG 0x0027
# define HBLANKH_REG 0x0040
# define HBLANKL_REG 0x0041
# define VSYNCH_REG 0x0042
# define VSYNCL_REG 0x0043
/* page 10 */
# define ISP_CTL_REG(n) (0x1010 + (n))
# define YOFS_REG 0x1040
# define DARK_YOFS_REG 0x1041
# define AG_ABRTH_REG 0x1050
# define SAT_CTL_REG 0x1060
# define BSAT_REG 0x1061
# define RSAT_REG 0x1062
# define AG_SAT_TH_REG 0x1063
/* page 11 */
# define ZLPF_CTRL_REG 0x1110
# define ZLPF_CTRL2_REG 0x1112
# define ZLPF_AGH_THR_REG 0x1121
# define ZLPF_THR_REG 0x1160
# define ZLPF_DYN_THR_REG 0x1160
/* page 12 */
# define YCLPF_CTL1_REG 0x1240
# define YCLPF_CTL2_REG 0x1241
# define YCLPF_THR_REG 0x1250
# define BLPF_CTL_REG 0x1270
# define BLPF_THR1_REG 0x1274
# define BLPF_THR2_REG 0x1275
/* page 14 - Lens Shading Compensation */
# define LENS_CTRL_REG 0x1410
# define LENS_XCEN_REG 0x1420
# define LENS_YCEN_REG 0x1421
# define LENS_R_COMP_REG 0x1422
# define LENS_G_COMP_REG 0x1423
# define LENS_B_COMP_REG 0x1424
/* page 15 - Color correction */
# define CMC_CTL_REG 0x1510
# define CMC_OFSGH_REG 0x1514
# define CMC_OFSGL_REG 0x1516
# define CMC_SIGN_REG 0x1517
/* Color correction coefficients */
# define CMC_COEF_REG(n) (0x1530 + (n))
/* Color correction offset coefficients */
# define CMC_OFS_REG(n) (0x1540 + (n))
/* page 16 - Gamma correction */
# define GMA_CTL_REG 0x1610
/* Gamma correction coefficients 0.14 */
# define GMA_COEF_REG(n) (0x1630 + (n))
/* page 20 - Auto Exposure */
# define AE_CTL1_REG 0x2010
# define AE_CTL2_REG 0x2011
# define AE_FRM_CTL_REG 0x2020
# define AE_FINE_CTL_REG(n) (0x2028 + (n))
# define EXP_TIMEH_REG 0x2083
# define EXP_TIMEM_REG 0x2084
# define EXP_TIMEL_REG 0x2085
# define EXP_MMINH_REG 0x2086
# define EXP_MMINL_REG 0x2087
# define EXP_MMAXH_REG 0x2088
# define EXP_MMAXM_REG 0x2089
# define EXP_MMAXL_REG 0x208A
/* page 22 - Auto White Balance */
# define AWB_CTL1_REG 0x2210
# define AWB_ENABLE 0x80
# define AWB_CTL2_REG 0x2211
# define MWB_ENABLE 0x01
/* RGB gain control (manual WB) when AWB_CTL1[7]=0 */
# define AWB_RGAIN_REG 0x2280
# define AWB_GGAIN_REG 0x2281
# define AWB_BGAIN_REG 0x2282
# define AWB_RMAX_REG 0x2283
# define AWB_RMIN_REG 0x2284
# define AWB_BMAX_REG 0x2285
# define AWB_BMIN_REG 0x2286
/* R, B gain range in bright light conditions */
# define AWB_RMAXB_REG 0x2287
# define AWB_RMINB_REG 0x2288
# define AWB_BMAXB_REG 0x2289
# define AWB_BMINB_REG 0x228A
/* manual white balance, when AWB_CTL2[0]=1 */
# define MWB_RGAIN_REG 0x22B2
# define MWB_BGAIN_REG 0x22B3
/* the token to mark an array end */
# define REG_TERM 0xFFFF
/* Minimum and maximum exposure time in ms */
# define EXPOS_MIN_MS 1
# define EXPOS_MAX_MS 125
struct sr030pc30_info {
struct v4l2_subdev sd ;
2013-05-31 13:17:42 +04:00
struct v4l2_ctrl_handler hdl ;
2010-10-11 20:33:57 +04:00
const struct sr030pc30_platform_data * pdata ;
const struct sr030pc30_format * curr_fmt ;
const struct sr030pc30_frmsize * curr_win ;
unsigned int hflip : 1 ;
unsigned int vflip : 1 ;
unsigned int sleep : 1 ;
2013-05-31 13:17:42 +04:00
struct {
/* auto whitebalance control cluster */
struct v4l2_ctrl * awb ;
struct v4l2_ctrl * red ;
struct v4l2_ctrl * blue ;
} ;
struct {
/* auto exposure control cluster */
struct v4l2_ctrl * autoexp ;
struct v4l2_ctrl * exp ;
} ;
2010-10-11 20:33:57 +04:00
u8 i2c_reg_page ;
} ;
struct sr030pc30_format {
2014-11-10 20:28:29 +03:00
u32 code ;
2010-10-11 20:33:57 +04:00
enum v4l2_colorspace colorspace ;
u16 ispctl1_reg ;
} ;
struct sr030pc30_frmsize {
u16 width ;
u16 height ;
int vid_ctl1 ;
} ;
struct i2c_regval {
u16 addr ;
u16 val ;
} ;
/* supported resolutions */
static const struct sr030pc30_frmsize sr030pc30_sizes [ ] = {
{
. width = 640 ,
. height = 480 ,
. vid_ctl1 = SUBSAMPL_NONE_VGA ,
} , {
. width = 320 ,
. height = 240 ,
. vid_ctl1 = SUBSAMPL_QVGA ,
} , {
. width = 160 ,
. height = 120 ,
. vid_ctl1 = SUBSAMPL_QQVGA ,
} ,
} ;
/* supported pixel formats */
static const struct sr030pc30_format sr030pc30_formats [ ] = {
{
2014-11-10 20:28:29 +03:00
. code = MEDIA_BUS_FMT_YUYV8_2X8 ,
2010-10-11 20:33:57 +04:00
. colorspace = V4L2_COLORSPACE_JPEG ,
. ispctl1_reg = 0x03 ,
} , {
2014-11-10 20:28:29 +03:00
. code = MEDIA_BUS_FMT_YVYU8_2X8 ,
2010-10-11 20:33:57 +04:00
. colorspace = V4L2_COLORSPACE_JPEG ,
. ispctl1_reg = 0x02 ,
} , {
2014-11-10 20:28:29 +03:00
. code = MEDIA_BUS_FMT_VYUY8_2X8 ,
2010-10-11 20:33:57 +04:00
. colorspace = V4L2_COLORSPACE_JPEG ,
. ispctl1_reg = 0 ,
} , {
2014-11-10 20:28:29 +03:00
. code = MEDIA_BUS_FMT_UYVY8_2X8 ,
2010-10-11 20:33:57 +04:00
. colorspace = V4L2_COLORSPACE_JPEG ,
. ispctl1_reg = 0x01 ,
} , {
2014-11-10 20:28:29 +03:00
. code = MEDIA_BUS_FMT_RGB565_2X8_BE ,
2010-10-11 20:33:57 +04:00
. colorspace = V4L2_COLORSPACE_JPEG ,
. ispctl1_reg = 0x40 ,
} ,
} ;
static const struct i2c_regval sr030pc30_base_regs [ ] = {
/* Window size and position within pixel matrix */
{ WIN_ROWH_REG , 0x00 } , { WIN_ROWL_REG , 0x06 } ,
{ WIN_COLH_REG , 0x00 } , { WIN_COLL_REG , 0x06 } ,
{ WIN_HEIGHTH_REG , 0x01 } , { WIN_HEIGHTL_REG , 0xE0 } ,
{ WIN_WIDTHH_REG , 0x02 } , { WIN_WIDTHL_REG , 0x80 } ,
{ HBLANKH_REG , 0x01 } , { HBLANKL_REG , 0x50 } ,
{ VSYNCH_REG , 0x00 } , { VSYNCL_REG , 0x14 } ,
{ SYNC_CTL_REG , 0 } ,
/* Color corection and saturation */
{ ISP_CTL_REG ( 0 ) , 0x30 } , { YOFS_REG , 0x80 } ,
{ DARK_YOFS_REG , 0x04 } , { AG_ABRTH_REG , 0x78 } ,
{ SAT_CTL_REG , 0x1F } , { BSAT_REG , 0x90 } ,
{ AG_SAT_TH_REG , 0xF0 } , { 0x1064 , 0x80 } ,
{ CMC_CTL_REG , 0x03 } , { CMC_OFSGH_REG , 0x3C } ,
{ CMC_OFSGL_REG , 0x2C } , { CMC_SIGN_REG , 0x2F } ,
{ CMC_COEF_REG ( 0 ) , 0xCB } , { CMC_OFS_REG ( 0 ) , 0x87 } ,
{ CMC_COEF_REG ( 1 ) , 0x61 } , { CMC_OFS_REG ( 1 ) , 0x18 } ,
{ CMC_COEF_REG ( 2 ) , 0x16 } , { CMC_OFS_REG ( 2 ) , 0x91 } ,
{ CMC_COEF_REG ( 3 ) , 0x23 } , { CMC_OFS_REG ( 3 ) , 0x94 } ,
{ CMC_COEF_REG ( 4 ) , 0xCE } , { CMC_OFS_REG ( 4 ) , 0x9f } ,
{ CMC_COEF_REG ( 5 ) , 0x2B } , { CMC_OFS_REG ( 5 ) , 0x33 } ,
{ CMC_COEF_REG ( 6 ) , 0x01 } , { CMC_OFS_REG ( 6 ) , 0x00 } ,
{ CMC_COEF_REG ( 7 ) , 0x34 } , { CMC_OFS_REG ( 7 ) , 0x94 } ,
{ CMC_COEF_REG ( 8 ) , 0x75 } , { CMC_OFS_REG ( 8 ) , 0x14 } ,
/* Color corection coefficients */
{ GMA_CTL_REG , 0x03 } , { GMA_COEF_REG ( 0 ) , 0x00 } ,
{ GMA_COEF_REG ( 1 ) , 0x19 } , { GMA_COEF_REG ( 2 ) , 0x26 } ,
{ GMA_COEF_REG ( 3 ) , 0x3B } , { GMA_COEF_REG ( 4 ) , 0x5D } ,
{ GMA_COEF_REG ( 5 ) , 0x79 } , { GMA_COEF_REG ( 6 ) , 0x8E } ,
{ GMA_COEF_REG ( 7 ) , 0x9F } , { GMA_COEF_REG ( 8 ) , 0xAF } ,
{ GMA_COEF_REG ( 9 ) , 0xBD } , { GMA_COEF_REG ( 10 ) , 0xCA } ,
{ GMA_COEF_REG ( 11 ) , 0xDD } , { GMA_COEF_REG ( 12 ) , 0xEC } ,
{ GMA_COEF_REG ( 13 ) , 0xF7 } , { GMA_COEF_REG ( 14 ) , 0xFF } ,
/* Noise reduction, Z-LPF, YC-LPF and BLPF filters setup */
{ ZLPF_CTRL_REG , 0x99 } , { ZLPF_CTRL2_REG , 0x0E } ,
{ ZLPF_AGH_THR_REG , 0x29 } , { ZLPF_THR_REG , 0x0F } ,
{ ZLPF_DYN_THR_REG , 0x63 } , { YCLPF_CTL1_REG , 0x23 } ,
{ YCLPF_CTL2_REG , 0x3B } , { YCLPF_THR_REG , 0x05 } ,
{ BLPF_CTL_REG , 0x1D } , { BLPF_THR1_REG , 0x05 } ,
{ BLPF_THR2_REG , 0x04 } ,
/* Automatic white balance */
{ AWB_CTL1_REG , 0xFB } , { AWB_CTL2_REG , 0x26 } ,
{ AWB_RMAX_REG , 0x54 } , { AWB_RMIN_REG , 0x2B } ,
{ AWB_BMAX_REG , 0x57 } , { AWB_BMIN_REG , 0x29 } ,
{ AWB_RMAXB_REG , 0x50 } , { AWB_RMINB_REG , 0x43 } ,
{ AWB_BMAXB_REG , 0x30 } , { AWB_BMINB_REG , 0x22 } ,
/* Auto exposure */
{ AE_CTL1_REG , 0x8C } , { AE_CTL2_REG , 0x04 } ,
{ AE_FRM_CTL_REG , 0x01 } , { AE_FINE_CTL_REG ( 0 ) , 0x3F } ,
{ AE_FINE_CTL_REG ( 1 ) , 0xA3 } , { AE_FINE_CTL_REG ( 3 ) , 0x34 } ,
/* Lens shading compensation */
{ LENS_CTRL_REG , 0x01 } , { LENS_XCEN_REG , 0x80 } ,
{ LENS_YCEN_REG , 0x70 } , { LENS_R_COMP_REG , 0x53 } ,
{ LENS_G_COMP_REG , 0x40 } , { LENS_B_COMP_REG , 0x3e } ,
{ REG_TERM , 0 } ,
} ;
static inline struct sr030pc30_info * to_sr030pc30 ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct sr030pc30_info , sd ) ;
}
static inline int set_i2c_page ( struct sr030pc30_info * info ,
struct i2c_client * client , unsigned int reg )
{
2010-10-18 13:51:35 +04:00
int ret = 0 ;
2010-10-11 20:33:57 +04:00
u32 page = reg > > 8 & 0xFF ;
if ( info - > i2c_reg_page ! = page & & ( reg & 0xFF ) ! = 0x03 ) {
ret = i2c_smbus_write_byte_data ( client , PAGEMODE_REG , page ) ;
if ( ! ret )
info - > i2c_reg_page = page ;
}
return ret ;
}
static int cam_i2c_read ( struct v4l2_subdev * sd , u32 reg_addr )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct sr030pc30_info * info = to_sr030pc30 ( sd ) ;
int ret = set_i2c_page ( info , client , reg_addr ) ;
if ( ! ret )
ret = i2c_smbus_read_byte_data ( client , reg_addr & 0xFF ) ;
return ret ;
}
static int cam_i2c_write ( struct v4l2_subdev * sd , u32 reg_addr , u32 val )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct sr030pc30_info * info = to_sr030pc30 ( sd ) ;
int ret = set_i2c_page ( info , client , reg_addr ) ;
if ( ! ret )
ret = i2c_smbus_write_byte_data (
client , reg_addr & 0xFF , val ) ;
return ret ;
}
static inline int sr030pc30_bulk_write_reg ( struct v4l2_subdev * sd ,
const struct i2c_regval * msg )
{
while ( msg - > addr ! = REG_TERM ) {
int ret = cam_i2c_write ( sd , msg - > addr , msg - > val ) ;
if ( ret )
return ret ;
msg + + ;
}
return 0 ;
}
/* Device reset and sleep mode control */
static int sr030pc30_pwr_ctrl ( struct v4l2_subdev * sd ,
bool reset , bool sleep )
{
struct sr030pc30_info * info = to_sr030pc30 ( sd ) ;
u8 reg = sleep ? 0xF1 : 0xF0 ;
int ret = 0 ;
if ( reset )
ret = cam_i2c_write ( sd , POWER_CTRL_REG , reg | 0x02 ) ;
if ( ! ret ) {
ret = cam_i2c_write ( sd , POWER_CTRL_REG , reg ) ;
if ( ! ret ) {
info - > sleep = sleep ;
if ( reset )
info - > i2c_reg_page = - 1 ;
}
}
return ret ;
}
static int sr030pc30_set_flip ( struct v4l2_subdev * sd )
{
struct sr030pc30_info * info = to_sr030pc30 ( sd ) ;
s32 reg = cam_i2c_read ( sd , VDO_CTL2_REG ) ;
if ( reg < 0 )
return reg ;
reg & = 0x7C ;
if ( info - > hflip )
reg | = 0x01 ;
if ( info - > vflip )
reg | = 0x02 ;
return cam_i2c_write ( sd , VDO_CTL2_REG , reg | 0x80 ) ;
}
/* Configure resolution, color format and image flip */
static int sr030pc30_set_params ( struct v4l2_subdev * sd )
{
struct sr030pc30_info * info = to_sr030pc30 ( sd ) ;
int ret ;
if ( ! info - > curr_win )
return - EINVAL ;
/* Configure the resolution through subsampling */
ret = cam_i2c_write ( sd , VDO_CTL1_REG ,
info - > curr_win - > vid_ctl1 ) ;
if ( ! ret & & info - > curr_fmt )
ret = cam_i2c_write ( sd , ISP_CTL_REG ( 0 ) ,
info - > curr_fmt - > ispctl1_reg ) ;
if ( ! ret )
ret = sr030pc30_set_flip ( sd ) ;
return ret ;
}
/* Find nearest matching image pixel size. */
static int sr030pc30_try_frame_size ( struct v4l2_mbus_framefmt * mf )
{
unsigned int min_err = ~ 0 ;
int i = ARRAY_SIZE ( sr030pc30_sizes ) ;
const struct sr030pc30_frmsize * fsize = & sr030pc30_sizes [ 0 ] ,
* match = NULL ;
while ( i - - ) {
int err = abs ( fsize - > width - mf - > width )
+ abs ( fsize - > height - mf - > height ) ;
if ( err < min_err ) {
min_err = err ;
match = fsize ;
}
fsize + + ;
}
if ( match ) {
mf - > width = match - > width ;
mf - > height = match - > height ;
return 0 ;
}
return - EINVAL ;
}
2013-05-31 13:17:42 +04:00
static int sr030pc30_s_ctrl ( struct v4l2_ctrl * ctrl )
2010-10-11 20:33:57 +04:00
{
2013-05-31 13:17:42 +04:00
struct sr030pc30_info * info =
container_of ( ctrl - > handler , struct sr030pc30_info , hdl ) ;
struct v4l2_subdev * sd = & info - > sd ;
int ret = 0 ;
2010-10-11 20:33:57 +04:00
v4l2_dbg ( 1 , debug , sd , " %s: ctrl_id: %d, value: %d \n " ,
2013-05-31 13:17:42 +04:00
__func__ , ctrl - > id , ctrl - > val ) ;
2010-10-11 20:33:57 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUTO_WHITE_BALANCE :
2013-05-31 13:17:42 +04:00
if ( ctrl - > is_new ) {
ret = cam_i2c_write ( sd , AWB_CTL2_REG ,
ctrl - > val ? 0x2E : 0x2F ) ;
if ( ! ret )
ret = cam_i2c_write ( sd , AWB_CTL1_REG ,
ctrl - > val ? 0xFB : 0x7B ) ;
}
if ( ! ret & & info - > blue - > is_new )
ret = cam_i2c_write ( sd , MWB_BGAIN_REG , info - > blue - > val ) ;
if ( ! ret & & info - > red - > is_new )
ret = cam_i2c_write ( sd , MWB_RGAIN_REG , info - > red - > val ) ;
return ret ;
2010-10-11 20:33:57 +04:00
case V4L2_CID_EXPOSURE_AUTO :
2013-05-31 13:17:42 +04:00
/* auto anti-flicker is also enabled here */
if ( ctrl - > is_new )
ret = cam_i2c_write ( sd , AE_CTL1_REG ,
ctrl - > val = = V4L2_EXPOSURE_AUTO ? 0xDC : 0x0C ) ;
if ( info - > exp - > is_new ) {
unsigned long expos = info - > exp - > val ;
expos = expos * info - > pdata - > clk_rate / ( 8 * 1000 ) ;
if ( ! ret )
ret = cam_i2c_write ( sd , EXP_TIMEH_REG ,
expos > > 16 & 0xFF ) ;
if ( ! ret )
ret = cam_i2c_write ( sd , EXP_TIMEM_REG ,
expos > > 8 & 0xFF ) ;
if ( ! ret )
ret = cam_i2c_write ( sd , EXP_TIMEL_REG ,
expos & 0xFF ) ;
}
return ret ;
2010-10-11 20:33:57 +04:00
default :
return - EINVAL ;
}
2013-05-31 13:17:42 +04:00
2010-10-11 20:33:57 +04:00
return 0 ;
}
2015-04-09 10:01:33 +03:00
static int sr030pc30_enum_mbus_code ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_mbus_code_enum * code )
2010-10-11 20:33:57 +04:00
{
2015-04-09 10:01:33 +03:00
if ( ! code | | code - > pad | |
code - > index > = ARRAY_SIZE ( sr030pc30_formats ) )
2010-10-11 20:33:57 +04:00
return - EINVAL ;
2015-04-09 10:01:33 +03:00
code - > code = sr030pc30_formats [ code - > index ] . code ;
2010-10-11 20:33:57 +04:00
return 0 ;
}
2015-04-09 10:02:34 +03:00
static int sr030pc30_get_fmt ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_format * format )
2010-10-11 20:33:57 +04:00
{
2015-04-09 10:02:34 +03:00
struct v4l2_mbus_framefmt * mf ;
2010-10-11 20:33:57 +04:00
struct sr030pc30_info * info = to_sr030pc30 ( sd ) ;
2015-04-09 10:02:34 +03:00
if ( ! format | | format - > pad )
2010-10-11 20:33:57 +04:00
return - EINVAL ;
2015-04-09 10:02:34 +03:00
mf = & format - > format ;
2015-08-11 18:18:33 +03:00
if ( ! info - > curr_win | | ! info - > curr_fmt )
return - EINVAL ;
2010-10-11 20:33:57 +04:00
mf - > width = info - > curr_win - > width ;
mf - > height = info - > curr_win - > height ;
mf - > code = info - > curr_fmt - > code ;
mf - > colorspace = info - > curr_fmt - > colorspace ;
mf - > field = V4L2_FIELD_NONE ;
return 0 ;
}
/* Return nearest media bus frame format. */
static const struct sr030pc30_format * try_fmt ( struct v4l2_subdev * sd ,
struct v4l2_mbus_framefmt * mf )
{
2018-01-25 17:15:25 +03:00
int i ;
2010-10-11 20:33:57 +04:00
sr030pc30_try_frame_size ( mf ) ;
2018-01-25 17:15:25 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( sr030pc30_formats ) ; i + + ) {
2010-10-11 20:33:57 +04:00
if ( mf - > code = = sr030pc30_formats [ i ] . code )
break ;
2018-01-25 17:15:25 +03:00
}
if ( i = = ARRAY_SIZE ( sr030pc30_formats ) )
i = 0 ;
2010-10-11 20:33:57 +04:00
mf - > code = sr030pc30_formats [ i ] . code ;
return & sr030pc30_formats [ i ] ;
}
/* Return nearest media bus frame format. */
2015-04-09 12:24:36 +03:00
static int sr030pc30_set_fmt ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_format * format )
2010-10-11 20:33:57 +04:00
{
2015-04-09 12:24:36 +03:00
struct sr030pc30_info * info = sd ? to_sr030pc30 ( sd ) : NULL ;
const struct sr030pc30_format * fmt ;
struct v4l2_mbus_framefmt * mf ;
2010-10-11 20:33:57 +04:00
2015-04-09 12:24:36 +03:00
if ( ! sd | | ! format )
return - EINVAL ;
2010-10-11 20:33:57 +04:00
2015-04-09 12:24:36 +03:00
mf = & format - > format ;
if ( format - > pad )
2010-10-11 20:33:57 +04:00
return - EINVAL ;
2015-04-09 12:24:36 +03:00
fmt = try_fmt ( sd , mf ) ;
if ( format - > which = = V4L2_SUBDEV_FORMAT_TRY ) {
cfg - > try_fmt = * mf ;
return 0 ;
}
info - > curr_fmt = fmt ;
2010-10-11 20:33:57 +04:00
return sr030pc30_set_params ( sd ) ;
}
static int sr030pc30_base_config ( struct v4l2_subdev * sd )
{
struct sr030pc30_info * info = to_sr030pc30 ( sd ) ;
int ret ;
unsigned long expmin , expmax ;
ret = sr030pc30_bulk_write_reg ( sd , sr030pc30_base_regs ) ;
if ( ! ret ) {
info - > curr_fmt = & sr030pc30_formats [ 0 ] ;
info - > curr_win = & sr030pc30_sizes [ 0 ] ;
ret = sr030pc30_set_params ( sd ) ;
}
if ( ! ret )
ret = sr030pc30_pwr_ctrl ( sd , false , false ) ;
2018-08-31 16:20:18 +03:00
if ( ret )
2010-10-11 20:33:57 +04:00
return ret ;
expmin = EXPOS_MIN_MS * info - > pdata - > clk_rate / ( 8 * 1000 ) ;
expmax = EXPOS_MAX_MS * info - > pdata - > clk_rate / ( 8 * 1000 ) ;
v4l2_dbg ( 1 , debug , sd , " %s: expmin= %lx, expmax= %lx " , __func__ ,
expmin , expmax ) ;
/* Setting up manual exposure time range */
ret = cam_i2c_write ( sd , EXP_MMINH_REG , expmin > > 8 & 0xFF ) ;
if ( ! ret )
ret = cam_i2c_write ( sd , EXP_MMINL_REG , expmin & 0xFF ) ;
if ( ! ret )
ret = cam_i2c_write ( sd , EXP_MMAXH_REG , expmax > > 16 & 0xFF ) ;
if ( ! ret )
ret = cam_i2c_write ( sd , EXP_MMAXM_REG , expmax > > 8 & 0xFF ) ;
if ( ! ret )
ret = cam_i2c_write ( sd , EXP_MMAXL_REG , expmax & 0xFF ) ;
return ret ;
}
static int sr030pc30_s_power ( struct v4l2_subdev * sd , int on )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct sr030pc30_info * info = to_sr030pc30 ( sd ) ;
const struct sr030pc30_platform_data * pdata = info - > pdata ;
int ret ;
2011-05-26 11:49:52 +04:00
if ( pdata = = NULL ) {
WARN ( 1 , " No platform data! \n " ) ;
return - EINVAL ;
}
2010-10-11 20:33:57 +04:00
/*
* Put sensor into power sleep mode before switching off
* power and disabling MCLK .
*/
if ( ! on )
sr030pc30_pwr_ctrl ( sd , false , true ) ;
/* set_power controls sensor's power and clock */
if ( pdata - > set_power ) {
ret = pdata - > set_power ( & client - > dev , on ) ;
if ( ret )
return ret ;
}
if ( on ) {
ret = sr030pc30_base_config ( sd ) ;
} else {
2011-05-26 11:49:52 +04:00
ret = 0 ;
2010-10-11 20:33:57 +04:00
info - > curr_win = NULL ;
info - > curr_fmt = NULL ;
}
return ret ;
}
2013-05-31 13:17:42 +04:00
static const struct v4l2_ctrl_ops sr030pc30_ctrl_ops = {
. s_ctrl = sr030pc30_s_ctrl ,
} ;
2010-10-11 20:33:57 +04:00
static const struct v4l2_subdev_core_ops sr030pc30_core_ops = {
. s_power = sr030pc30_s_power ,
} ;
2015-04-09 10:01:33 +03:00
static const struct v4l2_subdev_pad_ops sr030pc30_pad_ops = {
. enum_mbus_code = sr030pc30_enum_mbus_code ,
2015-04-09 10:02:34 +03:00
. get_fmt = sr030pc30_get_fmt ,
2015-04-09 12:24:36 +03:00
. set_fmt = sr030pc30_set_fmt ,
2010-10-11 20:33:57 +04:00
} ;
static const struct v4l2_subdev_ops sr030pc30_ops = {
. core = & sr030pc30_core_ops ,
2015-04-09 10:01:33 +03:00
. pad = & sr030pc30_pad_ops ,
2010-10-11 20:33:57 +04:00
} ;
/*
* Detect sensor type . Return 0 if SR030PC30 was detected
* or - ENODEV otherwise .
*/
static int sr030pc30_detect ( struct i2c_client * client )
{
const struct sr030pc30_platform_data * pdata
= client - > dev . platform_data ;
int ret ;
/* Enable sensor's power and clock */
if ( pdata - > set_power ) {
ret = pdata - > set_power ( & client - > dev , 1 ) ;
if ( ret )
return ret ;
}
ret = i2c_smbus_read_byte_data ( client , DEVICE_ID_REG ) ;
if ( pdata - > set_power )
pdata - > set_power ( & client - > dev , 0 ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " %s: I2C read failed \n " , __func__ ) ;
return ret ;
}
return ret = = SR030PC30_ID ? 0 : - ENODEV ;
}
static int sr030pc30_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct sr030pc30_info * info ;
struct v4l2_subdev * sd ;
2013-05-31 13:17:42 +04:00
struct v4l2_ctrl_handler * hdl ;
2010-10-11 20:33:57 +04:00
const struct sr030pc30_platform_data * pdata
= client - > dev . platform_data ;
int ret ;
if ( ! pdata ) {
dev_err ( & client - > dev , " No platform data! " ) ;
return - EIO ;
}
ret = sr030pc30_detect ( client ) ;
if ( ret )
return ret ;
2013-05-02 15:29:43 +04:00
info = devm_kzalloc ( & client - > dev , sizeof ( * info ) , GFP_KERNEL ) ;
2010-10-11 20:33:57 +04:00
if ( ! info )
return - ENOMEM ;
sd = & info - > sd ;
info - > pdata = client - > dev . platform_data ;
v4l2_i2c_subdev_init ( sd , client , & sr030pc30_ops ) ;
2013-05-31 13:17:42 +04:00
hdl = & info - > hdl ;
v4l2_ctrl_handler_init ( hdl , 6 ) ;
info - > awb = v4l2_ctrl_new_std ( hdl , & sr030pc30_ctrl_ops ,
V4L2_CID_AUTO_WHITE_BALANCE , 0 , 1 , 1 , 1 ) ;
info - > red = v4l2_ctrl_new_std ( hdl , & sr030pc30_ctrl_ops ,
V4L2_CID_RED_BALANCE , 0 , 127 , 1 , 64 ) ;
info - > blue = v4l2_ctrl_new_std ( hdl , & sr030pc30_ctrl_ops ,
V4L2_CID_BLUE_BALANCE , 0 , 127 , 1 , 64 ) ;
info - > autoexp = v4l2_ctrl_new_std ( hdl , & sr030pc30_ctrl_ops ,
V4L2_CID_EXPOSURE_AUTO , 0 , 1 , 1 , 1 ) ;
info - > exp = v4l2_ctrl_new_std ( hdl , & sr030pc30_ctrl_ops ,
V4L2_CID_EXPOSURE , EXPOS_MIN_MS , EXPOS_MAX_MS , 1 , 30 ) ;
sd - > ctrl_handler = hdl ;
if ( hdl - > error ) {
int err = hdl - > error ;
v4l2_ctrl_handler_free ( hdl ) ;
return err ;
}
v4l2_ctrl_auto_cluster ( 3 , & info - > awb , 0 , false ) ;
v4l2_ctrl_auto_cluster ( 2 , & info - > autoexp , V4L2_EXPOSURE_MANUAL , false ) ;
v4l2_ctrl_handler_setup ( hdl ) ;
2010-10-11 20:33:57 +04:00
info - > i2c_reg_page = - 1 ;
info - > hflip = 1 ;
return 0 ;
}
static int sr030pc30_remove ( struct i2c_client * client )
{
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
v4l2_device_unregister_subdev ( sd ) ;
2013-05-31 13:17:42 +04:00
v4l2_ctrl_handler_free ( sd - > ctrl_handler ) ;
2010-10-11 20:33:57 +04:00
return 0 ;
}
static const struct i2c_device_id sr030pc30_id [ ] = {
{ MODULE_NAME , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , sr030pc30_id ) ;
static struct i2c_driver sr030pc30_i2c_driver = {
. driver = {
. name = MODULE_NAME
} ,
. probe = sr030pc30_probe ,
. remove = sr030pc30_remove ,
. id_table = sr030pc30_id ,
} ;
2012-02-12 13:56:32 +04:00
module_i2c_driver ( sr030pc30_i2c_driver ) ;
2010-10-11 20:33:57 +04:00
MODULE_DESCRIPTION ( " Siliconfile SR030PC30 camera driver " ) ;
MODULE_AUTHOR ( " Sylwester Nawrocki <s.nawrocki@samsung.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;