blob: 578e2340db5519368b93ad1b5095ef59b14e4ad9 [file] [log] [blame]
/*
* OmniVision OV8825 sensor driver
*
* Copyright (C) 2013 Broadcom Corporation
*
* 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 version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
*kind, whether express or implied; without even the implied warranty
*of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/printk.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
#include <media/soc_camera.h>
#include <linux/videodev2_brcm.h>
#include "ov8825.h"
#ifdef CONFIG_VIDEO_A3907
#include <media/a3907.h>
#endif
#ifdef CONFIG_VIDEO_AS3643
#include "as3643.h"
#endif
#ifdef CONFIG_GPIO_FLASH
#include "flash_gpio.h"
#endif
static int Is_SnapToPrev_expo;
static int Is_SnapToPrev_gain;
#define OV8825_DEBUG 1
/* OV5648 has only one fixed colorspace per pixelcode */
struct ov8825_datafmt {
enum v4l2_mbus_pixelcode code;
enum v4l2_colorspace colorspace;
};
static const struct ov8825_datafmt ov8825_fmts[] = {
/*
Order important: first natively supported,
second supported with a GPIO extender
*/
{V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB},
{V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
{V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_COLORSPACE_SRGB},
{V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_COLORSPACE_SRGB},
};
enum ov8825_mode {
OV8825_MODE_1280x960P30 = 0,
OV8825_MODE_3264x2448P15 = 1,
OV8825_MODE_MAX = 2,
};
enum ov8825_state {
OV8825_STATE_STOP = 0,
OV8825_STATE_STRM = 1,
OV8825_STATE_MAX = 2,
};
enum bayer_order {
BAYER_RGGB = 0,
BAYER_GRBG = 1,
BAYER_GBRG = 2,
BAYER_BGGR = 3,
};
struct sensor_mode {
char name[128];
int height;
int width;
int line_length_ns;
int hts;
int vts;
int vts_max;
enum bayer_order bayer;
int bpp;
int fps;
int binning_factor;
};
struct sensor_mode ov8825_mode[OV8825_MODE_MAX + 1] = {
{
.name = "1280x960_2lane_30Fps",
.height = 960,
.width = 1280,
.hts = 3516,
.vts = 1274,
.vts_max = 32767 - 6,
.line_length_ns = 26458,
.bayer = BAYER_BGGR,
.bpp = 10,
.fps = F24p8(30.0),
.binning_factor = 1,
},
{
.name = "3264x2448_2lane_15Fps",
.height = 2448,
.width = 3264,
.hts = 3584,
.vts = 2480,
.vts_max = 32767 - 6,
.line_length_ns = 26970,
.bayer = BAYER_BGGR,
.bpp = 10,
.fps = F24p8(15.0),
.binning_factor = 2,
},
{
.name = "STOPPED",
}
};
/**
*struct ov8825_otp - ov8825 OTP format
*@module_integrator_id: 32-bit module integrator ID number
*@lens_id: 32-bit lens ID number
*@rg_ratio: 32-bit AWB R/G channel ratio
*@bg_ratio: 32-bit AWB B/G channel ratio
*@user_data: 64-bit OTP user data
*@light_rg: 32-bit light source R/G ratio
*@light_bg: 32-bit light source B/G ratio
*
* Define a structure for OV8825 OTP calibration data
*/
struct ov8825_otp {
u8 flag;
u8 module_integrator_id;
u8 lens_id;
u16 rg_ratio;
u16 bg_ratio;
u8 user_data[2];
u16 light_rg;
u16 light_bg;
};
struct ov8825 {
struct v4l2_subdev subdev;
struct v4l2_subdev_sensor_interface_parms *plat_parms;
struct soc_camera_device *icd;
int state;
int mode_idx;
int i_fmt;
int framerate;
int focus_mode;
int gain_current;
int exposure_current;
int line_length;
int vts;
int vts_max;
int vts_min;
int current_coarse_int;
int framerate_lo;
int framerate_hi;
int framerate_lo_absolute;
int framerate_hi_absolute;
int current_position;
int time_to_destination;
/*
* focus_status = 1 focusing
* focus_status = 0 focus cancelled or not focusing
*/
atomic_t focus_status;
int aecpos_delay;
#define EXP_DELAY_MAX 8
#define GAIN_DELAY_MAX 8
#define LENS_READ_DELAY 4
int exp_read_buf[EXP_DELAY_MAX];
int gain_read_buf[GAIN_DELAY_MAX];
int lens_read_buf[LENS_READ_DELAY];
int lenspos_delay;
int flashmode;
int flash_intensity;
int flash_timeout;
struct ov8825_otp otp;
int calibrated;
#define V4L2_FRAME_INFO_CACHE 4
int frame_info_size;
struct v4l2_frame_info frame_info[V4L2_FRAME_INFO_CACHE];
int powerdown;
int position;
int dac_code;
int timing_step_us;
int flying_time;
int flying_start_time;
int requested_position;
};
/**
*struct ov8825_reg - ov8825 register format
*@reg: 16-bit offset to register
*@val: 8/16/32-bit register value
*@length: length of the register
*
* Define a structure for OV8825 register initialization values
*/
struct ov8825_reg {
u16 reg;
u8 val;
u8 pad;
};
static const struct ov8825_reg ov8825_regtbl[OV8825_MODE_MAX][1024] = {
{
{0x0100, 0x00},
{0x3000, 0x16},
{0x3001, 0x00},
{0x3002, 0x6c},
{0x3003, 0xce},
{0x3004, 0xd4},
{0x3005, 0x00},
{0x3006, 0x10},
{0x3007, 0x3b},
{0x300d, 0x00},
{0x301f, 0x09},
{0x3020, 0x01},
{0x3010, 0x00},
{0x3011, 0x01},
{0x3012, 0x80},
{0x3013, 0x39},
{0x3018, 0x00},
{0x3104, 0x20},
{0x3106, 0x15},
{0x3300, 0x00},
{0x3500, 0x00},
{0x3501, 0x4e},
{0x3502, 0xa0},
{0x3503, 0x07},
{0x3509, 0x10},
{0x350b, 0x1f},
{0x3600, 0x06},
{0x3601, 0x34},
{0x3602, 0x42},
{0x3603, 0x5c},
{0x3604, 0x98},
{0x3605, 0xf5},
{0x3609, 0xb4},
{0x360a, 0x7c},
{0x360b, 0xc9},
{0x360c, 0x0b},
{0x3612, 0x00},
{0x3613, 0x02},
{0x3614, 0x0f},
{0x3615, 0x00},
{0x3616, 0x03},
{0x3617, 0xa1},
{0x3618, 0x0f},
{0x3619, 0x00},
{0x361a, 0x8a},
{0x361b, 0x02},
{0x361c, 0x07},
{0x3700, 0x20},
{0x3701, 0x44},
{0x3702, 0x50},
{0x3703, 0xcc},
{0x3704, 0x19},
{0x3705, 0x32},
{0x3706, 0x4b},
{0x3707, 0x63},
{0x3708, 0x84},
{0x3709, 0x40},
{0x370a, 0x33},
{0x370b, 0x01},
{0x370c, 0x50},
{0x370d, 0x00},
{0x370e, 0x00},
{0x3711, 0x0f},
{0x3712, 0x9c},
{0x3724, 0x01},
{0x3725, 0x92},
{0x3726, 0x01},
{0x3727, 0xc7},
{0x3800, 0x00},
{0x3801, 0x00},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3804, 0x0c},
{0x3805, 0xdf},
{0x3806, 0x09},
{0x3807, 0x9b},
{0x3808, 0x06},
{0x3809, 0x60},
{0x380a, 0x04},
{0x380b, 0xc8},
{0x380c, 0x0d},
{0x380d, 0xbc},
{0x380e, 0x04},
{0x380f, 0xf0},
{0x3810, 0x00},
{0x3811, 0x08},
{0x3812, 0x00},
{0x3813, 0x04},
{0x3814, 0x31},
{0x3815, 0x31},
{0x3816, 0x02},
{0x3817, 0x40},
{0x3818, 0x00},
{0x3819, 0x40},
{0x3820, 0x87},
{0x3821, 0x11},
{0x3b1f, 0x00},
{0x3d00, 0x00},
{0x3d01, 0x00},
{0x3d02, 0x00},
{0x3d03, 0x00},
{0x3d04, 0x00},
{0x3d05, 0x00},
{0x3d06, 0x00},
{0x3d07, 0x00},
{0x3d08, 0x00},
{0x3d09, 0x00},
{0x3d0a, 0x00},
{0x3d0b, 0x00},
{0x3d0c, 0x00},
{0x3d0d, 0x00},
{0x3d0e, 0x00},
{0x3d0f, 0x00},
{0x3d10, 0x00},
{0x3d11, 0x00},
{0x3d12, 0x00},
{0x3d13, 0x00},
{0x3d14, 0x00},
{0x3d15, 0x00},
{0x3d16, 0x00},
{0x3d17, 0x00},
{0x3d18, 0x00},
{0x3d19, 0x00},
{0x3d1a, 0x00},
{0x3d1b, 0x00},
{0x3d1c, 0x00},
{0x3d1d, 0x00},
{0x3d1e, 0x00},
{0x3d1f, 0x00},
{0x3d80, 0x00},
{0x3d81, 0x00},
{0x3d84, 0x00},
{0x3f00, 0x00},
{0x3f01, 0xfc},
{0x3f05, 0x10},
{0x3f06, 0x00},
{0x3f07, 0x00},
{0x4000, 0x29},
{0x4001, 0x02},
{0x4002, 0x45},
{0x4003, 0x08},
{0x4004, 0x04},
{0x4005, 0x18},
{0x404e, 0x37},
{0x404f, 0x8f},
{0x4300, 0xff},
{0x4303, 0x00},
{0x4304, 0x08},
{0x4307, 0x00},
{0x4600, 0x04},
{0x4601, 0x00},
{0x4602, 0x30},
{0x4800, 0x04},
{0x4801, 0x0f},
{0x4837, 0x28},
{0x4843, 0x02},
{0x5000, 0x06},
{0x5001, 0x00},
{0x5002, 0x00},
{0x5068, 0x00},
{0x506a, 0x00},
{0x501f, 0x00},
{0x5780, 0xfc},
{0x5c00, 0x80},
{0x5c01, 0x00},
{0x5c02, 0x00},
{0x5c03, 0x00},
{0x5c04, 0x00},
{0x5c05, 0x00},
{0x5c06, 0x00},
{0x5c07, 0x80},
{0x5c08, 0x10},
{0x6700, 0x05},
{0x6701, 0x19},
{0x6702, 0xfd},
{0x6703, 0xd7},
{0x6704, 0xff},
{0x6705, 0xff},
{0x6800, 0x10},
{0x6801, 0x02},
{0x6802, 0x90},
{0x6803, 0x10},
{0x6804, 0x59},
{0x6900, 0x60},
{0x6901, 0x04},
{0x5800, 0x0f},
{0x5801, 0x0d},
{0x5802, 0x09},
{0x5803, 0x0a},
{0x5804, 0x0d},
{0x5805, 0x14},
{0x5806, 0x0a},
{0x5807, 0x04},
{0x5808, 0x03},
{0x5809, 0x03},
{0x580a, 0x05},
{0x580b, 0x0a},
{0x580c, 0x05},
{0x580d, 0x02},
{0x580e, 0x00},
{0x580f, 0x00},
{0x5810, 0x03},
{0x5811, 0x05},
{0x5812, 0x09},
{0x5813, 0x03},
{0x5814, 0x01},
{0x5815, 0x01},
{0x5816, 0x04},
{0x5817, 0x09},
{0x5818, 0x09},
{0x5819, 0x08},
{0x581a, 0x06},
{0x581b, 0x06},
{0x581c, 0x08},
{0x581d, 0x06},
{0x581e, 0x33},
{0x581f, 0x11},
{0x5820, 0x0e},
{0x5821, 0x0f},
{0x5822, 0x11},
{0x5823, 0x3f},
{0x5824, 0x08},
{0x5825, 0x46},
{0x5826, 0x46},
{0x5827, 0x46},
{0x5828, 0x46},
{0x5829, 0x46},
{0x582a, 0x42},
{0x582b, 0x42},
{0x582c, 0x44},
{0x582d, 0x46},
{0x582e, 0x46},
{0x582f, 0x60},
{0x5830, 0x62},
{0x5831, 0x42},
{0x5832, 0x46},
{0x5833, 0x46},
{0x5834, 0x44},
{0x5835, 0x44},
{0x5836, 0x44},
{0x5837, 0x48},
{0x5838, 0x28},
{0x5839, 0x46},
{0x583a, 0x48},
{0x583b, 0x68},
{0x583c, 0x28},
{0x583d, 0xae},
{0x5842, 0x00},
{0x5843, 0xef},
{0x5844, 0x01},
{0x5845, 0x3f},
{0x5846, 0x01},
{0x5847, 0x3f},
{0x5848, 0x00},
{0x5849, 0xd5},
{0x3400, 0x04},
{0x3401, 0x00},
{0x3402, 0x04},
{0x3403, 0x00},
{0x3404, 0x04},
{0x3405, 0x00},
{0x3406, 0x01},
{0x5001, 0x01},
{0x5000, 0x86},
{0x301a, 0x71},
{0x301c, 0xf4},
{0x0100, 0x01},
{0x0100, 0x00},
{0x3003, 0xcc},
{0x3004, 0xd8},
{0x3006, 0x10},
{0x3007, 0x49},
{0x3020, 0x01},
{0x3501, 0x4e},
{0x3502, 0xa0},
{0x3700, 0x20},
{0x3702, 0x50},
{0x3703, 0xcc},
{0x3704, 0x19},
{0x3705, 0x32},
{0x3706, 0x4b},
{0x3708, 0x84},
{0x3709, 0x40},
{0x370a, 0x33},
{0x3711, 0x0f},
{0x3712, 0x9c},
{0x3724, 0x01},
{0x3725, 0x92},
{0x3726, 0x01},
{0x3727, 0xc7},
{0x3800, 0x00},
{0x3801, 0x00},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3804, 0x0c},
{0x3805, 0xdf},
{0x3806, 0x09},
{0x3807, 0x9b},
{0x3808, 0x05},
{0x3809, 0x00},
{0x380a, 0x03},
{0x380b, 0xc0},
{0x380c, 0x0d},
{0x380d, 0xbc},
{0x380e, 0x04},
{0x380f, 0xfa},
{0x3811, 0x08},
{0x3813, 0x04},
{0x3814, 0x31},
{0x3815, 0x31},
{0x3820, 0x87},
{0x3821, 0x11},
{0x3f00, 0x00},
{0x4005, 0x18},
{0x4601, 0x00},
{0x4602, 0x30},
{0x4837, 0x16},
{0x5068, 0x5a},
{0x506a, 0x5a},
{0x0100, 0x01},
{0x301c, 0xf0},
{0x301a, 0x70},
{ 0xFFFF, 0x00} /* end of the list */
},
{
{0x0100, 0x00},
{0x3000, 0x16},
{0x3001, 0x00},
{0x3002, 0x6c},
{0x3003, 0xce},
{0x3004, 0xd4},
{0x3005, 0x00},
{0x3006, 0x10},
{0x3007, 0x3b},
{0x300d, 0x00},
{0x301f, 0x09},
{0x3020, 0x01},
{0x3010, 0x00},
{0x3011, 0x01},
{0x3012, 0x80},
{0x3013, 0x39},
{0x3018, 0x00},
{0x3104, 0x20},
{0x3106, 0x15},
{0x3300, 0x00},
{0x3500, 0x00},
{0x3501, 0x4e},
{0x3502, 0xa0},
{0x3503, 0x07},
{0x3509, 0x10},
{0x350b, 0x1f},
{0x3600, 0x06},
{0x3601, 0x34},
{0x3602, 0x42},
{0x3603, 0x5c},
{0x3604, 0x98},
{0x3605, 0xf5},
{0x3609, 0xb4},
{0x360a, 0x7c},
{0x360b, 0xc9},
{0x360c, 0x0b},
{0x3612, 0x00},
{0x3613, 0x02},
{0x3614, 0x0f},
{0x3615, 0x00},
{0x3616, 0x03},
{0x3617, 0xa1},
{0x3618, 0x0f},
{0x3619, 0x00},
{0x361a, 0x8a},
{0x361b, 0x02},
{0x361c, 0x07},
{0x3700, 0x20},
{0x3701, 0x44},
{0x3702, 0x50},
{0x3703, 0xcc},
{0x3704, 0x19},
{0x3705, 0x32},
{0x3706, 0x4b},
{0x3707, 0x63},
{0x3708, 0x84},
{0x3709, 0x40},
{0x370a, 0x33},
{0x370b, 0x01},
{0x370c, 0x50},
{0x370d, 0x00},
{0x370e, 0x00},
{0x3711, 0x0f},
{0x3712, 0x9c},
{0x3724, 0x01},
{0x3725, 0x92},
{0x3726, 0x01},
{0x3727, 0xc7},
{0x3800, 0x00},
{0x3801, 0x00},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3804, 0x0c},
{0x3805, 0xdf},
{0x3806, 0x09},
{0x3807, 0x9b},
{0x3808, 0x06},
{0x3809, 0x60},
{0x380a, 0x04},
{0x380b, 0xc8},
{0x380c, 0x0d},
{0x380d, 0xbc},
{0x380e, 0x04},
{0x380f, 0xf0},
{0x3810, 0x00},
{0x3811, 0x08},
{0x3812, 0x00},
{0x3813, 0x04},
{0x3814, 0x31},
{0x3815, 0x31},
{0x3816, 0x02},
{0x3817, 0x40},
{0x3818, 0x00},
{0x3819, 0x40},
{0x3820, 0x87},
{0x3821, 0x11},
{0x3b1f, 0x00},
{0x3d00, 0x00},
{0x3d01, 0x00},
{0x3d02, 0x00},
{0x3d03, 0x00},
{0x3d04, 0x00},
{0x3d05, 0x00},
{0x3d06, 0x00},
{0x3d07, 0x00},
{0x3d08, 0x00},
{0x3d09, 0x00},
{0x3d0a, 0x00},
{0x3d0b, 0x00},
{0x3d0c, 0x00},
{0x3d0d, 0x00},
{0x3d0e, 0x00},
{0x3d0f, 0x00},
{0x3d10, 0x00},
{0x3d11, 0x00},
{0x3d12, 0x00},
{0x3d13, 0x00},
{0x3d14, 0x00},
{0x3d15, 0x00},
{0x3d16, 0x00},
{0x3d17, 0x00},
{0x3d18, 0x00},
{0x3d19, 0x00},
{0x3d1a, 0x00},
{0x3d1b, 0x00},
{0x3d1c, 0x00},
{0x3d1d, 0x00},
{0x3d1e, 0x00},
{0x3d1f, 0x00},
{0x3d80, 0x00},
{0x3d81, 0x00},
{0x3d84, 0x00},
{0x3f00, 0x00},
{0x3f01, 0xfc},
{0x3f05, 0x10},
{0x3f06, 0x00},
{0x3f07, 0x00},
{0x4000, 0x29},
{0x4001, 0x02},
{0x4002, 0x45},
{0x4003, 0x08},
{0x4004, 0x04},
{0x4005, 0x18},
{0x404e, 0x37},
{0x404f, 0x8f},
{0x4300, 0xff},
{0x4303, 0x00},
{0x4304, 0x08},
{0x4307, 0x00},
{0x4600, 0x04},
{0x4601, 0x00},
{0x4602, 0x30},
{0x4800, 0x04},
{0x4801, 0x0f},
{0x4837, 0x28},
{0x4843, 0x02},
{0x5000, 0x06},
{0x5001, 0x00},
{0x5002, 0x00},
{0x5068, 0x00},
{0x506a, 0x00},
{0x501f, 0x00},
{0x5780, 0xfc},
{0x5c00, 0x80},
{0x5c01, 0x00},
{0x5c02, 0x00},
{0x5c03, 0x00},
{0x5c04, 0x00},
{0x5c05, 0x00},
{0x5c06, 0x00},
{0x5c07, 0x80},
{0x5c08, 0x10},
{0x6700, 0x05},
{0x6701, 0x19},
{0x6702, 0xfd},
{0x6703, 0xd7},
{0x6704, 0xff},
{0x6705, 0xff},
{0x6800, 0x10},
{0x6801, 0x02},
{0x6802, 0x90},
{0x6803, 0x10},
{0x6804, 0x59},
{0x6900, 0x60},
{0x6901, 0x04},
{0x5800, 0x0f},
{0x5801, 0x0d},
{0x5802, 0x09},
{0x5803, 0x0a},
{0x5804, 0x0d},
{0x5805, 0x14},
{0x5806, 0x0a},
{0x5807, 0x04},
{0x5808, 0x03},
{0x5809, 0x03},
{0x580a, 0x05},
{0x580b, 0x0a},
{0x580c, 0x05},
{0x580d, 0x02},
{0x580e, 0x00},
{0x580f, 0x00},
{0x5810, 0x03},
{0x5811, 0x05},
{0x5812, 0x09},
{0x5813, 0x03},
{0x5814, 0x01},
{0x5815, 0x01},
{0x5816, 0x04},
{0x5817, 0x09},
{0x5818, 0x09},
{0x5819, 0x08},
{0x581a, 0x06},
{0x581b, 0x06},
{0x581c, 0x08},
{0x581d, 0x06},
{0x581e, 0x33},
{0x581f, 0x11},
{0x5820, 0x0e},
{0x5821, 0x0f},
{0x5822, 0x11},
{0x5823, 0x3f},
{0x5824, 0x08},
{0x5825, 0x46},
{0x5826, 0x46},
{0x5827, 0x46},
{0x5828, 0x46},
{0x5829, 0x46},
{0x582a, 0x42},
{0x582b, 0x42},
{0x582c, 0x44},
{0x582d, 0x46},
{0x582e, 0x46},
{0x582f, 0x60},
{0x5830, 0x62},
{0x5831, 0x42},
{0x5832, 0x46},
{0x5833, 0x46},
{0x5834, 0x44},
{0x5835, 0x44},
{0x5836, 0x44},
{0x5837, 0x48},
{0x5838, 0x28},
{0x5839, 0x46},
{0x583a, 0x48},
{0x583b, 0x68},
{0x583c, 0x28},
{0x583d, 0xae},
{0x5842, 0x00},
{0x5843, 0xef},
{0x5844, 0x01},
{0x5845, 0x3f},
{0x5846, 0x01},
{0x5847, 0x3f},
{0x5848, 0x00},
{0x5849, 0xd5},
{0x3400, 0x04},
{0x3401, 0x00},
{0x3402, 0x04},
{0x3403, 0x00},
{0x3404, 0x04},
{0x3405, 0x00},
{0x3406, 0x01},
{0x5001, 0x01},
{0x5000, 0x86},
{0x301a, 0x71},
{0x301c, 0xf4},
{0x0100, 0x01},
{0xFFFF, 0x00}
}
};
static const struct ov8825_reg ov8825_regdif[OV8825_MODE_MAX][256] = {
{
{0x0100, 0x00},
{0x3003, 0xcc},
{0x3004, 0xd8},
{0x3005, 0x00},
{0x3006, 0x10},
{0x3007, 0x49},
{0x3020, 0x01},
{0x3501, 0x4e},
{0x3502, 0xa0},
{0x3700, 0x20},
{0x3702, 0x50},
{0x3703, 0xcc},
{0x3704, 0x19},
{0x3705, 0x32},
{0x3706, 0x4b},
{0x3708, 0x84},
{0x3709, 0x40},
{0x370a, 0x33},
{0x3711, 0x0f},
{0x3712, 0x9c},
{0x3724, 0x01},
{0x3725, 0x92},
{0x3726, 0x01},
{0x3727, 0xc7},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3806, 0x09},
{0x3807, 0x9b},
{0x3808, 0x05},
{0x3809, 0x00},
{0x380a, 0x03},
{0x380b, 0xc0},
{0x380c, 0x0d},
{0x380d, 0xbc},
{0x380e, 0x04},
{0x380f, 0xfa},
{0x3811, 0x08},
{0x3813, 0x04},
{0x3814, 0x31},
{0x3815, 0x31},
{0x3820, 0x87},
{0x3821, 0x11},
{0x3f00, 0x00},
{0x4005, 0x18},
{0x4601, 0x00},
{0x4602, 0x30},
{0x4837, 0x16},
{0x5068, 0x5a},
{0x506a, 0x5a},
{0x0100, 0x01},
{0x301c, 0xf0},
{0x301a, 0x70},
{0xFFFF, 0x00}
},
{
{0x0100, 0x00},
{0x3003, 0xcc},
{0x3004, 0xd8},
{0x3005, 0x00},
{0x3006, 0x10},
{0x3007, 0x49},
{0x3020, 0x81},
{0x3501, 0x9a},
{0x3502, 0xa0},
{0x3700, 0x10},
{0x3702, 0x28},
{0x3703, 0x6c},
{0x3704, 0x40},
{0x3705, 0x19},
{0x3706, 0x27},
{0x3708, 0x48},
{0x3709, 0x20},
{0x370a, 0x31},
{0x3711, 0x07},
{0x3712, 0x4e},
{0x3724, 0x00},
{0x3725, 0xd4},
{0x3726, 0x00},
{0x3727, 0xf0},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3806, 0x09},
{0x3807, 0x9b},
{0x3808, 0x0c},
{0x3809, 0xc0},
{0x380a, 0x09},
{0x380b, 0x90},
{0x380c, 0x0e},
{0x380d, 0x00},
{0x380e, 0x09},
{0x380f, 0xb0},
{0x3811, 0x10},
{0x3813, 0x06},
{0x3814, 0x11},
{0x3815, 0x11},
{0x3820, 0x86},
{0x3821, 0x10},
{0x3f00, 0x02},
{0x4005, 0x1a},
{0x4601, 0x00},
{0x4602, 0x20},
{0x4837, 0x16},
{0x5068, 0x00},
{0x506a, 0x00},
{0x0100, 0x01},
{0x301c, 0xf0},
{0x301a, 0x70},
{0xFFFF, 0x00}
},
};
static const struct ov8825_reg ov8825_reg_state[OV8825_STATE_MAX][3] = {
{ /* to power down */
{0x0100, 0x00}, /* disable streaming */
{0x3018, 0x10}, /* disable mipi */
{0xFFFF, 0x00}
},
{ /* to streaming */
{0x3018, 0x00}, /* enable mipi */
{0x0100, 0x01}, /* enable streaming */
{0xFFFF, 0x00}
},
};
static int set_flash_mode(struct i2c_client *client, int mode);
static int ov8825_set_mode(struct i2c_client *client, int new_mode_idx);
static int ov8825_set_state(struct i2c_client *client, int new_state);
static int ov8825_init(struct i2c_client *client);
/*add an timer to close the flash after two frames*/
#if defined(CONFIG_MACH_JAVA_C_5606)
static struct timer_list timer;
static char *msg = "hello world";
static void print_func(unsigned long lparam)
{
gpio_set_value(TORCH_EN, 0);
gpio_set_value(FLASH_EN, 0);
}
#endif
/*
* Find a data format by a pixel code in an array
*/
static int ov8825_find_datafmt(enum v4l2_mbus_pixelcode code)
{
int i;
for (i = 0; i < ARRAY_SIZE(ov8825_fmts); i++)
if (ov8825_fmts[i].code == code)
break;
/* If not found, select latest */
if (i >= ARRAY_SIZE(ov8825_fmts))
i = ARRAY_SIZE(ov8825_fmts) - 1;
return i;
}
/* Find a frame size in an array */
static int ov8825_find_framesize(u32 width, u32 height)
{
int i;
for (i = 0; i < OV8825_MODE_MAX; i++) {
if ((ov8825_mode[i].width >= width) &&
(ov8825_mode[i].height >= height))
break;
}
/* If not found, select biggest */
if (i >= OV8825_MODE_MAX)
i = OV8825_MODE_MAX - 1;
return i;
}
static struct ov8825 *to_ov8825(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client), struct ov8825, subdev);
}
/**
*ov8825_reg_read - Read a value from a register in an ov8825 sensor device
*@client: i2c driver client structure
*@reg: register address / offset
*@val: stores the value that gets read
*
* Read a value from a register in an ov8825 sensor device.
* The value is returned in 'val'.
* Returns zero if successful, or non-zero otherwise.
*/
static int ov8825_reg_read(struct i2c_client *client, u16 reg, u8 *val)
{
int ret;
u8 data[2] = { 0 };
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = data,
};
data[0] = (u8) (reg >> 8);
data[1] = (u8) (reg & 0xff);
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
goto err;
msg.flags = I2C_M_RD;
msg.len = 1;
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
goto err;
*val = data[0];
return 0;
err:
dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
return ret;
}
/**
* Write a value to a register in ov8825 sensor device.
*@client: i2c driver client structure.
*@reg: Address of the register to read value from.
*@val: Value to be written to a specific register.
* Returns zero if successful, or non-zero otherwise.
*/
static int ov8825_reg_write(struct i2c_client *client, u16 reg, u8 val)
{
int ret;
unsigned char data[3] = { (u8) (reg >> 8), (u8) (reg & 0xff), val };
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = 3,
.buf = data,
};
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0) {
dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
return ret;
}
return 0;
}
static int ov8825_reg_read_multi(struct i2c_client *client,
u16 reg, u8 *val, u16 cnt)
{
int ret;
u8 data[2] = { 0 };
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = data,
};
data[0] = (u8) (reg >> 8);
data[1] = (u8) (reg & 0xff);
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
goto err;
msg.flags = I2C_M_RD;
msg.len = cnt;
msg.buf = val;
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
goto err;
return 0;
err:
dev_err(&client->dev, "Failed multiple reading register 0x%02x\n", reg);
return ret;
}
static const struct v4l2_queryctrl ov8825_controls[] = {
{
.id = V4L2_CID_CAMERA_FRAME_RATE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Framerate control",
.minimum = FRAME_RATE_AUTO,
.maximum = (1 << FRAME_RATE_AUTO | 1 << FRAME_RATE_5 |
1 << FRAME_RATE_10 | 1 << FRAME_RATE_15 |
1 << FRAME_RATE_25 | 1 << FRAME_RATE_30),
.step = 1,
.default_value = FRAME_RATE_AUTO,
},
{
.id = V4L2_CID_CAMERA_FOCUS_MODE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Focus Modes",
.minimum = FOCUS_MODE_AUTO,
.maximum = (1 << FOCUS_MODE_AUTO | 1 << FOCUS_MODE_MACRO
| 1 << FOCUS_MODE_INFINITY | 1 << FOCUS_MODE_MANUAL),
.step = 1,
.default_value = FOCUS_MODE_AUTO,
},
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Gain",
.minimum = OV8825_GAIN_MIN,
.maximum = OV8825_GAIN_MAX,
.step = OV8825_GAIN_STEP,
.default_value = DEFAULT_GAIN,
},
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Exposure",
.minimum = OV8825_EXP_MIN,
.maximum = OV8825_EXP_MAX,
.step = OV8825_EXP_STEP,
.default_value = DEFAULT_EXPO,
},
{
.id = V4L2_CID_CAMERA_LENS_POSITION,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Lens Position",
.minimum = OV8825_LENS_MIN,
.maximum = OV8825_LENS_MAX,
.step = OV8825_LENS_STEP,
.default_value = DEFAULT_LENS_POS,
},
{
.id = V4L2_CID_CAMERA_FLASH_MODE,
.type = V4L2_CTRL_TYPE_INTEGER,
#ifdef CONFIG_VIDEO_AS3643
.name = "AS3643-flash",
#endif
#ifdef CONFIG_GPIO_FLASH
.name = "GPIO-flash",
#endif
.minimum = FLASH_MODE_OFF,
.maximum = (1 << FLASH_MODE_OFF) | (1 << FLASH_MODE_ON) |
(1 << FLASH_MODE_TORCH_OFF) | (1 << FLASH_MODE_TORCH_ON),
.step = 1,
.default_value = FLASH_MODE_OFF,
},
{
.id = V4L2_CID_FLASH_INTENSITY,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Intensity in mA",
.minimum = OV8825_FLASH_INTENSITY_MIN,
.maximum = OV8825_FLASH_INTENSITY_MAX,
.step = OV8825_FLASH_INTENSITY_STEP,
.default_value = OV8825_FLASH_INTENSITY_DEFAULT,
},
{
.id = V4L2_CID_FLASH_TIMEOUT,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Flash timeout in us",
.minimum = OV8825_FLASH_TIMEOUT_MIN,
.maximum = OV8825_FLASH_TIMEOUT_MAX,
.step = OV8825_FLASH_TIMEOUT_STEP,
.default_value = OV8825_FLASH_TIMEOUT_DEFAULT,
},
};
/**
* Initialize a list of ov8825 registers.
* The list of registers is terminated by the pair of values
*@client: i2c driver client structure.
*@reglist[]: List of address of the registers to write data.
* Returns zero if successful, or non-zero otherwise.
*/
static int ov8825_reg_writes(struct i2c_client *client,
const struct ov8825_reg reglist[])
{
int err = 0, i;
for (i = 0; reglist[i].reg != 0xFFFF; i++) {
if (reglist[i].reg == 0xFFFE) {
msleep(reglist[i].val);
} else {
err |= ov8825_reg_write(client, reglist[i].reg,
reglist[i].val);
}
if (err != 0)
break;
}
return 0;
}
#ifdef OV8825_DEBUG
static int ov8825_reglist_compare(struct i2c_client *client,
const struct ov8825_reg reglist[])
{
int err = 0;
u8 reg, i;
for (i = 0; ((reglist[i].reg != 0xFFFF) && (err == 0)); i++) {
if (reglist[i].reg != 0xFFFE) {
err |= ov8825_reg_read(client, reglist[i].reg, &reg);
pr_debug("ov8825_reglist_compare: [0x%04x]=0x%02x",
reglist[i].reg, reg);
} else {
msleep(reglist[i].val);
}
}
return 0;
}
#endif
/**
* Write an array of data to ov8825 sensor device.
*@client: i2c driver client structure.
*@reg: Address of the register to read value from.
*@data: pointer to data to be written starting at specific register.
*@size: # of data to be written starting at specific register.
* Returns zero if successful, or non-zero otherwise.
*/
static int ov8825_array_write(struct i2c_client *client,
const u8 *data, u16 size)
{
int ret;
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = size,
.buf = (u8 *)data,
};
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0) {
dev_err(&client->dev, "Failed writing array to 0x%02x!\n",
((data[0] << 8) | (data[1])));
return ret;
}
return 0;
}
#if 0
static const struct ov8825_reg ov8825_otpbank_rdsel[2][8] = {
{ /* bank 0 */
{0x3d84, 0xc0},
{0x3d85, 0x00},
{0x3d86, 0x0f},
{0x3d81, 0x01},
{0xFFFE, 20}, /* delay 20 ms */
{0xFFFF, 0x00}
},
{ /* bank 1 */
{0x3d84, 0xc0},
{0x3d85, 0x10},
{0x3d86, 0x1f},
{0x3d81, 0x01},
{0xFFFE, 20}, /* delay 20 ms */
{0xFFFF, 0x00}
}
};
/**
* Read OTP data into buf
*@client: i2c driver client structure.
*@bank: OTP bank index
*@buf: buffer for OTP data
* Returns zero if successful or non-zero otherwise.
*/
static int ov8825_otp_bank_read(struct i2c_client *client, int bank, u8 *buf)
{
int ret, i;
u8 val;
pr_debug("ov8825_otp_bank_read: bank=%d\n", bank);
ret = ov8825_reg_writes(client, ov8825_otpbank_rdsel[bank&1]);
if (ret != 0) {
dev_err(&client->dev, "OTP bank %d read select failed\n", bank);
return -1;
}
for (i = 0; i < 0x10; i++) {
ret |= ov8825_reg_read(client, 0x3d00+i, &val);
buf[i] = val;
}
pr_debug("ov8825_otp_bank_read: OK bank=%d\n", bank);
return 0;
}
/**
* Print ov8825 OTP calibration data
*@otp: Address of otp structure to print
*@index: OTP index
*/
#if OV8825_DEBUG
static void ov8825_otp_print(struct ov8825_otp *otp, int index)
{
pr_debug("ov8825: OTP index=%d\n", index);
pr_debug("ov8825: integr_id = 0x%02X\n", otp->module_integrator_id);
pr_debug("ov8825: lens_id = 0x%02X\n", otp->lens_id);
pr_debug("ov8825: rg_ratio = 0x%04X\n", otp->rg_ratio);
pr_debug("ov8825: bg_ratio = 0x%04X\n", otp->bg_ratio);
pr_debug("ov8825: light_rg = 0x%04X\n", otp->light_rg);
pr_debug("ov8825: light_bg = 0x%04X\n", otp->light_bg);
pr_debug("ov8825: user_data = [0x%02X 0x%02X]\n",
otp->user_data[0], otp->user_data[1]);
}
/**
* Read and print ov8825 OTP R/B channel gains.
*@client: i2c driver client structure.
*/
static void ov8825_rbgains_print(struct i2c_client *client)
{
u8 val;
u16 gainr, gaing, gainb;
int i;
ov8825_reg_read(client, 0x5001, &val);
pr_debug("ov8825 0x%04X=0x%02X]\n", 0x5001, val);
ov8825_reg_read(client, 0x5002, &val);
pr_debug("ov8825 0x%04X=0x%02X]\n", 0x5002, val);
ov8825_reg_read(client, 0x5180, &val);
pr_debug("ov8825 0x%04X=0x%02X]\n", 0x5180, val);
ov8825_reg_read(client, 0x5186, &val);
gainr = val << 8;
ov8825_reg_read(client, 0x5187, &val);
gainr += val;
ov8825_reg_read(client, 0x5188, &val);
gaing = val << 8;
ov8825_reg_read(client, 0x5189, &val);
gaing += val;
ov8825_reg_read(client, 0x518a, &val);
gainb = val << 8;
ov8825_reg_read(client, 0x518b, &val);
gainb += val;
pr_debug("ov8825 rb gains readback = [%04X %04X %04X]\n",
gainr, gaing, gainb);
}
#endif
/**
* Convert OTP binary data to formatted calibration information
*@otp: Address of otp structure to write values into
*@buf: otp binary data
* Returns zero if successful or non-zero otherwise.
*/
static void ov8825_buf2otp(struct ov8825_otp *otp, u8 *buf)
{
otp->flag = buf[0] & 0x08;
otp->module_integrator_id = buf[0] & 0x7f;
otp->lens_id = buf[1];
otp->rg_ratio = (buf[2] << 2) + ((buf[6] >> 6) & 3);
otp->bg_ratio = (buf[3] << 2) + ((buf[6] >> 4) & 3);
otp->light_rg = (buf[7] << 2) + ((buf[6] >> 2) & 3);
otp->light_bg = (buf[8] << 2) + ((buf[6] >> 0) & 3);
otp->user_data[0] = buf[4];
otp->user_data[1] = buf[5];
}
/**
* Read OTP calibration data at index from ov8825 sensor.
*@client: i2c driver client structure.
*@otp: Address of otp structure to read values into
*@bank: otp index select
* Returns zero if OTP is present and read is successful
* or non-zero otherwise.
*/
static int ov8825_otp_index_read(struct i2c_client *client,
struct ov8825_otp *otp, int index)
{
int ret = -1;
u8 buf[32];
pr_debug("ov8825_otp_index_read: index %d OTP read\n", index);
memset(otp, 0, sizeof(*otp));
switch (index) {
case 0:
ret = ov8825_otp_bank_read(client, 0, buf);
if (ret < 0)
break;
ov8825_buf2otp(otp, buf+5);
break;
case 1:
ret = ov8825_otp_bank_read(client, 0, buf);
break;
if (ret < 0)
break;
ov8825_buf2otp(otp, buf+0xe);
break;
case 2:
ret = ov8825_otp_bank_read(client, 0, buf);
if (ret < 0)
break;
ov8825_buf2otp(otp, buf+7);
break;
default:
pr_debug("ov8825_otp_index_read: invalid OTP idx: %d\n",
index);
ret = -1;
break;
}
pr_debug("ov8825_otp_index_read: rg_ratio = 0x%04X\n", otp->rg_ratio);
pr_debug("ov8825_otp_index_read: bg_ratio = 0x%04X\n", otp->bg_ratio);
#if OV8825_DEBUG
ov8825_otp_print(otp, index);
#endif
if (ret != 0) {
pr_debug("ov8825_otp_index_read: index %d OTP read failed\n",
index);
memset(otp, 0, sizeof(*otp));
return -1;
}
if (otp->flag == 1 || otp->rg_ratio == 0 || otp->bg_ratio == 0) {
memset(otp, 0, sizeof(*otp));
return -1;
}
return 0;
}
/**
* Find and read OTP calibration data from ov8825 sensor.
*@client: i2c driver client structure.
*@otp: Address of otp structure to read values into
*@bank: otp index select
* Returns zero if OTP is present and read is successful
* or non-zero otherwise.
*/
static int ov8825_otp_read(struct i2c_client *client)
{
int i, ret;
struct ov8825 *ov8825 = to_ov8825(client);
struct ov8825_otp *otp = &(ov8825->otp);
for (i = 0; i < 3; i++) {
ret = ov8825_otp_index_read(client, otp, i);
if (ret == 0)
break;
}
return ret;
}
/**
* Set Sensor R and B channel gains according to OTP calibration data.
*@client: i2c driver client structure.
*@otp: Address of otp structure with calibration data
* Returns zero if successful, or non-zero otherwise.
*/
static int ov8825_rbgains_update(struct i2c_client *client)
{
#define RG_GOLDEN 0x145
#define BG_GOLDEN 0x15e
u16 gainr, gaing, gainb, ggainb, ggainr;
int ret;
struct ov8825 *ov8825 = to_ov8825(client);
struct ov8825_otp *otp = &(ov8825->otp);
if (otp->rg_ratio == 0 || otp->bg_ratio == 0) {
pr_debug("ov8825_rbgains_update: OTP not initialized\n");
return -1;
}
# if 0
gainr = 0x400 * RG_GOLDEN / otp->rg_ratio;
gaing = 0x400;
gainb = 0x400 * BG_GOLDEN / otp->bg_ratio;
#else
if (otp->bg_ratio < BG_GOLDEN) {
if (otp->rg_ratio < RG_GOLDEN) {
gaing = 0x400;
gainb = 0x400 * BG_GOLDEN / otp->bg_ratio;
gainr = 0x400 * RG_GOLDEN / otp->rg_ratio;
} else {
gainr = 0x400;
gaing = 0x400 * otp->rg_ratio / RG_GOLDEN;
gainb = gaing * BG_GOLDEN / otp->bg_ratio;
}
} else {
if (otp->rg_ratio < RG_GOLDEN) {
gainb = 0x400;
gaing = 0x400 * otp->bg_ratio / BG_GOLDEN;
gainr = gaing * RG_GOLDEN / otp->rg_ratio;
} else {
ggainb = 0x400 * otp->bg_ratio / BG_GOLDEN;
ggainr = 0x400 * otp->rg_ratio / RG_GOLDEN;
if (ggainb > ggainr) {
gainb = 0x400;
gaing = ggainb;
gainr = gaing * RG_GOLDEN / otp->rg_ratio;
} else {
gainr = 0x400;
gaing = ggainr;
gainb = gaing * BG_GOLDEN / otp->bg_ratio; }
}
}
#endif
ret = ov8825_reg_write(client, 0x5180, 1<<3);
ret |= ov8825_reg_write(client, 0x5186, gainr >> 8);
ret |= ov8825_reg_write(client, 0x5187, gainr & 0xff);
ret |= ov8825_reg_write(client, 0x5188, gaing >> 8);
ret |= ov8825_reg_write(client, 0x5189, gaing & 0xff);
ret |= ov8825_reg_write(client, 0x518a, gainb >> 8);
ret |= ov8825_reg_write(client, 0x518b, gainb & 0xff);
if (ret == 0) {
dev_info(&client->dev, "ov8825 1 sensor update for calibrated data g=[0x%04X, 0x%04X, 0x%04X]\n",
gainr, gaing, gainb);
#if OV5648_DEBUG
ov8825_rbgains_print(client);
#endif
return 0;
} else {
dev_info(&client->dev,
"ov8825 sensor 1 update for calibrated data failed\n");
return -1;
}
}
#endif
/* ov8825 use internal vcm driver, the same i2c client */
#define OV8825_VCM_STEP_PERIOD_US 100 /* in microseconds */
#define OV8825_VCM_MAX_POSITION 1023
#define OV8825_VCM_START_POINT 100
#define OV8825_VCM_MIN_POSITION 0
#define OV8825_DAC_A_VCM_LOW 0x3618
#define OV8825_DAC_A_VCM_MID0 0x3619
/*
* Input: lens position from 0 to 1024
* Output: DAC code
* DAC code is {3619[5:0],3618[7:4]}. 3618[3:0] is slew rate.
*/
static int ov8825_vcm_position_code(struct ov8825 *ov8825,
uint8_t *code1, uint8_t *code2)
{
int pos = ov8825->position;
*code1 = ((pos & 0xF) << 4) | 0x2;
*code2 = (pos >> 4) & 0x3F;
return 0;
}
static int ov8825_vcm_lens_set_position(struct i2c_client *client,
int target_position)
{
int ret = 0;
unsigned int diff;
int dac_code;
struct ov8825 *ov8825 = to_ov8825(client);
uint8_t code_3618, code_3619;
int step = 0;
ov8825->requested_position = target_position;
dac_code = ((OV8825_VCM_MAX_POSITION - OV8825_VCM_START_POINT)
* (255 - target_position)) / 255 + OV8825_VCM_START_POINT;
dac_code = max(OV8825_VCM_MIN_POSITION,
min(dac_code, OV8825_VCM_MAX_POSITION));
diff = abs(dac_code - ov8825->position);
ov8825->position = dac_code;
pr_debug("ov8825_vcm_lens_set_position diff=%d", diff);
ov8825_vcm_position_code(ov8825, &code_3618, &code_3619);
/* Not exact, but close (off by <500us) */
#if 0
while (diff) {
if (0 < diff <= 16) {
step++;
break;
} else if (16 < diff <= 128) {
step += diff / 16;
diff = diff % 16;
} else if (diff > 128) {
step += diff / 64;
diff = diff % 64;
}
}
#endif
pr_debug("ov8825_vcm_lens_set_position step=%d", step);
ov8825->flying_time = step * OV8825_VCM_STEP_PERIOD_US;
ov8825->flying_start_time = jiffies_to_usecs(jiffies);
ret = ov8825_reg_write(client, OV8825_DAC_A_VCM_LOW, code_3618);
ret |= ov8825_reg_write(client, OV8825_DAC_A_VCM_MID0, code_3619);
pr_debug("target_position=%d dac_code=%d", target_position, dac_code);
return ret;
}
/*
* Routine used to get the current lens position and/or the estimated
*time required to get to the requested destination (time in us).
*/
int ov8825_vcm_lens_get_position(struct i2c_client *client,
int *current_position, int *time_to_destination)
{
struct ov8825 *ov8825 = to_ov8825(client);
*time_to_destination = (ov8825->flying_start_time +
ov8825->flying_time) - jiffies_to_usecs(jiffies);
if (*time_to_destination < 0 || !ov8825->flying_time ||
*time_to_destination > 100000) {
ov8825->flying_time = 0;
*current_position = ov8825->requested_position;
} else {
*current_position = -1;
}
pr_debug("current_position=%X(%d)", (*current_position) & 0xffff,
(*current_position) & 0xffff);
return 0;
}
/*
*Routine used to send lens to a traget position position and calculate
*the estimated time required to get to this position, the flying time in us.
*/
static int ov8825_lens_set_position(struct i2c_client *client,
int target_position)
{
int ret = 0;
ret = ov8825_vcm_lens_set_position(client, target_position);
return ret;
}
/*
* Routine used to get the current lens position and/or the estimated
*time required to get to the requested destination (time in us).
*/
static void ov8825_lens_get_position(struct i2c_client *client,
int *current_position,
int *time_to_destination)
{
int ret = 0;
int i = 0;
struct ov8825 *ov8825 = to_ov8825(client);
static int lens_read_buf[LENS_READ_DELAY];
ret = ov8825_vcm_lens_get_position(client,
current_position, time_to_destination);
for (i = 0; i < ov8825->lenspos_delay; i++)
lens_read_buf[i] = lens_read_buf[i + 1];
lens_read_buf[ov8825->lenspos_delay] = *current_position;
*current_position = lens_read_buf[0];
return;
}
/*
* Setup the sensor integration and frame length based on requested mimimum
* framerate
*/
static void ov8825_set_framerate_lo(struct i2c_client *client, int framerate)
{
struct ov8825 *ov8825 = to_ov8825(client);
framerate = max(framerate, ov8825->framerate_lo_absolute);
framerate = min(framerate, ov8825->framerate_hi_absolute);
ov8825->framerate_lo = framerate;
ov8825->vts_max = 1000000000
/ (unsigned long)((ov8825->line_length * framerate) >> 8);
ov8825->vts_max = min(ov8825->vts_max,
ov8825_mode[ov8825->mode_idx].vts_max);
pr_debug("ov8825_set_framerate_lo: Setting frame rate lo %d vts_max %d",
ov8825->framerate_lo, ov8825->vts_max);
}
/*
* Setup the sensor integration and frame length based on requested maximum
* framerate, famerate is 24p8 fixed point.
*/
static void ov8825_set_framerate_hi(struct i2c_client *client, int framerate)
{
struct ov8825 *ov8825 = to_ov8825(client);
framerate = max(framerate, ov8825->framerate_lo_absolute);
framerate = min(framerate, ov8825->framerate_hi_absolute);
ov8825->framerate_hi = framerate;
ov8825->vts_min = 1000000000
/ (unsigned long)((ov8825->line_length * framerate) >> 8);
ov8825->vts_min = max(ov8825->vts_min,
ov8825_mode[ov8825->mode_idx].vts);
pr_debug("ov8825_set_framerate_hi: Setting frame rate hi %d vts_min %d",
ov8825->framerate_hi, ov8825->vts_min);
}
/*
* Determine the closest achievable gain to the desired gain.
* Calculates the closest achievable gain to the requested gain
* that can be achieved by this sensor.
* @param gain_value Desired gain on 8.8 linear scale.
* @return Achievable gain on 8.8 linear scale.
*/
static int ov8825_calc_gain(struct i2c_client *client, int gain_value,
int *gain_code_p)
{
int gain_code = (gain_value & 0x3fff) >> 4;
int actual_gain = gain_code << 4;
if (gain_code_p)
*gain_code_p = gain_code;
return actual_gain;
}
/*
*setup the sensor analog gain on requested gain value 8.8 linear scale.
*/
#define GAIN_HIST_MAX 0
static int ov8825_set_gain(struct i2c_client *client, int gain_value)
{
struct ov8825 *ov8825 = to_ov8825(client);
int ret = 0;
int gain_code_analog, gain_actual;
#if GAIN_HIST_MAX > 0
int i;
static int gain_prev[GAIN_HIST_MAX] = {DEFAULT_GAIN, DEFAULT_GAIN,
DEFAULT_GAIN, DEFAULT_GAIN};
for (i = 0; i < GAIN_HIST_MAX - 1; i++)
gain_prev[i] = gain_prev[i + 1];
gain_prev[GAIN_HIST_MAX-1] = gain_value;
gain_value = 0;
for (i = 0; i < GAIN_HIST_MAX ; i++)
gain_value = gain_value + gain_prev[i];
gain_value = gain_value / GAIN_HIST_MAX;
#endif
gain_actual = ov8825_calc_gain(client, gain_value, &gain_code_analog);
pr_debug("ov8825_set_gain: cur=%u req=%u act=%u cod=%u",
ov8825->gain_current, gain_value,
gain_actual, gain_code_analog);
if (gain_actual == ov8825->gain_current)
return 0;
ret = ov8825_reg_write(client,
OV8825_REG_AGC_HI, (gain_code_analog >> 8) & 0x3);
ret |= ov8825_reg_write(client,
OV8825_REG_AGC_LO, gain_code_analog & 0xFF);
ov8825->gain_current = gain_actual;
return 0;
}
static int ov8825_get_gain(struct i2c_client *client,
int *gain_value_p, int *gain_code_p)
{
struct ov8825 *ov8825 = to_ov8825(client);
int ret = 0;
int gain_code;
u8 gain_buf[2];
int i;
/*
ov8825_reg_read_multi(client, OV8825_REG_AGC_HI, gain_buf, 2);
gain_code = ((gain_buf[0] & 0x3f) << 8) + gain_buf[1];
*/
gain_code = ov8825->gain_current >> 4;
if (Is_SnapToPrev_gain == 1) {
gain_code = ov8825->gain_read_buf[0];
Is_SnapToPrev_gain = 0;
}
if (ov8825->aecpos_delay > 0) {
ov8825->gain_read_buf[ov8825->aecpos_delay] = gain_code;
gain_code = ov8825->gain_read_buf[0];
for (i = 0; i < GAIN_DELAY_MAX-1; i++)
ov8825->gain_read_buf[i] = ov8825->gain_read_buf[i+1];
}
if (gain_code < 0x10)
gain_code = 0x10;
if (gain_code_p)
*gain_code_p = gain_code;
if (gain_value_p)
*gain_value_p = gain_code << 4;
return ret;
}
static int ov8825_get_exposure(struct i2c_client *client,
int *exp_value_p, int *exp_code_p)
{
struct ov8825 *ov8825 = to_ov8825(client);
int i, ret = 0;
int exp_code;
u8 exp_buf[3];
/*
ov8825_reg_read_multi(client, OV8825_REG_EXP_HI, exp_buf, 3);
exp_code =
((exp_buf[0] & 0xf) << 16) +
((exp_buf[1] & 0xff) << 8) +
(exp_buf[2] & 0xf0);
*/
exp_code = (ov8825->exposure_current * 1000 / ov8825->line_length) << 4;
if (Is_SnapToPrev_expo == 1) {
exp_code = ov8825->exp_read_buf[0];
Is_SnapToPrev_expo = 0;
}
if (ov8825->aecpos_delay > 0) {
ov8825->exp_read_buf[ov8825->aecpos_delay] = exp_code;
exp_code = ov8825->exp_read_buf[0];
for (i = 0; i < EXP_DELAY_MAX-1; i++)
ov8825->exp_read_buf[i] = ov8825->exp_read_buf[i+1];
}
pr_debug("ov8825_get_exposure: exp_code=%d", exp_code);
if (exp_code_p)
*exp_code_p = exp_code;
if (exp_value_p)
*exp_value_p = ((exp_code >> 4) * ov8825->line_length) /
(unsigned long)1000;
return ret;
}
#define INTEGRATION_MIN 1
#define INTEGRATION_OFFSET 10
static int ov8825_calc_exposure(struct i2c_client *client,
int exposure_value,
unsigned int *vts_ptr,
unsigned int *coarse_int_lines)
{
struct ov8825 *ov8825 = to_ov8825(client);
unsigned int integration_lines;
int vts = ov8825_mode[ov8825->mode_idx].vts;
int binning_factor = ov8825_mode[ov8825->mode_idx].binning_factor;
integration_lines = (1000 * exposure_value) / ov8825->line_length;
integration_lines = integration_lines * binning_factor;
if (integration_lines < INTEGRATION_MIN)
integration_lines = INTEGRATION_MIN;
else if (integration_lines > (unsigned int)
(ov8825->vts_max - INTEGRATION_OFFSET))
integration_lines = ov8825->vts_max - INTEGRATION_OFFSET;
vts = min(integration_lines + INTEGRATION_OFFSET,
(unsigned int)ov8825_mode[ov8825->mode_idx].vts_max);
vts = max(vts, ov8825_mode[ov8825->mode_idx].vts);
vts = max(vts, ov8825->vts_min);
if (coarse_int_lines)
*coarse_int_lines = integration_lines;
if (vts_ptr)
*vts_ptr = vts;
exposure_value = integration_lines * ov8825->line_length /
(unsigned long)1000;
return exposure_value;
}
/*
* Setup the sensor integration and frame length based on requested exposure
* in microseconds.
*/
#define EXP_HIST_MAX 0
static void ov8825_set_exposure(struct i2c_client *client, int exp_value)
{
struct ov8825 *ov8825 = to_ov8825(client);
u8 exp_buf[5];
unsigned int actual_exposure;
unsigned int vts;
unsigned int coarse_int_lines;
int ret = 0;
#if EXP_HIST_MAX > 0
int i;
static int exp_prev[EXP_HIST_MAX];
for (i = 0; i < EXP_HIST_MAX - 1; i++)
exp_prev[i] = exp_prev[i + 1];
exp_prev[EXP_HIST_MAX - 1] = exp_value;
exp_value = 0;
for (i = 0; i < EXP_HIST_MAX; i++)
exp_value = exp_value + exp_prev[i];
exp_value = exp_value / EXP_HIST_MAX;
#endif
actual_exposure = ov8825_calc_exposure(client, exp_value,
&vts, &coarse_int_lines);
pr_debug("ov8825_set_exposure: cur=%d req=%d act=%d coarse=%d vts=%d",
ov8825->exposure_current, exp_value, actual_exposure,
coarse_int_lines, vts);
ov8825->vts = vts;
ov8825->exposure_current = actual_exposure;
coarse_int_lines <<= 4;
exp_buf[0] = (u8)((OV8825_REG_EXP_HI >> 8) & 0xFF);
exp_buf[1] = (u8)(OV8825_REG_EXP_HI & 0xFF);
exp_buf[2] = (u8)((coarse_int_lines >> 16) & 0xFF);
exp_buf[3] = (u8)((coarse_int_lines >> 8) & 0xFF);
exp_buf[4] = (u8)((coarse_int_lines >> 0) & 0xF0);
ret = ov8825_array_write(client, exp_buf, 5);
ret |= ov8825_reg_write(client,
OV8825_REG_TIMING_VTS_HI, (vts & 0xFF00) >> 8);
ret |= ov8825_reg_write(client, OV8825_REG_TIMING_VTS_LO, vts & 0xFF);
if (ret) {
pr_debug("ov8825_set_exposure: error writing exp. ctrl");
return;
}
}
static int ov8825_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
ret = ov8825_set_state(client, enable);
return ret;
}
static int ov8825_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
/* TODO: Do the right thing here, and validate bus params */
return 0;
}
static unsigned long ov8825_query_bus_param(struct soc_camera_device *icd)
{
unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING |
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER;
/* TODO: Do the right thing here, and validate bus params */
flags |= SOCAM_DATAWIDTH_10;
return flags;
}
static int ov8825_enum_input(struct soc_camera_device *icd,
struct v4l2_input *inp)
{
struct soc_camera_link *icl = to_soc_camera_link(icd);
struct v4l2_subdev_sensor_interface_parms *plat_parms;
inp->type = V4L2_INPUT_TYPE_CAMERA;
inp->std = V4L2_STD_UNKNOWN;
strcpy(inp->name, "ov8825");
if (icl && icl->priv) {
plat_parms = icl->priv;
inp->status = 0;
if (plat_parms->orientation == V4L2_SUBDEV_SENSOR_PORTRAIT)
inp->status |= V4L2_IN_ST_HFLIP;
if (plat_parms->facing == V4L2_SUBDEV_SENSOR_BACK)
inp->status |= V4L2_IN_ST_BACK;
}
return 0;
}
static int ov8825_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov8825 *ov8825 = to_ov8825(client);
mf->width = ov8825_mode[ov8825->mode_idx].width;
mf->height = ov8825_mode[ov8825->mode_idx].height;
mf->code = ov8825_fmts[ov8825->i_fmt].code;
mf->colorspace = ov8825_fmts[ov8825->i_fmt].colorspace;
mf->field = V4L2_FIELD_NONE;
return 0;
}
static int ov8825_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
{
int i_fmt;
int mode_idx;
i_fmt = ov8825_find_datafmt(mf->code);
mf->code = ov8825_fmts[i_fmt].code;
mf->colorspace = ov8825_fmts[i_fmt].colorspace;
mf->field = V4L2_FIELD_NONE;
mode_idx = ov8825_find_framesize(mf->width, mf->height);
mf->width = ov8825_mode[mode_idx].width;
mf->height = ov8825_mode[mode_idx].height;
return 0;
}
static int ov8825_set_mode(struct i2c_client *client, int new_mode_idx)
{
struct ov8825 *ov8825 = to_ov8825(client);
int ret = 0;
if (ov8825->mode_idx == new_mode_idx) {
pr_debug("ov8825_set_mode: skip init from mode[%d]=%s to mode[%d]=%s",
ov8825->mode_idx, ov8825_mode[ov8825->mode_idx].name,
new_mode_idx, ov8825_mode[new_mode_idx].name);
return ret;
}
if (ov8825->mode_idx == OV8825_MODE_MAX) {
pr_debug("ov8825_set_mode: full init from mode[%d]=%s to mode[%d]=%s",
ov8825->mode_idx, ov8825_mode[ov8825->mode_idx].name,
new_mode_idx, ov8825_mode[new_mode_idx].name);
ov8825_init(client);
ret = ov8825_reg_writes(client, ov8825_regtbl[new_mode_idx]);
} else {
pr_debug("ov8825_set_mode: diff init from mode[%d]=%s to mode[%d]=%s",
ov8825->mode_idx, ov8825_mode[ov8825->mode_idx].name,
new_mode_idx, ov8825_mode[new_mode_idx].name);
ret = ov8825_reg_writes(client, ov8825_regdif[new_mode_idx]);
}
if (ret)
return ret;
if (new_mode_idx == 1) {
Is_SnapToPrev_expo = 1;
Is_SnapToPrev_gain = 1;
}
ov8825->mode_idx = new_mode_idx;
ov8825->line_length = ov8825_mode[new_mode_idx].line_length_ns;
ov8825->vts = ov8825_mode[new_mode_idx].vts;
ov8825->framerate_lo_absolute = F24p8(1.0);
ov8825->framerate_hi_absolute = ov8825_mode[new_mode_idx].fps;
ov8825_set_framerate_lo(client, ov8825->framerate_lo_absolute);
ov8825_set_framerate_hi(client, ov8825->framerate_hi_absolute);
ov8825_set_exposure(client, ov8825->exposure_current);
ov8825_set_gain(client, ov8825->gain_current);
return 0;
}
static int ov8825_set_state(struct i2c_client *client, int new_state)
{
struct ov8825 *ov8825 = to_ov8825(client);
int ret = 0;
pr_debug("ov8825_set_state: %d (%s) -> %d (%s)", ov8825->state,
ov8825->state ? "strm" : "stop",
new_state, new_state ? "strm" : "stop");
if (ov8825->state != new_state) {
ret = ov8825_reg_writes(client, ov8825_reg_state[new_state]);
ov8825->state = new_state;
if (OV8825_STATE_STRM == new_state) {
ov8825->frame_info_size = 0;
memset(ov8825->frame_info, 0,
ARRAY_SIZE(ov8825->frame_info));
if (0 == ov8825->calibrated)
ov8825->calibrated = 1;
}
}
return ret;
}
static int ov8825_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov8825 *ov8825 = to_ov8825(client);
int ret = 0;
int new_mode_idx;
ret = ov8825_try_fmt(sd, mf);
if (ret < 0)
return ret;
pr_debug("ov8825_s_fmt: width %d, height %d", mf->width, mf->height);
new_mode_idx = ov8825_find_framesize(mf->width, mf->height);
ov8825->i_fmt = ov8825_find_datafmt(mf->code);
switch ((u32) ov8825_fmts[ov8825->i_fmt].code) {
case V4L2_MBUS_FMT_SBGGR10_1X10:
case V4L2_MBUS_FMT_SGBRG10_1X10:
case V4L2_MBUS_FMT_SGRBG10_1X10:
case V4L2_MBUS_FMT_SRGGB10_1X10:
break;
default:
/* This shouldn't happen */
ret = -EINVAL;
return ret;
}
ov8825_set_mode(client, new_mode_idx);
return ret;
}
static int ov8825_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *id)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
return -EINVAL;
if (id->match.addr != client->addr)
return -ENODEV;
id->ident = V4L2_IDENT_OV8825;
id->revision = 0;
return 0;
}
static int ov8825_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov8825 *ov8825 = to_ov8825(client);
int retval;
switch (ctrl->id) {
case V4L2_CID_CAMERA_FRAME_RATE:
ctrl->value = ov8825->framerate;
break;
case V4L2_CID_CAMERA_FOCUS_MODE:
ctrl->value = ov8825->focus_mode;
break;
case V4L2_CID_GAIN:
ov8825_get_gain(client, &retval, NULL);
ctrl->value = retval;
break;
case V4L2_CID_EXPOSURE:
ov8825_get_exposure(client, &retval, NULL);
ctrl->value = retval;
break;
case V4L2_CID_CAMERA_LENS_POSITION:
ov8825_lens_get_position(client, &ov8825->current_position,
&ov8825->time_to_destination);
ctrl->value = ov8825->current_position;
break;
case V4L2_CID_CAMERA_FLASH_MODE:
ctrl->value = ov8825->flashmode;
break;
case V4L2_CID_FLASH_INTENSITY:
ctrl->value = ov8825->flash_intensity;
break;
case V4L2_CID_FLASH_TIMEOUT:
ctrl->value = ov8825->flash_timeout;
break;
}
return 0;
}
static int ov8825_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov8825 *ov8825 = to_ov8825(client);
int ret = 0;
switch (ctrl->id) {
case V4L2_CID_CAMERA_FRAME_RATE:
if (ctrl->value > FRAME_RATE_30)
return -EINVAL;
ov8825->framerate = ctrl->value;
switch (ov8825->framerate) {
case FRAME_RATE_5:
ov8825->framerate_lo = F24p8(5.0);
ov8825->framerate_hi = F24p8(5.0);
break;
case FRAME_RATE_7:
ov8825->framerate_lo = F24p8(7.0);
ov8825->framerate_hi = F24p8(7.0);
break;
case FRAME_RATE_10:
ov8825->framerate_lo = F24p8(10.0);
ov8825->framerate_hi = F24p8(10.0);
break;
case FRAME_RATE_15:
ov8825->framerate_lo = F24p8(15.0);
ov8825->framerate_hi = F24p8(15.0);
break;
case FRAME_RATE_20:
ov8825->framerate_lo = F24p8(20.0);
ov8825->framerate_hi = F24p8(20.0);
break;
case FRAME_RATE_25:
ov8825->framerate_lo = F24p8(25.0);
ov8825->framerate_hi = F24p8(25.0);
break;
case FRAME_RATE_30:
ov8825->framerate_lo = F24p8(30.0);
ov8825->framerate_hi = F24p8(30.0);
break;
case FRAME_RATE_AUTO:
default:
ov8825->framerate_lo = F24p8(1.0);
ov8825->framerate_hi = F24p8(30.0);
break;
}
ov8825_set_framerate_lo(client, ov8825->framerate_lo);
ov8825_set_framerate_hi(client, ov8825->framerate_hi);
ov8825_set_exposure(client, ov8825->exposure_current);
ov8825_set_gain(client, ov8825->gain_current);
if (ret)
return ret;
break;
case V4L2_CID_CAMERA_FOCUS_MODE:
if (ctrl->value > FOCUS_MODE_MANUAL)
return -EINVAL;
ov8825->focus_mode = ctrl->value;
/*
* Donot start the AF cycle here
* AF Start will be called later in
* V4L2_CID_CAMERA_SET_AUTO_FOCUS only for auto, macro mode
* it wont be called for infinity.
* Donot worry about resolution change for now.
* From userspace we set the resolution first
* and then set the focus mode.
*/
switch (ov8825->focus_mode) {
case FOCUS_MODE_MACRO:
/*
* set the table for macro mode
*/
break;
case FOCUS_MODE_INFINITY:
/*
* set the table for infinity
*/
ret = 0;
break;
case FOCUS_MODE_MANUAL:
/*
* set the table for manual
*/
ret = 0;
break;
default:
ret = 0;
break;
}
if (ret)
return ret;
break;
case V4L2_CID_CAMERA_SET_AUTO_FOCUS:
if (ctrl->value > AUTO_FOCUS_ON)
return -EINVAL;
if (ret)
return ret;
break;
case V4L2_CID_GAIN:
if ((unsigned int)(ctrl->value) > OV8825_GAIN_MAX) {
pr_debug("V4L2_CID_GAIN invalid gain=%u max=%u",
ctrl->value, OV8825_GAIN_MAX);
return -EINVAL;
}
ov8825_set_gain(client, ctrl->value);
break;
case V4L2_CID_EXPOSURE:
if (ctrl->value > OV8825_EXP_MAX)
return -EINVAL;
ov8825_set_exposure(client, ctrl->value);
break;
case V4L2_CID_CAMERA_LENS_POSITION:
if (ctrl->value > OV8825_LENS_MAX)
return -EINVAL;
ov8825_lens_set_position(client, ctrl->value);
break;
case V4L2_CID_CAMERA_FLASH_MODE:
set_flash_mode(client, ctrl->value);
break;
case V4L2_CID_FLASH_INTENSITY:
ov8825->flash_intensity = ctrl->value;
break;
case V4L2_CID_FLASH_TIMEOUT:
ov8825->flash_timeout = ctrl->value;
break;
}
return ret;
}
int set_flash_mode(struct i2c_client *client, int mode)
{
struct ov8825 *ov8825 = to_ov8825(client);
if (ov8825->flashmode == mode)
return 0;
#ifdef CONFIG_VIDEO_AS3643
if ((mode == FLASH_MODE_OFF) || (mode == FLASH_MODE_TORCH_OFF)) {
if (ov8825->flashmode != FLASH_MODE_OFF) {
ov8825_reg_write(client, 0x3B00, 0x00);
as3643_clear_all();
as3643_gpio_toggle(0);
}
} else if (mode == FLASH_MODE_TORCH_ON) {
as3643_gpio_toggle(1);
usleep_range(25, 30);
as3643_set_torch_flash(0x80);
} else if (mode == FLASH_MODE_ON) {
ov8825_reg_write(client, 0x3B00, 0x83);
as3643_gpio_toggle(1);
usleep_range(25, 30);
/* FIXME: timeout should be vts*line_length/1000+constant? */
as3643_set_ind_led(ov8825->flash_intensity / 5,
ov8825->flash_timeout);
} else if (mode == FLASH_MODE_AUTO) {
as3643_gpio_toggle(1);
usleep_range(25, 30);
as3643_set_ind_led(0x80, 30000);
ov8825_reg_write(client, 0x3B00, 0x83);
/* Not yet implemented */
} else {
return -EINVAL;
}
ov8825->flashmode = mode;
#endif
#ifdef CONFIG_GPIO_FLASH
if ((mode == FLASH_MODE_OFF)
|| (mode == FLASH_MODE_TORCH_OFF)) {
gpio_flash_torch_off();
} else if (mode == FLASH_MODE_TORCH_ON) {
gpio_flash_torch_on();
} else if (mode == FLASH_MODE_ON) {
ov8825->flash_timeout =
3 * (ov8825->vts * ov8825->line_length)/1000;
gpio_flash_flash_on(ov8825->flash_timeout);
} else if (mode == FLASH_MODE_AUTO) {
ov8825->flash_timeout =
3 * (ov8825->vts * ov8825->line_length)/1000;
gpio_flash_flash_on(ov8825->flash_timeout);
} else {
return -EINVAL;
}
ov8825->flashmode = mode;
#endif
return 0;
}
static long ov8825_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
int ret = 0;
switch (cmd) {
case VIDIOC_THUMB_SUPPORTED:
{
int *p = arg;
*p = 0; /* no we don't support thumbnail */
break;
}
case VIDIOC_JPEG_G_PACKET_INFO:
{
struct v4l2_jpeg_packet_info *p =
(struct v4l2_jpeg_packet_info *)arg;
p->padded = 0;
p->packet_size = 0;
break;
}
case VIDIOC_SENSOR_G_OPTICAL_INFO:
{
struct v4l2_sensor_optical_info *p =
(struct v4l2_sensor_optical_info *)arg;
/* assuming 67.5 degree diagonal viewing angle */
p->hor_angle.numerator = 5401;
p->hor_angle.denominator = 100;
p->ver_angle.numerator = 3608;
p->ver_angle.denominator = 100;
p->focus_distance[0] = 10; /* near focus in cm */
p->focus_distance[1] = 100; /* optimal focus in cm */
p->focus_distance[2] = -1; /* infinity */
p->focal_length.numerator = 342;
p->focal_length.denominator = 100;
break;
}
case VIDIOC_SENSOR_G_FRAME_INFO:
{
int i, j;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov8825 *ov8825 = to_ov8825(client);
struct v4l2_frame_info *fi_cache, *fi_usr =
(struct v4l2_frame_info *)arg;
ret = -EINVAL;
if (!timespec_valid(&fi_usr->timestamp)) {
fi_usr->flash_mode = ov8825->flashmode;
ov8825_get_gain(client, &fi_usr->an_gain, NULL);
ov8825_get_exposure(client,
&fi_usr->exposure, NULL);
ov8825_lens_get_position(client,
&fi_usr->focus, &i);
ret = 0;
break;
}
j = ov8825->frame_info_size;
fi_cache = &ov8825->frame_info[0];
for (i = 0; i < V4L2_FRAME_INFO_CACHE && j > 0;
i++, j--, fi_cache++) {
if (timespec_equal(&fi_cache->timestamp,
&fi_usr->timestamp))
break;
}
if (j != 0) {
*fi_usr = *fi_cache;
ret = 0;
}
break;
}
case VIDIOC_SENSOR_S_FRAME_INFO:
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov8825 *ov8825 = to_ov8825(client);
struct v4l2_frame_info *fi_dst, *fi_src;
fi_dst = &ov8825->frame_info[0];
fi_src = (struct v4l2_frame_info *)arg;
if (!timespec_valid(&fi_src->timestamp))
break;
if (ov8825->frame_info_size == V4L2_FRAME_INFO_CACHE) {
int i;
for (i = 0; i < V4L2_FRAME_INFO_CACHE - 1; i++)
fi_dst[i] = fi_dst[i + 1];
ov8825->frame_info_size--;
}
fi_dst[ov8825->frame_info_size++] = *fi_src;
break;
}
default:
ret = -ENOIOCTLCMD;
break;
}
return ret;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov8825_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->size > 2)
return -EINVAL;
if (reg->match.addr != client->addr)
return -ENODEV;
reg->size = 2;
if (ov8825_reg_read(client, reg->reg, &reg->val))
return -EIO return 0;
}
static int ov8825_s_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->size > 2)
return -EINVAL;
if (reg->match.addr != client->addr)
return -ENODEV;
if (ov8825_reg_write(client, reg->reg, reg->val))
return -EIO;
return 0;
}
#endif
static int ov8825_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov8825 *ov8825 = to_ov8825(client);
if (0 == on) {
ov8825->mode_idx = OV8825_MODE_MAX;
ov8825->calibrated = 0;
}
return 0;
}
static struct soc_camera_ops ov8825_ops = {
.set_bus_param = ov8825_set_bus_param,
.query_bus_param = ov8825_query_bus_param,
.enum_input = ov8825_enum_input,
.controls = ov8825_controls,
.num_controls = ARRAY_SIZE(ov8825_controls),
};
static int ov8825_init(struct i2c_client *client)
{
struct ov8825 *ov8825 = to_ov8825(client);
int ret = 0;
/* reset */
ov8825_reg_write(client, 0x0103, 0x01);
/* set initial mode */
ov8825->mode_idx = OV8825_MODE_MAX;
ov8825->state = OV8825_STATE_STOP;
/* default settings */
ov8825->framerate = FRAME_RATE_AUTO;
ov8825->focus_mode = FOCUS_MODE_AUTO;
ov8825->gain_current = DEFAULT_GAIN;
memset(&ov8825->exp_read_buf, 0, sizeof(ov8825->exp_read_buf));
memset(&ov8825->gain_read_buf, 0, sizeof(ov8825->gain_read_buf));
memset(&ov8825->lens_read_buf, 0, sizeof(ov8825->lens_read_buf));
/*
* Exposure should be DEFAULT_EXPO * line_length / 1000
* Since we don't have line_length yet, just estimate
*/
ov8825->exposure_current = 10000;
ov8825->exp_read_buf[0] = 6032;
ov8825->exp_read_buf[1] = 6032;
ov8825->gain_read_buf[0] = (ov8825->gain_current & 0x3fff) >> 4;
ov8825->gain_read_buf[1] = (ov8825->gain_current & 0x3fff) >> 4;
ov8825->aecpos_delay = 1;
ov8825->lenspos_delay = 0;
ov8825->flashmode = FLASH_MODE_OFF;
ov8825->flash_intensity = OV8825_FLASH_INTENSITY_DEFAULT;
ov8825->flash_timeout = OV8825_FLASH_TIMEOUT_DEFAULT;
ov8825->position = 0;
ov8825->dac_code = 0;
ov8825->position = 0;
ov8825->timing_step_us = 0xf;
dev_dbg(&client->dev, "Sensor initialized\n");
return ret;
}
/*
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
static int ov8825_video_probe(struct soc_camera_device *icd,
struct i2c_client *client)
{
unsigned long flags;
int ret = 0;
u8 revision = 0, id_high, id_low;
u16 id;
/*
* We must have a parent by now. And it cannot be a wrong one.
* So this entire test is completely redundant.
*/
if (!icd->dev.parent ||
to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
return -ENODEV;
ret = ov8825_reg_read(client, 0x302A, &revision);
if (ret) {
dev_err(&client->dev, "Failure to detect OV8825 chip\n");
goto out;
}
revision &= 0xF;
flags = SOCAM_DATAWIDTH_8;
/* Read sensor Model ID */
ret = ov8825_reg_read(client, OV8825_REG_CHIP_ID_HIGH, &id_high);
if (ret < 0)
return ret;
id = id_high << 8;
ret = ov8825_reg_read(client, OV8825_REG_CHIP_ID_LOW, &id_low);
if (ret < 0)
return ret;
id |= id_low;
if (id != 0x8825)
return -ENODEV;
dev_err(&client->dev, "Detected a OV8825 chip 0x%04x, revision %x\n",
id, revision);
out:
return ret;
}
static void ov8825_video_remove(struct soc_camera_device *icd)
{
dev_dbg(&icd->dev, "Video removed: %p, %p\n",
icd->dev.parent, icd->vdev);
}
static struct v4l2_subdev_core_ops ov8825_subdev_core_ops = {
.g_chip_ident = ov8825_g_chip_ident,
.g_ctrl = ov8825_g_ctrl,
.s_ctrl = ov8825_s_ctrl,
.ioctl = ov8825_ioctl,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov8825_g_register,
.s_register = ov8825_s_register,
#endif
.s_power = ov8825_s_power,
};
static int ov8825_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
if (index >= ARRAY_SIZE(ov8825_fmts))
return -EINVAL;
*code = ov8825_fmts[index].code;
return 0;
}
static int ov8825_enum_framesizes(struct v4l2_subdev *sd,
struct v4l2_frmsizeenum *fsize)
{
if (fsize->index >= OV8825_MODE_MAX)
return -EINVAL;
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->pixel_format = V4L2_PIX_FMT_SRGGB10;
fsize->discrete.width = ov8825_mode[fsize->index].width;
fsize->discrete.height = ov8825_mode[fsize->index].height;
return 0;
}
/* we only support fixed frame rate */
static int ov8825_enum_frameintervals(struct v4l2_subdev *sd,
struct v4l2_frmivalenum *interval)
{
int size;
if (interval->index >= 1)
return -EINVAL;
interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
size = ov8825_find_framesize(interval->width, interval->height);
switch (size) {
case OV8825_MODE_3264x2448P15:
interval->discrete.numerator = 1;
interval->discrete.denominator = 15;
break;
case OV8825_MODE_1280x960P30:
default:
interval->discrete.numerator = 1;
interval->discrete.denominator = 30;
break;
}
return 0;
}
static int ov8825_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov8825 *ov8825 = to_ov8825(client);
struct v4l2_captureparm *cparm;
if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
cparm = &param->parm.capture;
memset(param, 0, sizeof(*param));
param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cparm->capability = V4L2_CAP_TIMEPERFRAME;
switch (ov8825->mode_idx) {
case OV8825_MODE_3264x2448P15:
cparm->timeperframe.numerator = 1;
cparm->timeperframe.denominator = 15;
break;
case OV8825_MODE_1280x960P30:
default:
cparm->timeperframe.numerator = 1;
cparm->timeperframe.denominator = 30;
break;
}
return 0;
}
static int ov8825_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
{
/*
* FIXME: This just enforces the hardcoded framerates until this is
*flexible enough.
*/
return ov8825_g_parm(sd, param);
}
static struct v4l2_subdev_video_ops ov8825_subdev_video_ops = {
.s_stream = ov8825_s_stream,
.s_mbus_fmt = ov8825_s_fmt,
.g_mbus_fmt = ov8825_g_fmt,
.try_mbus_fmt = ov8825_try_fmt,
.enum_mbus_fmt = ov8825_enum_fmt,
.enum_mbus_fsizes = ov8825_enum_framesizes,
.enum_framesizes = ov8825_enum_framesizes,
.enum_frameintervals = ov8825_enum_frameintervals,
.g_parm = ov8825_g_parm,
.s_parm = ov8825_s_parm,
};
static int ov8825_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
{
/* Quantity of initial bad frames to skip. Revisit. */
*frames = 2;
return 0;
}
static int ov8825_g_interface_parms(struct v4l2_subdev *sd,
struct v4l2_subdev_sensor_interface_parms
*parms)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov8825 *ov8825 = to_ov8825(client);
if (!parms)
return -EINVAL;
parms->if_type = ov8825->plat_parms->if_type;
parms->if_mode = ov8825->plat_parms->if_mode;
parms->parms = ov8825->plat_parms->parms;
/* set the hs term time */
parms->parms.serial.hs_term_time = 0;
parms->parms.serial.hs_settle_time = 6;
return 0;
}
static struct v4l2_subdev_sensor_ops ov8825_subdev_sensor_ops = {
.g_skip_frames = ov8825_g_skip_frames,
.g_interface_parms = ov8825_g_interface_parms,
};
static struct v4l2_subdev_ops ov8825_subdev_ops = {
.core = &ov8825_subdev_core_ops,
.video = &ov8825_subdev_video_ops,
.sensor = &ov8825_subdev_sensor_ops,
};
#ifdef CONFIG_VIDEO_A3907
#define A3907_I2C_ADDR 0x18
static struct i2c_board_info a3907_i2c_board_info = {
I2C_BOARD_INFO("a3907", (A3907_I2C_ADDR >> 1))
};
static struct i2c_client *a3907_i2c_client;
static struct i2c_adapter *a3907_i2c_adap;
#endif
static int ov8825_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov8825 *ov8825;
struct soc_camera_device *icd = client->dev.platform_data;
struct soc_camera_link *icl;
int ret;
if (!icd) {
dev_err(&client->dev, "ov8825: missing soc-camera data!\n");
return -EINVAL;
}
icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "ov8825 driver needs platform data\n");
return -EINVAL;
}
if (!icl->priv) {
dev_err(&client->dev,
"ov8825 driver needs i/f platform data\n");
return -EINVAL;
}
ov8825 = kzalloc(sizeof(struct ov8825), GFP_KERNEL);
if (!ov8825)
return -ENOMEM;
v4l2_i2c_subdev_init(&ov8825->subdev, client, &ov8825_subdev_ops);
/* Second stage probe - when a capture adapter is there */
icd->ops = &ov8825_ops;
ov8825->mode_idx = OV8825_MODE_MAX;
ov8825->i_fmt = 0; /* First format in the list */
ov8825->plat_parms = icl->priv;
ov8825->icd = icd;
ret = ov8825_video_probe(icd, client);
if (ret) {
icd->ops = NULL;
kfree(ov8825);
return ret;
}
/* init the sensor here */
ret = ov8825_init(client);
if (ret) {
dev_err(&client->dev, "Failed to initialize sensor\n");
ret = -EINVAL;
}
#ifdef CONFIG_VIDEO_A3907
pr_debug("A3907 i2c start");
a3907_i2c_adap = i2c_get_adapter(0);
if (!a3907_i2c_adap)
pr_debug("A3907 i2c_get_adapter(0) FAILED");
if (a3907_i2c_adap) {
a3907_i2c_client = i2c_new_device(a3907_i2c_adap,
&a3907_i2c_board_info);
i2c_put_adapter(a3907_i2c_adap);
pr_debug("A3907 i2c_get_adapter(0) OK");
}
#endif
return ret;
}
static int ov8825_remove(struct i2c_client *client)
{
struct ov8825 *ov8825 = to_ov8825(client);
struct soc_camera_device *icd = client->dev.platform_data;
pr_debug(" remove");
#ifdef CONFIG_VIDEO_A3907
pr_debug("A3907 i2c_unregister_device");
if (a3907_i2c_client)
i2c_unregister_device(a3907_i2c_client);
#endif
icd->ops = NULL;
ov8825_video_remove(icd);
client->driver = NULL;
kfree(ov8825);
return 0;
}
static const struct i2c_device_id ov8825_id[] = {
{"ov8825", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ov8825_id);
static struct i2c_driver ov8825_i2c_driver = {
.driver = {
.name = "ov8825",
},
.probe = ov8825_probe,
.remove = ov8825_remove,
.id_table = ov8825_id,
};
static int __init ov8825_mod_init(void)
{
return i2c_add_driver(&ov8825_i2c_driver);
}
static void __exit ov8825_mod_exit(void)
{
i2c_del_driver(&ov8825_i2c_driver);
}
module_init(ov8825_mod_init);
module_exit(ov8825_mod_exit);
MODULE_DESCRIPTION("OmniVision OV8825 Camera driver");
MODULE_AUTHOR("Broadcom Corporation");
MODULE_LICENSE("GPL v2");