blob: 8fa1aad873226ec4113df28f1943b89e1e2cf755 [file] [log] [blame]
/*
* OmniVision OV5648 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/printk.h>
#include <linux/proc_fs.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
#include <media/soc_camera.h>
#include <linux/videodev2_brcm.h>
#ifdef CONFIG_VIDEO_A3907
#include <media/a3907.h>
#endif
#ifdef CONFIG_VIDEO_DW9714
#include <media/dw9714.h>
#endif
#include "flash_lamp.h"
#include "ov5648.h"
#define SENSOR_NAME_STR "ov5648"
#define OV5648_DEBUG 0
#define OV5648_DEBUGFS 1
#define OV5648_MCLK_26MHZ
static int Is_SnapToPrev_expo;
static int Is_SnapToPrev_gain;
/* OV5648 has only one fixed colorspace per pixelcode */
struct ov5648_datafmt {
enum v4l2_mbus_pixelcode code;
enum v4l2_colorspace colorspace;
};
static const struct ov5648_datafmt ov5648_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 ov5648_mode {
OV5648_MODE_1280x720P30 = 0,
OV5648_MODE_1280x960P30 = 1,
#ifdef CONFIG_ARCH_JAVA
OV5648_MODE_1920x1080P30 = 2,
OV5648_MODE_2592x1944P15 = 3,
OV5648_MODE_MAX = 4,
#else
OV5648_MODE_2592x1944P15 = 2,
OV5648_MODE_MAX = 3,
#endif
};
enum ov5648_state {
OV5648_STATE_STOP = 0,
OV5648_STATE_STRM = 1,
OV5648_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;
};
struct sensor_mode ov5648_mode[OV5648_MODE_MAX + 1] = {
{
.name = "1280x720P30",
.height = 720,
.width = 1280,
.hts = 1912,
.vts = 1496,
.vts_max = 32767 - 6,
.line_length_ns = 22285,
.bayer = BAYER_BGGR,
.bpp = 10,
.fps = F24p8(30.0),
},
{
.name = "1280x960P30",
.height = 960,
.width = 1280,
.hts = 1912,
.vts = 1496,
.vts_max = 32767 - 6,
.line_length_ns = 22285,
.bayer = BAYER_BGGR,
.bpp = 10,
.fps = F24p8(30.0),
},
#ifdef CONFIG_ARCH_JAVA
{
.name = "1920x1080P30",
.height = 1080,
.width = 1920,
.hts = 2554,
.vts = 1120,
.vts_max = 32767 - 6,
.line_length_ns = 29767,
.bayer = BAYER_BGGR,
.bpp = 10,
.fps = F24p8(30.0),
},
#endif
{
.name = "2592x1944P15",
.height = 1944,
.width = 2592,
.hts = 2844,
.vts = 1968,
.vts_max = 32767 - 6,
.line_length_ns = 33148,
.bayer = BAYER_BGGR,
.bpp = 10,
.fps = F24p8(15.0),
},
{
.name = "STOPPED",
.line_length_ns = 22190,
}
};
/**
*struct ov5648_otp - ov5648 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 OV5648 OTP calibration data
*/
struct ov5648_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;
};
#define EXP_DELAY_MAX 8
#define GAIN_DELAY_MAX 8
#define LENS_READ_DELAY 4
#define FLASH_DELAY_MAX 4
#define V4L2_FRAME_INFO_CACHE 4
struct ov5648 {
struct v4l2_subdev subdev;
struct v4l2_subdev_sensor_interface_parms *plat_parms;
struct v4l2_ctrl_handler hdl;
struct soc_camera_device *icd;
struct proc_dir_entry *proc_entry;
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 coarse_int_lines;
int agc_on;
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;
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 af_on;
int flash_mode;
int flash_intensity;
int flash_timeout;
int flash_delay;
int flash_on;
int flash_int_buf[FLASH_DELAY_MAX];
int flash_mod_buf[FLASH_DELAY_MAX];
struct flash_lamp_s lamp;
struct ov5648_otp otp;
int calibrated;
int frame_info_size;
struct v4l2_frame_info frame_info[V4L2_FRAME_INFO_CACHE];
int fps, fcnt, fps_t0;
};
/**
*struct ov5648_reg - ov5648 register format
*@reg: 16-bit offset to register
*@val: 8/16/32-bit register value
*@length: length of the register
*
* Define a structure for OV5648 register initialization values
*/
struct ov5648_reg {
u16 reg;
u8 val;
u8 pad;
};
static const struct ov5648_reg ov5648_reginit[256] = {
{0x0103, 0x01},
{0x3001, 0x00},
{0x3002, 0x00},
{0x3011, 0x02},
{0x3017, 0x05},
{0x3018, 0x4c},
{0x301c, 0xd2},
{0x3022, 0x00},
{0x3034, 0x1a},
{0x3035, 0x21},
#ifdef OV5648_MCLK_26MHZ
{0x3036, 0x63}, /* MCLK=26MHz MIPI=429MBs(214.5Mb*2) Lanes FPS=30 */
#else
{0x3036, 0x69}, /* MCLK=24MHz MIPI=420MBs*2 Lanes FPS=30 */
#endif
{0x3037, 0x03},
{0x3038, 0x00},
{0x3039, 0x00},
{0x303a, 0x00},
{0x303b, 0x19},
{0x303c, 0x11},
{0x303d, 0x30},
{0x3105, 0x11},
{0x3106, 0x05},
{0x3304, 0x28},
{0x3305, 0x41},
{0x3306, 0x30},
{0x3308, 0x00},
{0x3309, 0xc8},
{0x330a, 0x01},
{0x330b, 0x90},
{0x330c, 0x02},
{0x330d, 0x58},
{0x330e, 0x03},
{0x330f, 0x20},
{0x3300, 0x00},
{0x3500, 0x00},
{0x3501, 0x7b},
{0x3502, 0x00},
{0x3503, 0x07},
{0x350a, 0x00},
{0x350b, 0x40},
{0x3601, 0x33},
{0x3602, 0x00},
{0x3611, 0x0e},
{0x3612, 0x2b},
{0x3614, 0x50},
{0x3620, 0x33},
{0x3622, 0x00},
{0x3630, 0xad},
{0x3631, 0x00},
{0x3632, 0x94},
{0x3633, 0x17},
{0x3634, 0x14},
{0x3704, 0xc0},
{0x3705, 0x2a},
{0x3708, 0x63},
{0x3709, 0x12},
{0x370b, 0x23},
{0x370c, 0xc0},
{0x370d, 0x00},
{0x370e, 0x00},
{0x371c, 0x07},
{0x3739, 0xd2},
{0x373c, 0x00},
{0x3800, 0x00},
{0x3801, 0x00},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3804, 0x0a},
{0x3805, 0x3f},
{0x3806, 0x07},
{0x3807, 0xa3},
{0x3808, 0x0a},
{0x3809, 0x20},
{0x380a, 0x07},
{0x380b, 0x98},
{0x380c, 0x0b},
{0x380d, 0x00},
{0x380e, 0x07},
{0x380f, 0xc0},
{0x3810, 0x00},
{0x3811, 0x10},
{0x3812, 0x00},
{0x3813, 0x06},
{0x3814, 0x11},
{0x3815, 0x11},
{0x3817, 0x00},
{0x3820, 0x40},
{0x3821, 0x06},
{0x3826, 0x03},
{0x3829, 0x00},
{0x382b, 0x0b},
{0x3830, 0x00},
{0x3836, 0x00},
{0x3837, 0x00},
{0x3838, 0x00},
{0x3839, 0x04},
{0x383a, 0x00},
{0x383b, 0x01},
{0X3A00, 0X58},
{0x3b00, 0x00},
{0x3b02, 0x08},
{0x3b03, 0x00},
{0x3b04, 0x04},
{0x3b05, 0x00},
{0x3b06, 0x04},
{0x3b07, 0x08},
{0x3b08, 0x00},
{0x3b09, 0x02},
{0x3b0a, 0x04},
{0x3b0b, 0x00},
{0x3b0c, 0x3d},
{0x3f01, 0x0d},
{0x3f0f, 0xf5},
{0x4000, 0x89},
{0x4001, 0x02},
{0x4002, 0x45},
{0x4004, 0x04},
{0x4005, 0x18},
{0x4006, 0x08},
{0x4007, 0x10},
{0x4008, 0x00},
{0x4050, 0x6e},
{0x4051, 0x8f},
{0x4300, 0xf8},
{0x4303, 0xff},
{0x4304, 0x00},
{0x4307, 0xff},
{0x4520, 0x00},
{0x4521, 0x00},
{0x4511, 0x22},
{0x4801, 0x0f},
{0x4814, 0x2a},
{0x481f, 0x3c},
{0x4823, 0x3c},
{0x4826, 0x00},
{0x481b, 0x3c},
{0x4827, 0x32},
{0x4837, 0x18},
{0x4b00, 0x06},
{0x4b01, 0x0a},
{0x4b04, 0x10},
{0x5000, 0xff},
{0x5001, 0x00},
{0x5002, 0x41},
{0x5003, 0x0a},
{0x5004, 0x00},
{0x5043, 0x00},
{0x5013, 0x00},
{0x501f, 0x03},
{0x503d, 0x00},
{0x5a00, 0x08},
{0x5b00, 0x01},
{0x5b01, 0x40},
{0x5b02, 0x00},
{0x5b03, 0xf0},
{0x0100, 0x01},
{0xFFFF, 0x00} /* end of the list */
};
static const struct ov5648_reg ov5648_regdif[OV5648_MODE_MAX][32] = {
{
/* to 1280x720P30 */
{0x301a, 0xf1},
{0x3708, 0x66},
{0x3709, 0x52},
{0x370c, 0xc3},
{0x3800, 0x00},
{0x3801, 0x10},
{0x3802, 0x00},
{0x3803, 0xfe},
{0x3804, 0x0a},
{0x3805, 0x2f},
{0x3806, 0x06},
{0x3807, 0xa5},
{0x3808, 0x05},
{0x3809, 0x00},
{0x380a, 0x02},
{0x380b, 0xd0},
{0x380c, 0x07},
{0x380d, 0x78},
{0x380e, 0x05},
{0x380f, 0xd8},
{0x3810, 0x00},
{0x3811, 0x08},
{0x3812, 0x00},
{0x3813, 0x02},
{0x3814, 0x31},
{0x3815, 0x31},
#ifdef CONFIG_MACH_JAVA_C_5606
{0x3820, 0x00},
{0x3821, 0x07},
#else
{0x3820, 0x0e},
{0x3821, 0x01},
#endif
{0x4004, 0x02},
{0x4005, 0x1a},
{0x301a, 0xf0},
{0xFFFF, 0x00}
},
{
/* to 1280x960P30 */
{0x301a, 0xf1},
{0x3708, 0x66},
{0x3709, 0x52},
{0x370c, 0xc3},
{0x3800, 0x00},
{0x3801, 0x10},
{0x3802, 0x00},
{0x3803, 0x06},
{0x3804, 0x0a},
{0x3805, 0x2f},
{0x3806, 0x07},
{0x3807, 0x9d},
{0x3808, 0x05},
{0x3809, 0x00},
{0x380a, 0x03},
{0x380b, 0xc0},
{0x380c, 0x07},
{0x380d, 0x78},
{0x380e, 0x05},
{0x380f, 0xd8},
{0x3810, 0x00},
{0x3811, 0x08},
{0x3812, 0x00},
{0x3813, 0x06},
{0x3814, 0x31},
{0x3815, 0x31},
#ifdef CONFIG_MACH_JAVA_C_5606
{0x3820, 0x00},
{0x3821, 0x07},
#else
{0x3820, 0x0e},
{0x3821, 0x01},
#endif
{0x4004, 0x02},
{0x4005, 0x1a},
{0x301a, 0xf0},
{0xFFFF, 0x00}
},
#ifdef CONFIG_ARCH_JAVA
{
/* to 1920x1080P30 */
{0x301a, 0xf1},
{0x3708, 0x63},
{0x3709, 0x12},
{0x370c, 0xc0},
{0x3800, 0x01},
{0x3801, 0x50},
{0x3802, 0x01},
{0x3803, 0xb2},
{0x3804, 0x08},
{0x3805, 0xef},
{0x3806, 0x05},
{0x3807, 0xf1},
{0x3808, 0x07},
{0x3809, 0x80},
{0x380a, 0x04},
{0x380b, 0x38},
{0x380c, 0x09},
{0x380d, 0xfa},
{0x380e, 0x04},
{0x380f, 0x60},
{0x3810, 0x00},
{0x3811, 0x10},
{0x3812, 0x00},
{0x3813, 0x04},
{0x3814, 0x11},
{0x3815, 0x11},
#ifdef CONFIG_MACH_JAVA_C_5606
{0x3820, 0x00},
{0x3821, 0x07},
#else
{0x3820, 0x46},
{0x3821, 0x00},
#endif
{0x4004, 0x04},
{0x4005, 0x18},
{0x301a, 0xf0},
{0xFFFF, 0x00}
},
#endif
{
/* to 2592x1944P15 */
{0x301a, 0xf1},
{0x3708, 0x63},
{0x3709, 0x12},
{0x370c, 0xc0},
{0x3800, 0x00},
{0x3801, 0x00},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3804, 0x0a},
{0x3805, 0x3f},
{0x3806, 0x07},
{0x3807, 0xa3},
{0x3808, 0x0a},
{0x3809, 0x20},
{0x380a, 0x07},
{0x380b, 0x98},
{0x380c, 0x0b},
{0x380d, 0x1c},
{0x380e, 0x07},
{0x380f, 0xb0},
{0x3810, 0x00},
{0x3811, 0x10},
{0x3812, 0x00},
{0x3813, 0x06},
{0x3814, 0x11},
{0x3815, 0x11},
#ifdef CONFIG_MACH_JAVA_C_5606
{0x3820, 0x00},
{0x3821, 0x06},
#else
{0x3820, 0x06},
{0x3821, 0x00},
#endif
{0x4004, 0x04},
{0x4005, 0x1a},
{0x301a, 0xf0},
{0xFFFF, 0x00}
}
};
static const struct ov5648_reg ov5648_reg_state[OV5648_STATE_MAX][3] = {
{ /* to power down */
{0x0100, 0x00}, /* disable streaming */
{0x3018, 0x5c}, /* disable mipi */
{0xFFFF, 0x00}
},
{ /* to streaming */
{0x3018, 0x4c}, /* enable mipi */
{0x0100, 0x01}, /* enable streaming */
{0xFFFF, 0x00}
},
};
static int ov5648_set_flash_mode(struct i2c_client *client, int mode);
static int ov5648_set_mode(struct i2c_client *client, int new_mode_idx);
static int ov5648_set_state(struct i2c_client *client, int new_state);
static int ov5648_init(struct i2c_client *client);
/*
#ifdef CONFIG_VIDEO_AS3643
#define flash_gpio_on() as3643_gpio_strobe(1)
#define flash_gpio_off() as3643_gpio_strobe(0)
#define torch_gpio_on() as3643_gpio_strobe(1)
#define torch_gpio_off() as3643_gpio_strobe(0)
#define flash_start(intensity, timeout) as3643_set_flash(intensity, timeout)
#define flash_stop() as3643_clear_all()
#define torch_start()
#define torch_stop() as3643_clear_all()
#elif defined CONFIG_MACH_JAVA_C_LC1 || \
defined CONFIG_MACH_JAVA_C_5609A || \
defined CONFIG_MACH_JAVA_C_5606
#define TORCH_EN (10)
#define FLASH_EN (11)
#define flash_gpio_on() \
do { \
gpio_set_value(FLASH_EN, 1); \
gpio_set_value(TORCH_EN, 1); \
} while (0)
#define flash_gpio_off() \
do { \
gpio_set_value(FLASH_EN, 0); \
gpio_set_value(TORCH_EN, 0); \
} while (0)
#define torch_gpio_on() gpio_set_value(TORCH_EN, 1)
#define torch_gpio_off() gpio_set_value(TORCH_EN, 0)
#define flash_start(intensity, timeout)
#define flash_stop()
#define torch_start()
#define torch_stop()
#else
#define flash_gpio_on()
#define flash_gpio_off()
#define torch_gpio_on()
#define torch_gpio_off()
#define flash_start(intensity, timeout)
#define flash_stop()
#define torch_start()
#define torch_stop()
#endif
*/
/*
* Find a data format by a pixel code in an array
*/
static int ov5648_find_datafmt(enum v4l2_mbus_pixelcode code)
{
int i;
for (i = 0; i < ARRAY_SIZE(ov5648_fmts); i++)
if (ov5648_fmts[i].code == code)
break;
/* If not found, select latest */
if (i >= ARRAY_SIZE(ov5648_fmts))
i = ARRAY_SIZE(ov5648_fmts) - 1;
return i;
}
/* Find a frame size in an array */
static int ov5648_find_framesize(u32 width, u32 height)
{
int i;
for (i = 0; i < OV5648_MODE_MAX; i++) {
if ((ov5648_mode[i].width >= width) &&
(ov5648_mode[i].height >= height))
break;
}
/* If not found, select biggest */
if (i >= OV5648_MODE_MAX)
i = OV5648_MODE_MAX - 1;
return i;
}
static struct ov5648 *to_ov5648(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client), struct ov5648, subdev);
}
/**
*ov5648_reg_read - Read a value from a register in an ov5648 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 ov5648 sensor device.
* The value is returned in 'val'.
* Returns zero if successful, or non-zero otherwise.
*/
static int ov5648_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 ov5648 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 ov5648_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 ov5648_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 int ov5648_s_ctrl(struct v4l2_ctrl *);
static int ov5648_g_ctrl(struct v4l2_ctrl *);
static int ov5648_try_ctrl(struct v4l2_ctrl *);
static struct v4l2_ctrl_ops ov5648_ctrl_ops = {
.s_ctrl = ov5648_s_ctrl,
.g_volatile_ctrl = ov5648_g_ctrl,
.try_ctrl = ov5648_try_ctrl,
};
static const struct v4l2_ctrl_config ov5648_controls[] = {
{
.ops = &ov5648_ctrl_ops,
.id = V4L2_CID_CAMERA_LENS_POSITION,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Lens Position",
.min = OV5648_LENS_MIN,
.max = OV5648_LENS_MAX,
.step = OV5648_LENS_STEP,
.def = DEFAULT_LENS_POS,
.flags = 0,
},
{
.ops = &ov5648_ctrl_ops,
.id = V4L2_CID_CAMERA_FRAME_RATE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Framerate control",
.min = FRAME_RATE_AUTO,
.max = (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,
.def = FRAME_RATE_AUTO,
.flags = 0,
},
#if 0
{
.ops = &ov5648_ctrl_ops,
.id = V4L2_CID_CAMERA_SET_AUTO_FOCUS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "AF start/stop",
.min = AUTO_FOCUS_OFF,
.max = AUTO_FOCUS_ON,
.step = 1,
.def = AUTO_FOCUS_OFF,
.flags = 0,
},
#endif
#if 0
{
.ops = &ov5648_ctrl_ops,
.id = V4L2_CID_CAMERA_FLASH_MODE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "AS3643-flash",
.min = FLASH_MODE_OFF,
.max = (1 << FLASH_MODE_OFF) | (1 << FLASH_MODE_ON) |
(1 << FLASH_MODE_TORCH_OFF) |
(1 << FLASH_MODE_TORCH_ON),
.step = 1,
.def = FLASH_MODE_OFF,
.flags = 0,
},
#endif
{
.ops = &ov5648_ctrl_ops,
.id = V4L2_CID_CAMERA_FOCUS_MODE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Focus Modes",
.min = FOCUS_MODE_AUTO,
.max = (1 << FOCUS_MODE_AUTO | 1 << FOCUS_MODE_MACRO |
1 << FOCUS_MODE_INFINITY | 1 << FOCUS_MODE_MANUAL),
.step = 1,
.def = FOCUS_MODE_AUTO,
.flags = 0,
},
{
.ops = &ov5648_ctrl_ops,
.id = V4L2_CID_CAM_MOUNTING,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Sensor mounting",
.min = 0,
.max = 0x10 | 0x20 | 0x40, /* (1 << V4L2_IN_ST_HFLIP |
1 << V4L2_IN_ST_VFLIP |
1 << V4L2_IN_ST_BACK), */
.step = 1,
.def = 0,
.flags = V4L2_CTRL_FLAG_VOLATILE,
},
};
/**
* Initialize a list of ov5648 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 ov5648_reg_writes(struct i2c_client *client,
const struct ov5648_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 |= ov5648_reg_write(client, reglist[i].reg,
reglist[i].val);
}
if (err != 0)
break;
}
return 0;
}
/**
* Write an array of data to ov5648 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 ov5648_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;
}
static const struct ov5648_reg ov5648_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 ov5648_otp_bank_read(struct i2c_client *client, int bank, u8 *buf)
{
int ret, i;
u8 val;
pr_debug("ov5648_otp_bank_read: bank=%d\n", bank);
ret = ov5648_reg_writes(client, ov5648_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 |= ov5648_reg_read(client, 0x3d00+i, &val);
buf[i] = val;
}
pr_debug("ov5648_otp_bank_read: OK bank=%d\n", bank);
return 0;
}
/**
* Print ov5648 OTP calibration data
*@otp: Address of otp structure to print
*@index: OTP index
*/
#if OV5648_DEBUG
static void ov5648_otp_print(struct ov5648_otp *otp, int index)
{
pr_debug("ov5648: OTP index=%d\n", index);
pr_debug("ov5648: integr_id = 0x%02X\n", otp->module_integrator_id);
pr_debug("ov5648: lens_id = 0x%02X\n", otp->lens_id);
pr_debug("ov5648: rg_ratio = 0x%04X\n", otp->rg_ratio);
pr_debug("ov5648: bg_ratio = 0x%04X\n", otp->bg_ratio);
pr_debug("ov5648: light_rg = 0x%04X\n", otp->light_rg);
pr_debug("ov5648: light_bg = 0x%04X\n", otp->light_bg);
pr_debug("ov5648: user_data = [0x%02X 0x%02X]\n",
otp->user_data[0], otp->user_data[1]);
}
/**
* Read and print ov5648 OTP R/B channel gains.
*@client: i2c driver client structure.
*/
static void ov5648_rbgains_print(struct i2c_client *client)
{
u8 val;
u16 gainr, gaing, gainb;
int i;
ov5648_reg_read(client, 0x5001, &val);
pr_debug("ov5648 0x%04X=0x%02X]\n", 0x5001, val);
ov5648_reg_read(client, 0x5002, &val);
pr_debug("ov5648 0x%04X=0x%02X]\n", 0x5002, val);
ov5648_reg_read(client, 0x5180, &val);
pr_debug("ov5648 0x%04X=0x%02X]\n", 0x5180, val);
ov5648_reg_read(client, 0x5186, &val);
gainr = val << 8;
ov5648_reg_read(client, 0x5187, &val);
gainr += val;
ov5648_reg_read(client, 0x5188, &val);
gaing = val << 8;
ov5648_reg_read(client, 0x5189, &val);
gaing += val;
ov5648_reg_read(client, 0x518a, &val);
gainb = val << 8;
ov5648_reg_read(client, 0x518b, &val);
gainb += val;
pr_debug("ov5648 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 ov5648_buf2otp(struct ov5648_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 ov5648 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 ov5648_otp_index_read(struct i2c_client *client,
struct ov5648_otp *otp, int index)
{
int ret = -1;
u8 buf[32];
pr_debug("ov5648_otp_index_read: index %d OTP read\n", index);
memset(otp, 0, sizeof(*otp));
switch (index) {
case 0:
ret = ov5648_otp_bank_read(client, 0, buf);
if (ret < 0)
break;
ov5648_buf2otp(otp, buf+5);
break;
case 1:
ret = ov5648_otp_bank_read(client, 0, buf);
break;
if (ret < 0)
break;
ov5648_buf2otp(otp, buf+0xe);
break;
case 2:
ret = ov5648_otp_bank_read(client, 0, buf);
if (ret < 0)
break;
ov5648_buf2otp(otp, buf+7);
break;
default:
pr_debug("ov5648_otp_index_read: invalid OTP idx: %d\n",
index);
ret = -1;
break;
}
pr_debug("ov5648_otp_index_read: rg_ratio = 0x%04X\n", otp->rg_ratio);
pr_debug("ov5648_otp_index_read: bg_ratio = 0x%04X\n", otp->bg_ratio);
#if OV5648_DEBUG
ov5648_otp_print(otp, index);
#endif
if (ret != 0) {
pr_debug("ov5648_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 ov5648 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 ov5648_otp_read(struct i2c_client *client)
{
int i, ret;
struct ov5648 *ov5648 = to_ov5648(client);
struct ov5648_otp *otp = &(ov5648->otp);
for (i = 0; i < 3; i++) {
ret = ov5648_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 ov5648_rbgains_update(struct i2c_client *client)
{
#define RG_GOLDEN 0x145
#define BG_GOLDEN 0x15e
u16 gainr, gaing, gainb, ggainb, ggainr;
int ret;
struct ov5648 *ov5648 = to_ov5648(client);
struct ov5648_otp *otp = &(ov5648->otp);
if (otp->rg_ratio == 0 || otp->bg_ratio == 0) {
pr_debug("ov5648_rbgains_update: OTP not initialized\n");
return -1;
}
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; }
}
}
ret = ov5648_reg_write(client, 0x5180, 1<<3);
ret |= ov5648_reg_write(client, 0x5186, gainr >> 8);
ret |= ov5648_reg_write(client, 0x5187, gainr & 0xff);
ret |= ov5648_reg_write(client, 0x5188, gaing >> 8);
ret |= ov5648_reg_write(client, 0x5189, gaing & 0xff);
ret |= ov5648_reg_write(client, 0x518a, gainb >> 8);
ret |= ov5648_reg_write(client, 0x518b, gainb & 0xff);
if (ret == 0) {
dev_info(&client->dev, "ov5648 1 sensor update for calibrated data g=[0x%04X, 0x%04X, 0x%04X]\n",
gainr, gaing, gainb);
#if OV5648_DEBUG
ov5648_rbgains_print(client);
#endif
return 0;
} else {
dev_info(&client->dev,
"ov5648 sensor 1 update for calibrated data failed\n");
return -1;
}
}
/*
*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 ov5648_lens_set_position(struct i2c_client *client,
int target_position)
{
int ret = 0;
#ifdef CONFIG_VIDEO_A3907
if (target_position & 0x80000000) {
int fine_target_position = target_position & ~0x80000000;
ret = a3907_lens_set_position_fine(fine_target_position);
} else {
ret = a3907_lens_set_position(target_position);
}
#endif
#ifdef CONFIG_VIDEO_DW9714
if (target_position & 0x80000000) {
int fine_target_position = target_position & ~0x80000000;
ret = dw9714_lens_set_position_fine(fine_target_position);
} else {
ret = dw9714_lens_set_position(target_position);
}
#endif
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 ov5648_lens_get_position(struct i2c_client *client,
int *current_position,
int *time_to_destination)
{
int ret = 0;
int i = 0;
struct ov5648 *ov5648 = to_ov5648(client);
static int lens_read_buf[LENS_READ_DELAY];
#ifdef CONFIG_VIDEO_A3907
ret = a3907_lens_get_position(current_position, time_to_destination);
#endif
#ifdef CONFIG_VIDEO_DW9714
ret = dw9714_lens_get_position(current_position, time_to_destination);
#endif
for (i = 0; i < ov5648->lenspos_delay; i++)
lens_read_buf[i] = lens_read_buf[i + 1];
lens_read_buf[ov5648->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 ov5648_set_framerate_lo(struct i2c_client *client, int framerate)
{
struct ov5648 *ov5648 = to_ov5648(client);
framerate = max(framerate, ov5648->framerate_lo_absolute);
framerate = min(framerate, ov5648->framerate_hi_absolute);
ov5648->framerate_lo = framerate;
ov5648->vts_max = 1000000000
/ (unsigned long)((ov5648->line_length * framerate) >> 8);
ov5648->vts_max = min(ov5648->vts_max,
ov5648_mode[ov5648->mode_idx].vts_max);
pr_debug("ov5648_set_framerate_lo: Setting frame rate lo %d vts_max %d",
ov5648->framerate_lo, ov5648->vts_max);
}
/*
* Setup the sensor integration and frame length based on requested maximum
* framerate, famerate is 24p8 fixed point.
*/
static void ov5648_set_framerate_hi(struct i2c_client *client, int framerate)
{
struct ov5648 *ov5648 = to_ov5648(client);
framerate = max(framerate, ov5648->framerate_lo_absolute);
framerate = min(framerate, ov5648->framerate_hi_absolute);
ov5648->framerate_hi = framerate;
ov5648->vts_min = 1000000000
/ (unsigned long)((ov5648->line_length * framerate) >> 8);
ov5648->vts_min = max(ov5648->vts_min,
ov5648_mode[ov5648->mode_idx].vts);
pr_debug("ov5648_set_framerate_hi: Setting frame rate hi %d vts_min %d",
ov5648->framerate_hi, ov5648->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 ov5648_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 ov5648_set_gain(struct i2c_client *client, int gain_value)
{
struct ov5648 *ov5648 = to_ov5648(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 = ov5648_calc_gain(client, gain_value, &gain_code_analog);
pr_debug("%s(): cur=%u req=%u act=%u cod=%u\n",
__func__,
ov5648->gain_current, gain_value,
gain_actual, gain_code_analog);
ret = ov5648_reg_write(client,
OV5648_REG_AGC_HI, (gain_code_analog >> 8) & 0x3);
ret |= ov5648_reg_write(client,
OV5648_REG_AGC_LO, gain_code_analog & 0xFF);
ov5648->gain_current = gain_actual;
return 0;
}
static int ov5648_get_gain(struct i2c_client *client,
int *gain_value_p, int *gain_code_p)
{
struct ov5648 *ov5648 = to_ov5648(client);
int ret = 0;
int gain_code;
int i;
/*
u8 gain_buf[2];
ov5648_reg_read_multi(client, OV5648_REG_AGC_HI, gain_buf, 2);
gain_code = ((gain_buf[0] & 0x3f) << 8) + gain_buf[1];*/
gain_code = ov5648->gain_current >> 4;
if (Is_SnapToPrev_gain == 1) {
gain_code = ov5648->gain_read_buf[0];
Is_SnapToPrev_gain = 0;
}
if (ov5648->aecpos_delay > 0) {
ov5648->gain_read_buf[ov5648->aecpos_delay] = gain_code;
gain_code = ov5648->gain_read_buf[0];
for (i = 0; i < GAIN_DELAY_MAX-1; i++)
ov5648->gain_read_buf[i] = ov5648->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 ov5648_get_exposure(struct i2c_client *client,
int *exp_value_p, int *exp_code_p)
{
struct ov5648 *ov5648 = to_ov5648(client);
int i, ret = 0;
int exp_code;
/*
u8 exp_buf[3];
ov5648_reg_read_multi(client, OV5648_REG_EXP_HI, exp_buf, 3);
exp_code =
((exp_buf[0] & 0xf) << 16) +
((exp_buf[1] & 0xff) << 8) +
(exp_buf[2] & 0xf0);*/
exp_code = (ov5648->exposure_current * 1000 / ov5648->line_length) << 4;
if (Is_SnapToPrev_expo == 1) {
exp_code = ov5648->exp_read_buf[0];
Is_SnapToPrev_expo = 0;
}
if (ov5648->aecpos_delay > 0) {
ov5648->exp_read_buf[ov5648->aecpos_delay] = exp_code;
exp_code = ov5648->exp_read_buf[0];
for (i = 0; i < EXP_DELAY_MAX-1; i++)
ov5648->exp_read_buf[i] = ov5648->exp_read_buf[i+1];
}
if (exp_code_p)
*exp_code_p = exp_code;
if (exp_value_p)
*exp_value_p = ((exp_code >> 4) * ov5648->line_length) /
(unsigned long)1000;
return ret;
}
#define INTEGRATION_MIN 1
#define INTEGRATION_OFFSET 10
static int ov5648_calc_exposure(struct i2c_client *client,
int exposure_value,
unsigned int *vts_ptr,
unsigned int *coarse_int_lines)
{
struct ov5648 *ov5648 = to_ov5648(client);
unsigned int integration_lines;
int vts = ov5648_mode[ov5648->mode_idx].vts;
integration_lines = (1000 * exposure_value) / ov5648->line_length;
if (integration_lines < INTEGRATION_MIN)
integration_lines = INTEGRATION_MIN;
else if (integration_lines > (unsigned int)
(ov5648->vts_max - INTEGRATION_OFFSET))
integration_lines = ov5648->vts_max - INTEGRATION_OFFSET;
vts = min(integration_lines + INTEGRATION_OFFSET,
(unsigned int)ov5648_mode[ov5648->mode_idx].vts_max);
vts = max_t(u16, vts, ov5648_mode[ov5648->mode_idx].vts);
vts = max_t(u16, vts, ov5648->vts_min);
if (coarse_int_lines)
*coarse_int_lines = integration_lines;
if (vts_ptr)
*vts_ptr = vts;
exposure_value = integration_lines * ov5648->line_length /
(unsigned long)1000;
pr_debug("%s(): integration_lines=%d vts_min=%d mode_vts_max=%d",
__func__,
integration_lines,
ov5648->vts_min,
ov5648_mode[ov5648->mode_idx].vts_max);
return exposure_value;
}
/*
* Setup the sensor integration and frame length based on requested exposure
* in microseconds.
*/
#define EXP_HIST_MAX 0
static void ov5648_set_exposure(struct i2c_client *client, int exp_value)
{
struct ov5648 *ov5648 = to_ov5648(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 = ov5648_calc_exposure(client, exp_value,
&vts, &coarse_int_lines);
pr_debug("ov5648_set_exposure: cur=%d req=%d act=%d coarse=%d vts=%d\n",
ov5648->exposure_current, exp_value, actual_exposure,
coarse_int_lines, vts);
ov5648->vts = vts;
ov5648->exposure_current = actual_exposure;
ov5648->coarse_int_lines = coarse_int_lines;
coarse_int_lines <<= 4;
exp_buf[0] = (u8)((OV5648_REG_EXP_HI >> 8) & 0xFF);
exp_buf[1] = (u8)(OV5648_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 = ov5648_array_write(client, exp_buf, 5);
ret |= ov5648_reg_write(client,
OV5648_REG_TIMING_VTS_HI, (vts & 0xFF00) >> 8);
ret |= ov5648_reg_write(client, OV5648_REG_TIMING_VTS_LO, vts & 0xFF);
if (ret) {
pr_debug("ov5648_set_exposure: error writing exp. ctrl");
return;
}
}
/*
*
*/
static int ov5648_frame_irq(struct v4l2_subdev *sd, u32 irq)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5648 *ov5648 = to_ov5648(client);
int i;
/* assume frame end irq only */
if (0 == ov5648->flash_on) {
if (ov5648->flash_mode == V4L2_FLASH_LED_MODE_FLASH) {
if (ov5648->flash_timeout > 0 &&
ov5648->flash_intensity > 0) {
ov5648->lamp.enable();
ov5648->flash_on = ov5648->flash_timeout;
pr_debug("%s(): flash_on: 0->%d;",
__func__,
ov5648->flash_on);
} else {
ov5648->flash_mode = V4L2_FLASH_LED_MODE_NONE;
}
}
} else {
if (ov5648->flash_mode == V4L2_FLASH_LED_MODE_FLASH) {
if (ov5648->flash_on == 1) {
ov5648->lamp.disable();
ov5648->flash_on = 0;
ov5648->flash_mode = V4L2_FLASH_LED_MODE_NONE;
pr_debug("%s(): flash_off", __func__);
} else {
pr_debug("%s(): flash_on: %d->%d",
__func__,
ov5648->flash_on,
ov5648->flash_on - 1);
ov5648->flash_on -= 1;
}
}
}
/* shift frame info */
for (i = 0; i < ov5648->flash_delay; i++) {
ov5648->flash_int_buf[i] =
ov5648->flash_int_buf[i + 1];
ov5648->flash_mod_buf[i] =
ov5648->flash_mod_buf[i + 1];
}
ov5648->flash_int_buf[ov5648->flash_delay] =
ov5648->flash_on ? ov5648->flash_intensity : 0;
ov5648->flash_mod_buf[ov5648->flash_delay] =
ov5648->flash_mode;
/* count fps */
if (ov5648->fcnt >= 30) {
int fps = ov5648->fcnt * 1000 /
(jiffies_to_msecs(jiffies) - ov5648->fps_t0);
pr_debug("%s(): fps=%d",
__func__, fps);
if (fps > 0)
ov5648->fps = fps;
ov5648->fcnt = 1;
ov5648->fps_t0 = jiffies_to_msecs(jiffies);
} else {
ov5648->fcnt += 1;
}
/* */
return 0;
}
static int ov5648_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
ret = ov5648_set_state(client, enable);
return ret;
}
static int ov5648_g_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5648 *ov5648 = to_ov5648(client);
mf->width = ov5648_mode[ov5648->mode_idx].width;
mf->height = ov5648_mode[ov5648->mode_idx].height;
mf->code = ov5648_fmts[ov5648->i_fmt].code;
mf->colorspace = ov5648_fmts[ov5648->i_fmt].colorspace;
mf->field = V4L2_FIELD_NONE;
return 0;
}
static int ov5648_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
{
int i_fmt;
int mode_idx;
i_fmt = ov5648_find_datafmt(mf->code);
mf->code = ov5648_fmts[i_fmt].code;
mf->colorspace = ov5648_fmts[i_fmt].colorspace;
mf->field = V4L2_FIELD_NONE;
mode_idx = ov5648_find_framesize(mf->width, mf->height);
mf->width = ov5648_mode[mode_idx].width;
mf->height = ov5648_mode[mode_idx].height;
return 0;
}
static int ov5648_set_mode(struct i2c_client *client, int new_mode_idx)
{
struct ov5648 *ov5648 = to_ov5648(client);
int ret = 0;
if (ov5648->mode_idx == new_mode_idx) {
pr_debug("ov5648_set_mode: skip init from mode[%d]=%s to mode[%d]=%s\n",
ov5648->mode_idx, ov5648_mode[ov5648->mode_idx].name,
new_mode_idx, ov5648_mode[new_mode_idx].name);
return ret;
}
if (ov5648->mode_idx == OV5648_MODE_MAX) {
pr_debug("ov5648_set_mode: full init from mode[%d]=%s to mode[%d]=%s\n",
ov5648->mode_idx, ov5648_mode[ov5648->mode_idx].name,
new_mode_idx, ov5648_mode[new_mode_idx].name);
ov5648_init(client);
ret = ov5648_reg_writes(client, ov5648_reginit);
ret = ov5648_reg_writes(client, ov5648_regdif[new_mode_idx]);
} else {
pr_debug("ov5648_set_mode: diff init from mode[%d]=%s to mode[%d]=%s\n",
ov5648->mode_idx, ov5648_mode[ov5648->mode_idx].name,
new_mode_idx, ov5648_mode[new_mode_idx].name);
ret = ov5648_reg_writes(client, ov5648_regdif[new_mode_idx]);
}
if (ret)
return ret;
if (new_mode_idx == OV5648_MODE_2592x1944P15) {
Is_SnapToPrev_expo = 1;
Is_SnapToPrev_gain = 1;
}
ov5648->mode_idx = new_mode_idx;
ov5648->line_length = ov5648_mode[new_mode_idx].line_length_ns;
ov5648->vts = ov5648_mode[new_mode_idx].vts;
ov5648->framerate_lo_absolute = F24p8(1.0);
ov5648->framerate_hi_absolute = ov5648_mode[new_mode_idx].fps;
ov5648_set_framerate_lo(client, ov5648->framerate_lo_absolute);
ov5648_set_framerate_hi(client, ov5648->framerate_hi_absolute);
ov5648->framerate = FRAME_RATE_AUTO;
ov5648_set_exposure(client, ov5648->exposure_current);
ov5648_set_gain(client, ov5648->gain_current);
return 0;
}
static int ov5648_set_state(struct i2c_client *client, int new_state)
{
struct ov5648 *ov5648 = to_ov5648(client);
int ret = 0;
pr_debug("ov5648_set_state: %d (%s) -> %d (%s)\n",\
ov5648->state, ov5648->state ? "strm" : "stop",\
new_state, new_state ? "strm" : "stop");
if (ov5648->state != new_state) {
ret = ov5648_reg_writes(client, ov5648_reg_state[new_state]);
ov5648->state = new_state;
if (OV5648_STATE_STRM == new_state) {
ov5648->frame_info_size = 0;
memset(ov5648->frame_info, 0,
ARRAY_SIZE(ov5648->frame_info));
if (0 == ov5648->calibrated) {
ov5648_otp_read(client);
ov5648_rbgains_update(client);
ov5648->calibrated = 1;
}
}
}
return ret;
}
static int ov5648_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5648 *ov5648 = to_ov5648(client);
int ret = 0;
int new_mode_idx;
ret = ov5648_try_fmt(sd, mf);
if (ret < 0)
return ret;
new_mode_idx = ov5648_find_framesize(mf->width, mf->height);
ov5648->i_fmt = ov5648_find_datafmt(mf->code);
switch ((u32) ov5648_fmts[ov5648->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;
}
ov5648_set_mode(client, new_mode_idx);
return ret;
}
static int ov5648_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *id)
{
/*
if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
return -EINVAL;
if (id->match.addr != client->addr)
return -ENODEV;
*/
id->ident = V4L2_IDENT_OV5648;
id->revision = 0;
return 0;
}
void ov5648_check_mounting(struct i2c_client *client, struct v4l2_ctrl *ctrl)
{
struct soc_camera_subdev_desc *ssd = client->dev.platform_data;
struct v4l2_subdev_sensor_interface_parms *iface_parms;
if (ssd && ssd->drv_priv) {
iface_parms = ssd->drv_priv;
pr_debug("facing: %d V4L2_SUBDEV_SENSOR_BACK: %d\n",
iface_parms->facing, V4L2_SUBDEV_SENSOR_BACK);
if (iface_parms->orientation == V4L2_SUBDEV_SENSOR_PORTRAIT)
ctrl->val |= V4L2_IN_ST_HFLIP;
if (iface_parms->facing == V4L2_SUBDEV_SENSOR_BACK)
ctrl->val |= V4L2_IN_ST_BACK;
} else
dev_err(&client->dev, "Missing interface parameters\n");
}
static int ov5648_g_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov5648 *ov5648 = container_of(ctrl->handler, struct ov5648, hdl);
struct v4l2_subdev *sd = &ov5648->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
int retval;
switch (ctrl->id) {
case V4L2_CID_CAMERA_FRAME_RATE:
ctrl->val = ov5648->framerate;
break;
case V4L2_CID_CAMERA_FOCUS_MODE:
ctrl->val = ov5648->focus_mode;
break;
case V4L2_CID_GAIN:
ov5648_get_gain(client, &retval, NULL);
ctrl->val = retval;
break;
case V4L2_CID_EXPOSURE:
ov5648_get_exposure(client, &retval, NULL);
ctrl->val = retval;
break;
case V4L2_CID_CAMERA_LENS_POSITION:
ov5648_lens_get_position(client, &ov5648->current_position,
&ov5648->time_to_destination);
ctrl->val = ov5648->current_position;
break;
case V4L2_CID_FLASH_LED_MODE:
ctrl->val = ov5648->flash_mode;
break;
case V4L2_CID_FLASH_INTENSITY:
ctrl->val = ov5648->flash_intensity;
break;
case V4L2_CID_FLASH_TIMEOUT:
ctrl->val = ov5648->flash_timeout;
break;
case V4L2_CID_CAM_MOUNTING:
ov5648_check_mounting(client, ctrl);
break;
}
return 0;
}
/* try_ctrl - Used to validate controls. Checks if the value
* in the control is valid and updates if necessary.
* Manipulate the current value and not the new value (to be set).
*/
static int ov5648_try_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov5648 *ov5648 = container_of(ctrl->handler, struct ov5648, hdl);
int ret = 0;
switch (ctrl->id) {
case V4L2_CID_CAMERA_FRAME_RATE:
ctrl->cur.val = ov5648->framerate;
break;
}
return ret;
}
static int ov5648_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov5648 *ov5648 = container_of(ctrl->handler, struct ov5648, hdl);
struct v4l2_subdev *sd = &ov5648->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = 0;
switch (ctrl->id) {
case V4L2_CID_CAMERA_FRAME_RATE:
if (ctrl->val > FRAME_RATE_30)
return -EINVAL;
ov5648->framerate = ctrl->val;
switch (ov5648->framerate) {
case FRAME_RATE_5:
ov5648->framerate_lo = F24p8(5.0);
ov5648->framerate_hi = F24p8(5.0);
break;
case FRAME_RATE_7:
ov5648->framerate_lo = F24p8(7.0);
ov5648->framerate_hi = F24p8(7.0);
break;
case FRAME_RATE_10:
ov5648->framerate_lo = F24p8(10.0);
ov5648->framerate_hi = F24p8(10.0);
break;
case FRAME_RATE_15:
ov5648->framerate_lo = F24p8(15.0);
ov5648->framerate_hi = F24p8(15.0);
break;
case FRAME_RATE_20:
ov5648->framerate_lo = F24p8(20.0);
ov5648->framerate_hi = F24p8(20.0);
break;
case FRAME_RATE_25:
ov5648->framerate_lo = F24p8(25.0);
ov5648->framerate_hi = F24p8(25.0);
break;
case FRAME_RATE_30:
ov5648->framerate_lo = F24p8(30.0);
ov5648->framerate_hi = F24p8(30.0);
break;
case FRAME_RATE_AUTO:
default:
ov5648->framerate_lo = F24p8(1.0);
ov5648->framerate_hi = F24p8(30.0);
break;
}
ov5648_set_framerate_lo(client, ov5648->framerate_lo);
ov5648_set_framerate_hi(client, ov5648->framerate_hi);
ov5648_set_exposure(client, ov5648->exposure_current);
ov5648_set_gain(client, ov5648->gain_current);
if (ret)
return ret;
break;
case V4L2_CID_CAMERA_FOCUS_MODE:
if (ctrl->val > FOCUS_MODE_MANUAL)
return -EINVAL;
ov5648->focus_mode = ctrl->val;
/*
* 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 (ov5648->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_AUTO_FOCUS_START:
case V4L2_CID_AUTO_FOCUS_STOP:
if (ctrl->val > AUTO_FOCUS_ON)
return -EINVAL;
if (ret)
return ret;
break;
case V4L2_CID_GAIN:
if ((unsigned int)(ctrl->val) > OV5648_GAIN_MAX) {
pr_debug("V4L2_CID_GAIN invalid gain=%u max=%u",
ctrl->val, OV5648_GAIN_MAX);
return -EINVAL;
}
if (ov5648->agc_on)
ov5648_set_gain(client, ctrl->val);
else
pr_debug("%s(): agc is off", __func__);
break;
case V4L2_CID_EXPOSURE:
if (ctrl->val > OV5648_EXP_MAX)
return -EINVAL;
if (ov5648->agc_on)
ov5648_set_exposure(client, ctrl->val);
else
pr_debug("%s(): agc is off", __func__);
break;
case V4L2_CID_CAMERA_LENS_POSITION:
if (ctrl->val > OV5648_LENS_MAX)
return -EINVAL;
ov5648_lens_set_position(client, ctrl->val);
break;
case V4L2_CID_FLASH_LED_MODE: {
/* translate from vl42:
V4L2_FLASH_LED_MODE_NONE, = 0
V4L2_FLASH_LED_MODE_FLASH,
V4L2_FLASH_LED_MODE_FLASH_AUTO,
V4L2_FLASH_LED_MODE_TORCH,
to v4l2_flash_mode
FLASH_MODE_BASE = 0,
FLASH_MODE_OFF,
FLASH_MODE_AUTO,
FLASH_MODE_ON,
FLASH_MODE_TORCH_ON,
FLASH_MODE_TORCH_OFF,
FLASH_MAIN_OFF,
FLASH_MODE_REDEYE,
FLASH_MODE_MAX,
static const int mode[4] = {
FLASH_MODE_OFF,
FLASH_MODE_ON,
FLASH_MODE_AUTO,
FLASH_MODE_TORCH_ON
};
*/
ov5648_set_flash_mode(client, ctrl->val);
pr_debug("%s() V4L2_CID_FLASH_LED_MODE val=%d",
__func__, ctrl->val);
break;
}
case V4L2_CID_FLASH_INTENSITY:
ov5648->flash_intensity = ctrl->val;
pr_info("%s() flash_intensity set to %d\n",
__func__, ov5648->flash_intensity);
break;
case V4L2_CID_FLASH_TIMEOUT:
ov5648->flash_timeout = ctrl->val;
pr_info("%s() flash_timeout set to %d\n",
__func__, ov5648->flash_timeout);
break;
}
return ret;
}
static int ov5648_set_flash_mode(struct i2c_client *client,
int mode)
{
static const char * const flashmode_str[] = {
"V4L2_FLASH_LED_MODE_NONE",
"V4L2_FLASH_LED_MODE_FLASH",
"V4L2_FLASH_LED_MODE_FLASH_AUTO",
"V4L2_FLASH_LED_MODE_TORCH",
};
struct ov5648 *ov5648 = to_ov5648(client);
/* check if lamp is presented */
if (NULL == ov5648->lamp.name)
return -1;
/* check if lamp is in requested mode */
if (ov5648->flash_mode == mode)
return 0;
/* apply new mode */
switch (mode) {
case V4L2_FLASH_LED_MODE_NONE:
ov5648->lamp.setup(mode, 0, 0);
ov5648->lamp.disable();
ov5648->flash_on = 0;
pr_debug("%s(): FLASH_MODE_OFF", __func__);
break;
case V4L2_FLASH_LED_MODE_TORCH:
ov5648->lamp.setup(mode, 0, 255);
ov5648->lamp.enable();
pr_debug("%s(): FLASH_MODE_TORCH_ON", __func__);
break;
case V4L2_FLASH_LED_MODE_FLASH:
case V4L2_FLASH_LED_MODE_FLASH_AUTO:
{
int flash_timeout_us =
ov5648->flash_timeout *
ov5648->line_length *
ov5648->vts / 1000;
ov5648->lamp.setup(mode, flash_timeout_us,
ov5648->flash_intensity);
pr_debug("%s(): FLASH_MODE_ON int=%d timeout: %d frames %d ms",
__func__,
ov5648->flash_intensity,
ov5648->flash_timeout,
flash_timeout_us/1000);
break;
}
default:
return -EINVAL;
}
/* update mode */
ov5648->flash_mode = mode;
pr_debug("%s() mode=%s intensity=%d timeout=%d\n",
__func__,
flashmode_str[mode],
ov5648->flash_intensity,
ov5648->flash_timeout);
return 0;
}
static long ov5648_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 ov5648 *ov5648 = to_ov5648(client);
struct v4l2_frame_info *fi_cache, *fi_usr =
(struct v4l2_frame_info *)arg;
ret = -EINVAL;
if (!timespec_valid(&fi_usr->timestamp)) {
ov5648_get_gain(client, &fi_usr->an_gain, NULL);
fi_usr->flash_mode =
ov5648->flash_mod_buf[0];
fi_usr->flash_intensity =
ov5648->flash_int_buf[0];
ov5648_get_exposure(client,
&fi_usr->exposure, NULL);
ov5648_lens_get_position(client,
&fi_usr->lens_pos, &i);
ret = 0;
/*
pr_debug("%s():%d VIDIOC_SENSOR_G_FRAME_INFO"\
" fi: exp=%d ag=%d lens=%d"\
" flash=[%d %d]"\
" tv=%ld",
__func__, __LINE__,
fi_usr->exposure,
fi_usr->an_gain,
fi_usr->lens_pos,
fi_usr->flash_mode,
fi_usr->flash_intensity,
fi_usr->timestamp.tv_nsec);
*/
break;
}
j = ov5648->frame_info_size;
fi_cache = &ov5648->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;
/*
pr_debug("%s():%d VIDIOC_SENSOR_G_FRAME_INFO"\
" fi[%d]: exp=%d ag=%d lens=%d"\
" flash=[%d %d] tv=%ld",
__func__, __LINE__,
i,
fi_usr->exposure,
fi_usr->an_gain,
fi_usr->lens_pos,
fi_usr->flash_mode,
fi_usr->flash_intensity,
fi_usr->timestamp.tv_nsec);
*/
}
break;
}
case VIDIOC_SENSOR_S_FRAME_INFO:
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5648 *ov5648 = to_ov5648(client);
struct v4l2_frame_info *fi_dst, *fi_src;
fi_dst = &ov5648->frame_info[0];
fi_src = (struct v4l2_frame_info *)arg;
if (!timespec_valid(&fi_src->timestamp))
break;
if (ov5648->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];
ov5648->frame_info_size--;
}
fi_dst[ov5648->frame_info_size++] = *fi_src;
/*
pr_debug("%s(): VIDIOC_SENSOR_S_FRAME_INFO"\
" fi[%d]: exp=%d ag=%d lens=%d flash=[%d %d]"\
" tv=%ld",
__func__,
ov5648->frame_info_size-1,
fi_src->exposure,
fi_src->an_gain,
fi_src->lens_pos,
fi_src->flash_mode,
fi_src->flash_intensity,
fi_src->timestamp.tv_nsec);
*/
break;
}
case VIDIOC_SENSOR_FRAME_IRQ:
ov5648_frame_irq(sd, (u32)arg);
break;
default:
ret = -ENOIOCTLCMD;
break;
}
return ret;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov5648_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 (ov5648_reg_read(client, reg->reg, &reg->val))
return -EIO return 0;
}
static int ov5648_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 (ov5648_reg_write(client, reg->reg, reg->val))
return -EIO;
return 0;
}
#endif
static int ov5648_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5648 *ov5648 = to_ov5648(client);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
if (!on) {
ov5648->mode_idx = OV5648_MODE_MAX;
ov5648->calibrated = 0;
return soc_camera_power_off(&client->dev, ssdd);
}
return soc_camera_power_on(&client->dev, ssdd);
}
static int ov5648_init(struct i2c_client *client)
{
struct ov5648 *ov5648 = to_ov5648(client);
int ret = 0;
int i = 0;
/* reset */
ov5648_reg_write(client, 0x0103, 0x01);
/* set initial mode */
ov5648->mode_idx = OV5648_MODE_MAX;
ov5648->state = OV5648_STATE_STOP;
/* default settings */
ov5648->framerate = FRAME_RATE_AUTO;
ov5648->focus_mode = FOCUS_MODE_AUTO;
ov5648->gain_current = DEFAULT_GAIN;
ov5648->agc_on = 1;
ov5648->af_on = 1;
memset(&ov5648->exp_read_buf, 0, sizeof(ov5648->exp_read_buf));
memset(&ov5648->gain_read_buf, 0, sizeof(ov5648->gain_read_buf));
memset(&ov5648->lens_read_buf, 0, sizeof(ov5648->lens_read_buf));
/*
* Exposure should be DEFAULT_EXPO * line_length / 1000
* Since we don't have line_length yet, just estimate
*/
ov5648->exposure_current = DEFAULT_EXPO;
ov5648->aecpos_delay = 1;
ov5648->lenspos_delay = 0;
ov5648->flash_mode = V4L2_FLASH_LED_MODE_NONE;
ov5648->flash_intensity = OV5648_FLASH_INTENSITY_DEFAULT;
ov5648->flash_timeout = OV5648_FLASH_TIMEOUT_DEFAULT;
ov5648->flash_delay = 0;
ov5648->fcnt = 1;
ov5648->line_length =
ov5648_mode[OV5648_MODE_1280x960P30].line_length_ns;
for (i = 0; i < ov5648->aecpos_delay; i++) {
ov5648->exp_read_buf[i] = \
(ov5648->exposure_current * 1000 / ov5648->line_length) << 4;
ov5648->gain_read_buf[i] = ov5648->gain_current >> 4;
}
/* get flashlamp */
if (0 == get_flash_lamp(&ov5648->lamp)) {
ov5648->lamp.reset();
} else {
/* no flashlamp */
ov5648->lamp.name = NULL;
}
dev_dbg(&client->dev, "Sensor initialized\n");
return ret;
}
#define OV5648_MAX_PROC 255
static int ov5648_procfs_read(struct file *flip, char __user *user_buf,
size_t count, loff_t *ppos) {
char buf[OV5648_MAX_PROC];
int len;
int lens_position, lens_ttd;
u8 gain_buf[2];
u8 exp_buf[3];
u16 gain_code;
u32 exp_code;
struct i2c_client *client;
struct ov5648 *ov5648;
char str[255];
int i, strlen;
client = (struct i2c_client *)PDE_DATA(file_inode(flip));
if (NULL == client)
goto err_out;
ov5648 = to_ov5648(client);
if (NULL == ov5648)
goto err_out;
ov5648_lens_get_position(client, &lens_position, &lens_ttd);
ov5648_reg_read_multi(client, OV5648_REG_AGC_HI, gain_buf, 2);
gain_code = ((gain_buf[0] & 0x3f) << 8) + gain_buf[1];
ov5648_reg_read_multi(client, OV5648_REG_EXP_HI, exp_buf, 3);
exp_code =
((exp_buf[0] & 0xf) << 16) +
((exp_buf[1] & 0xff) << 8) +
(exp_buf[2] & 0xf0);
strlen = 0;
for (i = 0; i < OV5648_MODE_MAX; i++)
strlen += sprintf(str+strlen, "%d ",
ov5648_mode[i].line_length_ns);
len = sprintf(buf,
"mode = %s\n"
"ag = 0x%2X (%d.%03d) 0x%04X\n"
"exp = %d\n"
"lines = [%d 0x%06X]\n"
"vts/min/max = [%d %d %d]\n"
"line_len = %d\n"
"mode_line_len = [%s]\n"
"agc = %s\n"
"lens = %d\n"
"af = %s\n"
"flash_delay = %d\n"
"fps/fcnt = %d %d\n",
ov5648_mode[ov5648->mode_idx].name,
ov5648->gain_current,
ov5648->gain_current >> 8, ov5648->gain_current & 0xFF,
gain_code,
ov5648->exposure_current,
ov5648->coarse_int_lines, exp_code,
ov5648->vts, ov5648->vts_min, ov5648->vts_max,
ov5648->line_length,
str,
ov5648->agc_on ? "on" : "off",
lens_position,
ov5648->af_on ? "on" : "off",
ov5648->flash_delay,
ov5648->fps, ov5648->fcnt
);
len = simple_read_from_buffer(user_buf, count, ppos, buf, len);
return len;
err_out:
return 0;
}
static int ov5648_procfs_write(struct file *flip,
const char __user *buf,
size_t count,
loff_t *data)
{
int val, val2;
char str[OV5648_MAX_PROC+1];
struct i2c_client *client;
struct ov5648 *ov5648;
int lens_position, lens_ttd;
client = (struct i2c_client *)PDE_DATA(file_inode(flip));
if (NULL == client)
return 0;
ov5648 = to_ov5648(client);
if (NULL == ov5648)
return 0;
if (count > OV5648_MAX_PROC)
count = OV5648_MAX_PROC;
if (copy_from_user(str, buf, count))
return -EFAULT;
str[min_t(unsigned long, OV5648_MAX_PROC, count)] = 0;
pr_debug("%s(): str=\"%s\"", __func__, str);
if (strncmp(str, "ag=", 3) == 0) {
val = 0;
sscanf(str, "ag=%d", &val);
if (val == -1) {
ov5648->agc_on = 1;
} else {
ov5648->agc_on = 0;
ov5648_set_gain(client, val);
}
pr_debug("%s(): ag=0x%X agc_on=%d\n",
__func__, val, ov5648->agc_on);
} else if (strncmp(str, "exp=", 4) == 0) {
val = 0;
sscanf(str, "exp=%d", &val);
if (val == -1) {
ov5648->agc_on = 1;
} else {
ov5648->agc_on = 0;
ov5648_set_exposure(client, val);
}
pr_debug("%s(): exp=0x%X agc_on=%d\n",
__func__, val, ov5648->agc_on);
} else if (strncmp(str, "line_len=", 9) == 0) {
val = 0;
sscanf(str, "line_len=%d", &val);
ov5648->line_length = val;
ov5648_set_exposure(client, ov5648->exposure_current);
pr_debug("%s(): line_len=%d ns\n",
__func__, ov5648->line_length);
} else if (strncmp(str, "mode_line_len", 13) == 0) {
val = 0;
sscanf(str, "mode_line_len[%d]=%d", &val, &val2);
ov5648_mode[val].line_length_ns = val2;
pr_debug("%s(): mode_line_len[%d]=%d ns\n",
__func__, val, val2);
} else if (strncmp(str, "lines=", 6) == 0) {
val = 0;
sscanf(str, "lines=%d", &val);
if (val == -1) {
ov5648->agc_on = 1;
} else {
u8 exp_buf[5];
ov5648->coarse_int_lines = val;
ov5648->agc_on = 0;
val <<= 4;
exp_buf[0] = (u8)((OV5648_REG_EXP_HI >> 8) & 0xFF);
exp_buf[1] = (u8)(OV5648_REG_EXP_HI & 0xFF);
exp_buf[2] = (u8)((val >> 16) & 0xFF);
exp_buf[3] = (u8)((val >> 8) & 0xFF);
exp_buf[4] = (u8)((val >> 0) & 0xF0);
ov5648_array_write(client, exp_buf, 5);
}
pr_debug("%s(): coarse_int_lines=%d ns\n",
__func__, ov5648->coarse_int_lines);
} else if (strncmp(str, "lens=", 5) == 0) {
val = 0;
sscanf(str, "lens=%d", &val);
if (val == -1) {
ov5648->af_on = 1;
} else {
ov5648->af_on = 0;
ov5648_lens_set_position(client, val);
}
pr_debug("%s(): lens_pos=%d af_on=%d\n",
__func__, val, ov5648->af_on);
} else if (strncmp(str, "lens+=", 6) == 0) {
val = 0;
sscanf(str, "lens+=%d", &val);
ov5648->af_on = 0;
ov5648_lens_get_position(client, &lens_position, &lens_ttd);
ov5648_lens_set_position(client, lens_position+val);
pr_debug("%s(): lens_pos=%d af_on=%d\n",
__func__, lens_position+val, ov5648->af_on);
} else if (strncmp(str, "lens-=", 6) == 0) {
val = 0;
sscanf(str, "lens-=%d", &val);
ov5648->af_on = 0;
ov5648_lens_get_position(client, &lens_position, &lens_ttd);
ov5648_lens_set_position(client, lens_position-val);
pr_debug("%s(): lens_pos=%d af_on=%d\n",
__func__, lens_position-val, ov5648->af_on);
} else if (strncmp(str, "flash=", 6) == 0) {
val = 0;
sscanf(str, "flash=%d %d", &val, &val2);
pr_debug("%s(): flash=%d %d",
__func__, val, val2);
ov5648->flash_intensity = val;
ov5648->flash_timeout = val2;
if (ov5648->flash_intensity > 0) {
ov5648_set_flash_mode(client,
V4L2_FLASH_LED_MODE_FLASH);
val2 = val2 *
ov5648->line_length *
ov5648->vts / 1000;
} else {
ov5648_set_flash_mode(client, V4L2_FLASH_LED_MODE_NONE);
}
} else if (strncmp(str, "flash_delay=", 12) == 0) {
val = 0;
sscanf(str, "flash_delay=%d", &val);
pr_debug("%s(): flash_delay=%d\n",
__func__, val);
ov5648->flash_delay = val;
} else
pr_debug("%s(): unknown command: \"%s\"",
__func__, str);
return count;
}
static const struct file_operations proc_fops = {
.write = ov5648_procfs_write,
.read = ov5648_procfs_read,
};
static int ov5648_procfs_init(struct i2c_client *client)
{
struct ov5648 *ov5648 = to_ov5648(client);
ov5648->proc_entry = proc_create_data(SENSOR_NAME_STR,
(S_IRUSR | S_IRGRP),
NULL,
&proc_fops,
client);
if (NULL == ov5648->proc_entry) {
dev_err(&client->dev, "%s(): error creating proc entry %s",
__func__,
SENSOR_NAME_STR);
return -ENOMEM;
}
dev_info(&client->dev, "%s(): proc entry %s OK",
__func__, SENSOR_NAME_STR);
return 0;
}
static int ov5648_procfs_exit(struct i2c_client *client)
{
struct ov5648 *ov5648 = to_ov5648(client);
if (ov5648->proc_entry) {
remove_proc_entry(SENSOR_NAME_STR, NULL);
dev_info(&client->dev, "%s(): proc entry %s removed",
__func__, SENSOR_NAME_STR);
}
return 0;
}
/*
* 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 ov5648_video_probe(struct i2c_client *client)
{
int ret = 0;
u8 revision = 0, id_high, id_low;
u16 id;
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
ret = ov5648_s_power(subdev, 1);
if (ret < 0)
return ret;
/*
* We must have a parent by now. And it cannot be a wrong one.
* So this entire test is completely redundant.
*/
ret = ov5648_reg_read(client, 0x302A, &revision);
if (ret) {
dev_err(&client->dev, "Failure to detect OV5648 chip\n");
goto ei2c;
}
revision &= 0xF;
/* Read sensor Model ID */
ret = ov5648_reg_read(client, OV5648_REG_CHIP_ID_HIGH, &id_high);
if (ret < 0)
goto ei2c;
id = id_high << 8;
ret = ov5648_reg_read(client, OV5648_REG_CHIP_ID_LOW, &id_low);
if (ret < 0)
goto ei2c;
id |= id_low;
if (id != 0x5648) {
ret = -ENODEV;
goto ei2c;
}
dev_info(&client->dev, "Detected a OV5648 chip 0x%04x, revision %x\n",
id, revision);
/* Init the sensor here */
ret = ov5648_init(client);
if (ret) {
dev_err(&client->dev, "Failed to initialize camera\n");
ret = -EINVAL;
}
ei2c:
ov5648_s_power(subdev, 0);
return ret;
}
static struct v4l2_subdev_core_ops ov5648_subdev_core_ops = {
.g_chip_ident = ov5648_g_chip_ident,
.ioctl = ov5648_ioctl,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov5648_g_register,
.s_register = ov5648_s_register,
#endif
.s_power = ov5648_s_power,
};
static int ov5648_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
if (index >= ARRAY_SIZE(ov5648_fmts))
return -EINVAL;
*code = ov5648_fmts[index].code;
return 0;
}
static int ov5648_enum_framesizes(struct v4l2_subdev *sd,
struct v4l2_frmsizeenum *fsize)
{
if (fsize->index >= OV5648_MODE_MAX)
return -EINVAL;
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->pixel_format = V4L2_PIX_FMT_SRGGB10;
fsize->discrete.width = ov5648_mode[fsize->index].width;
fsize->discrete.height = ov5648_mode[fsize->index].height;
return 0;
}
/* we only support fixed frame rate */
static int ov5648_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 = ov5648_find_framesize(interval->width, interval->height);
switch (size) {
case OV5648_MODE_2592x1944P15:
interval->discrete.numerator = 1;
interval->discrete.denominator = 15;
break;
case OV5648_MODE_1280x720P30:
case OV5648_MODE_1280x960P30:
default:
interval->discrete.numerator = 1;
interval->discrete.denominator = 30;
break;
}
return 0;
}
static int ov5648_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5648 *ov5648 = to_ov5648(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 (ov5648->mode_idx) {
case OV5648_MODE_2592x1944P15:
cparm->timeperframe.numerator = 1;
cparm->timeperframe.denominator = 15;
break;
case OV5648_MODE_1280x720P30:
case OV5648_MODE_1280x960P30:
#ifdef CONFIG_ARCH_JAVA
case OV5648_MODE_1920x1080P30:
#endif
default:
cparm->timeperframe.numerator = 1;
cparm->timeperframe.denominator = 30;
break;
}
return 0;
}
static int ov5648_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param)
{
/*
* FIXME: This just enforces the hardcoded framerates until this is
*flexible enough.
*/
return ov5648_g_parm(sd, param);
}
static struct v4l2_subdev_video_ops ov5648_subdev_video_ops = {
.s_stream = ov5648_s_stream,
.s_mbus_fmt = ov5648_s_fmt,
.g_mbus_fmt = ov5648_g_fmt,
.try_mbus_fmt = ov5648_try_fmt,
.enum_mbus_fmt = ov5648_enum_fmt,
.enum_mbus_fsizes = ov5648_enum_framesizes,
.enum_framesizes = ov5648_enum_framesizes,
.enum_frameintervals = ov5648_enum_frameintervals,
.g_parm = ov5648_g_parm,
.s_parm = ov5648_s_parm,
};
static int ov5648_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
{
/* Quantity of initial bad frames to skip. Revisit. */
*frames = 1;
return 0;
}
static int ov5648_g_interface_parms(struct v4l2_subdev *sd,
struct v4l2_subdev_sensor_interface_parms
*parms)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5648 *ov5648 = to_ov5648(client);
if (!parms)
return -EINVAL;
parms->if_type = ov5648->plat_parms->if_type;
parms->if_mode = ov5648->plat_parms->if_mode;
parms->parms = ov5648->plat_parms->parms;
/* set the hs term time */
parms->parms.serial.hs_term_time = 0;
parms->parms.serial.hs_settle_time = 2;
return 0;
}
static struct v4l2_subdev_sensor_ops ov5648_subdev_sensor_ops = {
.g_skip_frames = ov5648_g_skip_frames,
.g_interface_parms = ov5648_g_interface_parms,
};
static struct v4l2_subdev_ops ov5648_subdev_ops = {
.core = &ov5648_subdev_core_ops,
.video = &ov5648_subdev_video_ops,
.sensor = &ov5648_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
#ifdef CONFIG_VIDEO_DW9714
#define DW9714_I2C_ADDR 0x18
static struct i2c_board_info dw9714_i2c_board_info = {
I2C_BOARD_INFO("dw9714", (DW9714_I2C_ADDR >> 1))
};
static struct i2c_client *dw9714_i2c_client;
static struct i2c_adapter *dw9714_i2c_adap;
#endif
static int ov5648_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ov5648 *ov5648;
struct soc_camera_subdev_desc *sdesc = soc_camera_i2c_to_desc(client);
struct v4l2_subdev *subdev;
int ret, i;
if (!sdesc) {
dev_err(&client->dev, "OV5648: missing platform data!\n");
return -EINVAL;
}
ov5648 = kzalloc(sizeof(struct ov5648), GFP_KERNEL);
if (!ov5648)
return -ENOMEM;
v4l2_i2c_subdev_init(&ov5648->subdev, client, &ov5648_subdev_ops);
/* Second stage probe - when a capture adapter is there */
ov5648->mode_idx = OV5648_MODE_MAX;
ov5648->i_fmt = 0; /* First format in the list */
ov5648->plat_parms = sdesc->drv_priv;
subdev = i2c_get_clientdata(client);
v4l2_i2c_subdev_init(&ov5648->subdev, client, &ov5648_subdev_ops);
v4l2_ctrl_handler_init(&ov5648->hdl, ARRAY_SIZE(ov5648_controls) + 6);
if (ov5648->hdl.error)
pr_err("error set during init itself!\n");
/* register standard controls */
v4l2_ctrl_new_std(&ov5648->hdl, &ov5648_ctrl_ops,
V4L2_CID_GAIN, OV5648_GAIN_MIN,
OV5648_GAIN_MAX, OV5648_GAIN_STEP, DEFAULT_GAIN);
v4l2_ctrl_new_std(&ov5648->hdl, &ov5648_ctrl_ops,
V4L2_CID_EXPOSURE, OV5648_EXP_MIN,
OV5648_EXP_MAX, OV5648_EXP_STEP, DEFAULT_EXPO);
v4l2_ctrl_new_std(&ov5648->hdl, &ov5648_ctrl_ops,
V4L2_CID_FLASH_INTENSITY, OV5648_FLASH_INTENSITY_MIN,
OV5648_FLASH_INTENSITY_MAX, OV5648_FLASH_INTENSITY_STEP,
OV5648_FLASH_INTENSITY_DEFAULT);
v4l2_ctrl_new_std(&ov5648->hdl, &ov5648_ctrl_ops,
V4L2_CID_FLASH_TIMEOUT, OV5648_FLASH_TIMEOUT_MIN,
OV5648_FLASH_TIMEOUT_MAX, OV5648_FLASH_TIMEOUT_STEP,
OV5648_FLASH_TIMEOUT_DEFAULT);
if (ov5648->hdl.error) {
dev_err(&client->dev,
"Standard controls initialization error %d\n",
ov5648->hdl.error);
ret = ov5648->hdl.error;
goto ctrl_hdl_err;
}
/* register standard menu controls */
v4l2_ctrl_new_std_menu(&ov5648->hdl, &ov5648_ctrl_ops,
V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_INFINITY,
0, V4L2_AUTO_FOCUS_RANGE_AUTO);
v4l2_ctrl_new_std_menu(&ov5648->hdl, &ov5648_ctrl_ops,
V4L2_CID_FLASH_LED_MODE, V4L2_FLASH_LED_MODE_TORCH,
0, V4L2_FLASH_LED_MODE_FLASH);
if (ov5648->hdl.error) {
dev_err(&client->dev,
"Standard menu controls initialization error %d\n",
ov5648->hdl.error);
ret = ov5648->hdl.error;
goto ctrl_hdl_err;
}
/* register custom controls */
for (i = 0; i < ARRAY_SIZE(ov5648_controls); ++i) {
v4l2_ctrl_new_custom(&ov5648->hdl, &ov5648_controls[i], NULL);
if (ov5648->hdl.error) {
dev_err(&client->dev,
"Custom controls %d initialization error %d\n",
i,
ov5648->hdl.error);
ret = ov5648->hdl.error;
goto ctrl_hdl_err;
}
}
ov5648->subdev.ctrl_handler = &ov5648->hdl;
ret = ov5648_video_probe(client);
if (ret) {
goto vid_probe_fail;
}
#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
#ifdef CONFIG_VIDEO_DW9714
pr_debug("DW9714 i2c start");
dw9714_i2c_adap = i2c_get_adapter(0);
if (!dw9714_i2c_adap)
pr_debug("DW9714 i2c_get_adapter(0) FAILED");
if (dw9714_i2c_adap) {
dw9714_i2c_client = i2c_new_device(dw9714_i2c_adap,
&dw9714_i2c_board_info);
i2c_put_adapter(dw9714_i2c_adap);
pr_debug("DW9714 i2c_get_adapter(0) OK");
}
#endif
#if OV5648_DEBUGFS
/* init procfs */
ov5648_procfs_init(client);
#endif
return ret;
ctrl_hdl_err:
vid_probe_fail:
v4l2_ctrl_handler_free(&ov5648->hdl);
kfree(ov5648);
pr_err("ov5648_probe failed with ret = %d\n", ret);
return ret;
}
static int ov5648_remove(struct i2c_client *client)
{
struct ov5648 *ov5648 = to_ov5648(client);
pr_debug(" remove");
v4l2_device_unregister_subdev(&ov5648->subdev);
#if OV5648_DEBUGFS
ov5648_procfs_exit(client);
#endif
#ifdef CONFIG_VIDEO_A3907
pr_debug("A3907 i2c_unregister_device");
if (a3907_i2c_client)
i2c_unregister_device(a3907_i2c_client);
#endif
#ifdef CONFIG_VIDEO_DW9714
pr_debug("DW9714 i2c_unregister_device");
if (dw9714_i2c_client)
i2c_unregister_device(dw9714_i2c_client);
#endif
client->driver = NULL;
kfree(ov5648);
return 0;
}
static const struct i2c_device_id ov5648_id[] = {
{SENSOR_NAME_STR, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ov5648_id);
static struct i2c_driver ov5648_i2c_driver = {
.driver = {
.name = SENSOR_NAME_STR,
},
.probe = ov5648_probe,
.remove = ov5648_remove,
.id_table = ov5648_id,
};
static int __init ov5648_mod_init(void)
{
return i2c_add_driver(&ov5648_i2c_driver);
}
static void __exit ov5648_mod_exit(void)
{
i2c_del_driver(&ov5648_i2c_driver);
}
late_initcall(ov5648_mod_init);
module_exit(ov5648_mod_exit);
MODULE_DESCRIPTION("OmniVision OV5648 Camera driver");
MODULE_AUTHOR("Broadcom Corporation");
MODULE_LICENSE("GPL v2");