blob: 2ba8731f16c6ca044ccb240993222ad8e913e297 [file] [log] [blame]
/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include <mach/camera.h>
#include <mach/gpio.h>
#include <media/msm_camera.h>
#include "msm_sensor.h"
#include "ov2720.h"
#include "msm.h"
#define SENSOR_NAME "ov2720"
#define PLATFORM_DRIVER_NAME "msm_camera_ov2720"
#define ov2720_obj ov2720_##obj
DEFINE_MUTEX(ov2720_mut);
static struct msm_sensor_ctrl_t ov2720_s_ctrl;
struct msm_sensor_i2c_reg_conf ov2720_start_settings[] = {
{0x0100, 0x01},
};
struct msm_sensor_i2c_reg_conf ov2720_stop_settings[] = {
{0x0100, 0x00},
};
struct msm_sensor_i2c_reg_conf ov2720_groupon_settings[] = {
{0x3208, 0x00},
};
struct msm_sensor_i2c_reg_conf ov2720_groupoff_settings[] = {
{0x3208, 0x10},
{0x3208, 0xA0},
};
static struct msm_sensor_i2c_reg_conf ov2720_prev_settings[] = {
{0x3800, 0x00},
{0x3801, 0x02},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3804, 0x07},
{0x3805, 0xA1},
{0x3806, 0x04},
{0x3807, 0x47},
{0x3808, 0x07},
{0x3809, 0x8C},
{0x380a, 0x04},
{0x380b, 0x44},
{0x380c, 0x08},/*Line Length Pclk Hi*/
{0x380d, 0x5c},/*Line Length Pclk Lo*/
{0x380e, 0x04},/*Frame Length Line Hi*/
{0x380f, 0x60},/*Frame Length Line Lo*/
{0x3810, 0x00},
{0x3811, 0x09},
{0x3812, 0x00},
{0x3813, 0x02},
{0x3820, 0x80},
{0x3821, 0x06},
{0x3814, 0x11},
{0x3815, 0x11},
{0x3612, 0x0b},
{0x3618, 0x04},
{0x3a08, 0x01},
{0x3a09, 0x50},
{0x3a0a, 0x01},
{0x3a0b, 0x18},
{0x3a0d, 0x03},
{0x3a0e, 0x03},
{0x4520, 0x00},
{0x4837, 0x1b},
{0x3000, 0xff},
{0x3001, 0xff},
{0x3002, 0xf0},
{0x3600, 0x08},
{0x3621, 0xc0},
{0x3632, 0xd2},
{0x3633, 0x23},
{0x3634, 0x54},
{0x3f01, 0x0c},
{0x5001, 0xc1},
{0x3614, 0xf0},
{0x3630, 0x2d},
{0x370b, 0x62},
{0x3706, 0x61},
{0x4000, 0x02},
{0x4002, 0xc5},
{0x4005, 0x08},
{0x404f, 0x84},
{0x4051, 0x00},
{0x5000, 0xff},
{0x3a18, 0x00},
{0x3a19, 0x80},
{0x3503, 0x13},
{0x4521, 0x00},
{0x5183, 0xb0},
{0x5184, 0xb0},
{0x5185, 0xb0},
{0x370c, 0x0c},
{0x3035, 0x10},
{0x3036, 0x1e},
{0x3037, 0x21},
{0x303e, 0x19},
{0x3038, 0x06},
{0x3018, 0x04},
{0x3000, 0x00},
{0x3001, 0x00},
{0x3002, 0x00},
{0x3a0f, 0x40},
{0x3a10, 0x38},
{0x3a1b, 0x48},
{0x3a1e, 0x30},
{0x3a11, 0x90},
{0x3a1f, 0x10},
{0x4800, 0x24},
};
static struct msm_sensor_i2c_reg_conf ov2720_720_settings[] = {
{0x3800, 0x01},
{0x3801, 0x4a},
{0x3802, 0x00},
{0x3803, 0xba},
{0x3804, 0x06},
{0x3805, 0x51+32},
{0x3806, 0x03},
{0x3807, 0x8d+24},
{0x3808, 0x05},
{0x3809, 0x00+16},
{0x380a, 0x02},
{0x380b, 0x78},
{0x380c, 0x08},/*Line Length Pclk Hi*/
{0x380d, 0x5e},/*Line Length Pclk Lo*/
{0x380e, 0x04},/*Frame Length Line Hi*/
{0x380f, 0x60},/*Frame Length Line Lo*/
{0x3810, 0x00},
{0x3811, 0x05},
{0x3812, 0x00},
{0x3813, 0x02},
{0x3820, 0x80},
{0x3821, 0x06},
{0x3814, 0x11},
{0x3815, 0x11},
{0x3612, 0x0b},
{0x3618, 0x04},
{0x3a08, 0x01},
{0x3a09, 0x50},
{0x3a0a, 0x01},
{0x3a0b, 0x18},
{0x3a0d, 0x03},
{0x3a0e, 0x03},
{0x4520, 0x00},
{0x4837, 0x1b},
{0x3000, 0xff},
{0x3001, 0xff},
{0x3002, 0xf0},
{0x3600, 0x08},
{0x3621, 0xc0},
{0x3632, 0xd2},
{0x3633, 0x23},
{0x3634, 0x54},
{0x3f01, 0x0c},
{0x5001, 0xc1},
{0x3614, 0xf0},
{0x3630, 0x2d},
{0x370b, 0x62},
{0x3706, 0x61},
{0x4000, 0x02},
{0x4002, 0xc5},
{0x4005, 0x08},
{0x404f, 0x84},
{0x4051, 0x00},
{0x5000, 0xff},
{0x3a18, 0x00},
{0x3a19, 0x80},
{0x3503, 0x13},
{0x4521, 0x00},
{0x5183, 0xb0},
{0x5184, 0xb0},
{0x5185, 0xb0},
{0x370c, 0x0c},
{0x3035, 0x10},
{0x3036, 0x04},
{0x3037, 0x61},
{0x303e, 0x19},
{0x3038, 0x06},
{0x3018, 0x04},
{0x3000, 0x00},
{0x3001, 0x00},
{0x3002, 0x00},
{0x3a0f, 0x40},
{0x3a10, 0x38},
{0x3a1b, 0x48},
{0x3a1e, 0x30},
{0x3a11, 0x90},
{0x3a1f, 0x10},
{0x4800, 0x24},
};
static struct msm_sensor_i2c_reg_conf ov2720_vga_settings[] = {
{0x3800, 0x00},
{0x3801, 0x0c},
{0x3802, 0x00},
{0x3803, 0x02},
{0x3804, 0x07},
{0x3805, 0x97+32},
{0x3806, 0x04},
{0x3807, 0x45+24},
{0x3808, 0x02},
{0x3809, 0x88+16},
{0x380a, 0x01},
{0x380b, 0xe6+12},
{0x380c, 0x08},/*Line Length Pclk Hi*/
{0x380d, 0x5e},/*Line Length Pclk Lo*/
{0x380e, 0x04},/*Frame Length Line Hi*/
{0x380f, 0x68},/*Frame Length Line Lo*/
{0x3810, 0x00},
{0x3811, 0x03},
{0x3812, 0x00},
{0x3813, 0x03},
{0x3820, 0x80},
{0x3821, 0x06},
{0x3814, 0x11},
{0x3815, 0x11},
{0x3612, 0x0b},
{0x3618, 0x04},
{0x3a08, 0x01},
{0x3a09, 0x50},
{0x3a0a, 0x01},
{0x3a0b, 0x18},
{0x3a0d, 0x03},
{0x3a0e, 0x03},
{0x4520, 0x00},
{0x4837, 0x1b},
{0x3000, 0xff},
{0x3001, 0xff},
{0x3002, 0xf0},
{0x3600, 0x08},
{0x3621, 0xc0},
{0x3632, 0xd2},
{0x3633, 0x23},
{0x3634, 0x54},
{0x3f01, 0x0c},
{0x5001, 0xc1},
{0x3614, 0xf0},
{0x3630, 0x2d},
{0x370b, 0x62},
{0x3706, 0x61},
{0x4000, 0x02},
{0x4002, 0xc5},
{0x4005, 0x08},
{0x404f, 0x84},
{0x4051, 0x00},
{0x5000, 0xff},
{0x3a18, 0x00},
{0x3a19, 0x80},
{0x3503, 0x13},
{0x4521, 0x00},
{0x5183, 0xb0},
{0x5184, 0xb0},
{0x5185, 0xb0},
{0x370c, 0x0c},
{0x3035, 0x10},
{0x3036, 0x04},
{0x3037, 0x61},
{0x303e, 0x19},
{0x3038, 0x06},
{0x3018, 0x04},
{0x3000, 0x00},
{0x3001, 0x00},
{0x3002, 0x00},
{0x3a0f, 0x40},
{0x3a10, 0x38},
{0x3a1b, 0x48},
{0x3a1e, 0x30},
{0x3a11, 0x90},
{0x3a1f, 0x10},
{0x4800, 0x24},
{0x3500, 0x00},
{0x3501, 0x17},
{0x3502, 0xf0},
{0x3508, 0x00},
{0x3509, 0x20},
};
static struct msm_sensor_i2c_reg_conf ov2720_recommend_settings[] = {
{0x0103, 0x01},
{0x3718, 0x10},
{0x3702, 0x24},
{0x373a, 0x60},
{0x3715, 0x01},
{0x3703, 0x2e},
{0x3705, 0x10},
{0x3730, 0x30},
{0x3704, 0x62},
{0x3f06, 0x3a},
{0x371c, 0x00},
{0x371d, 0xc4},
{0x371e, 0x01},
{0x371f, 0x0d},
{0x3708, 0x61},
{0x3709, 0x12},
{0x5800, 0x03},
{0x5801, 0xD0},
{0x5802, 0x02},
{0x5803, 0x56},
{0x5804, 0x22},
{0x5805, 0x06},
{0x5806, 0xC2},
{0x5807, 0x08},
{0x5808, 0x03},
{0x5809, 0xD0},
{0x580A, 0x02},
{0x580B, 0x56},
{0x580C, 0x22},
{0x580D, 0x07},
{0x580E, 0xC2},
{0x580F, 0x08},
{0x5810, 0x03},
{0x5811, 0xD0},
{0x5812, 0x02},
{0x5813, 0x56},
{0x5814, 0x18},
{0x5815, 0x07},
{0x5816, 0xC2},
{0x5817, 0x08},
{0x5818, 0x04},
{0x5819, 0x80},
{0x581A, 0x06},
{0x581B, 0x0C},
{0x581C, 0x80},
};
static struct msm_camera_csi_params ov2720_csi_params = {
.lane_cnt = 2,
.data_format = CSI_10BIT,
.lane_assign = 0xe4,
.dpcm_scheme = 0,
.settle_cnt = 0x18,
};
static struct v4l2_subdev_info ov2720_subdev_info[] = {
{
.code = V4L2_MBUS_FMT_SBGGR10_1X10,
.colorspace = V4L2_COLORSPACE_JPEG,
.fmt = 1,
.order = 0,
},
/* more can be supported, to be added later */
};
static struct msm_sensor_i2c_conf_array ov2720_init_conf[] = {
{&ov2720_recommend_settings[0],
ARRAY_SIZE(ov2720_recommend_settings), 0, MSM_SENSOR_I2C_BYTE_DATA}
};
static struct msm_sensor_i2c_conf_array ov2720_confs[] = {
{&ov2720_prev_settings[0],
ARRAY_SIZE(ov2720_prev_settings), 0, MSM_SENSOR_I2C_BYTE_DATA},
{&ov2720_vga_settings[0],
ARRAY_SIZE(ov2720_vga_settings), 0, MSM_SENSOR_I2C_BYTE_DATA},
{&ov2720_720_settings[0],
ARRAY_SIZE(ov2720_720_settings), 0, MSM_SENSOR_I2C_BYTE_DATA},
};
static int32_t ov2720_write_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
uint16_t gain, uint32_t line)
{
uint32_t fl_lines, offset;
uint8_t int_time[3];
fl_lines =
(s_ctrl->curr_frame_length_lines * s_ctrl->fps_divider) / Q10;
offset = s_ctrl->vert_offset;
if (line > (fl_lines - offset))
fl_lines = line + offset;
s_ctrl->func_tbl.sensor_group_hold_on(s_ctrl);
msm_sensor_i2c_write(s_ctrl->sensor_i2c_client,
s_ctrl->frame_length_lines_addr, fl_lines,
MSM_SENSOR_I2C_WORD_DATA);
int_time[0] = line >> 12;
int_time[1] = line >> 4;
int_time[2] = line << 4;
msm_sensor_i2c_write_seq(s_ctrl->sensor_i2c_client,
s_ctrl->coarse_int_time_addr-1, &int_time[0], 3);
msm_sensor_i2c_write(s_ctrl->sensor_i2c_client,
s_ctrl->global_gain_addr, gain,
MSM_SENSOR_I2C_WORD_DATA);
s_ctrl->func_tbl.sensor_group_hold_off(s_ctrl);
return 0;
}
static int32_t ov2720_sensor_setting(struct msm_sensor_ctrl_t *s_ctrl,
int update_type, int rt)
{
struct msm_camera_csid_params ov2720_csid_params;
struct msm_camera_csiphy_params ov2720_csiphy_params;
int32_t rc = 0;
s_ctrl->func_tbl.sensor_stop_stream(s_ctrl);
msleep(30);
if (update_type == MSM_SENSOR_REG_INIT) {
s_ctrl->config_csi_flag = 1;
msm_sensor_enable_debugfs(s_ctrl);
msm_sensor_write_init_settings(s_ctrl);
} else if (update_type == MSM_SENSOR_UPDATE_PERIODIC) {
msm_sensor_write_res_settings(s_ctrl, rt);
if (s_ctrl->config_csi_flag) {
struct msm_camera_csid_vc_cfg ov2720_vccfg[] = {
{0, CSI_RAW10, CSI_DECODE_10BIT},
{1, CSI_EMBED_DATA, CSI_DECODE_8BIT},
};
ov2720_csid_params.lane_cnt = 2;
ov2720_csid_params.lane_assign = 0xe4;
ov2720_csid_params.lut_params.num_cid =
ARRAY_SIZE(ov2720_vccfg);
ov2720_csid_params.lut_params.vc_cfg =
&ov2720_vccfg[0];
ov2720_csiphy_params.lane_cnt = 2;
ov2720_csiphy_params.settle_cnt = 0x1B;
rc = msm_camio_csid_config(&ov2720_csid_params);
v4l2_subdev_notify(s_ctrl->sensor_v4l2_subdev,
NOTIFY_CID_CHANGE, NULL);
mb();
rc = msm_camio_csiphy_config(&ov2720_csiphy_params);
mb();
msleep(20);
s_ctrl->config_csi_flag = 0;
}
s_ctrl->func_tbl.sensor_start_stream(s_ctrl);
msleep(30);
}
return rc;
}
static int ov2720_sensor_config(void __user *argp)
{
return (int) msm_sensor_config(&ov2720_s_ctrl, argp);
}
static int ov2720_power_down(const struct msm_camera_sensor_info *data)
{
CDBG("%s\n", __func__);
gpio_set_value_cansleep(data->sensor_reset, 0);
gpio_free(data->sensor_reset);
return 0;
}
static int ov2720_power_up(const struct msm_camera_sensor_info *data)
{
int32_t rc = 0;
CDBG("%s: %d\n", __func__, __LINE__);
msm_camio_clk_rate_set(MSM_SENSOR_MCLK_24HZ);
rc = gpio_request(data->sensor_reset, "SENSOR_NAME");
if (rc < 0)
goto gpio_request_fail;
CDBG("%s: reset sensor\n", __func__);
gpio_direction_output(data->sensor_reset, 0);
msleep(50);
gpio_set_value_cansleep(data->sensor_reset, 1);
msleep(50);
rc = msm_sensor_match_id(&ov2720_s_ctrl);
if (rc < 0)
goto init_probe_fail;
goto init_probe_done;
gpio_request_fail:
CDBG("%s: gpio request fail\n", __func__);
return rc;
init_probe_fail:
CDBG(" %s fails\n", __func__);
ov2720_power_down(data);
return rc;
init_probe_done:
CDBG("%s finishes\n", __func__);
return rc;
}
static int ov2720_sensor_open_init(const struct msm_camera_sensor_info *data)
{
int32_t rc = 0;
CDBG("%s: %d\n", __func__, __LINE__);
ov2720_s_ctrl.fps = 30*Q8;
ov2720_s_ctrl.fps_divider = 1 * 0x00000400;
ov2720_s_ctrl.cam_mode = MSM_SENSOR_MODE_INVALID;
if (data)
ov2720_s_ctrl.sensordata = data;
rc = ov2720_power_up(data);
if (rc < 0)
goto init_done;
goto init_done;
init_done:
CDBG("%s finishes\n", __func__);
return rc;
}
static int ov2720_sensor_release(void)
{
mutex_lock(ov2720_s_ctrl.msm_sensor_mutex);
gpio_set_value_cansleep(ov2720_s_ctrl.sensordata->sensor_reset, 0);
msleep(20);
gpio_free(ov2720_s_ctrl.sensordata->sensor_reset);
mutex_unlock(ov2720_s_ctrl.msm_sensor_mutex);
CDBG("%s completed\n", __func__);
return 0;
}
static const struct i2c_device_id ov2720_i2c_id[] = {
{SENSOR_NAME, (kernel_ulong_t)&ov2720_s_ctrl},
{ }
};
static struct i2c_driver ov2720_i2c_driver = {
.id_table = ov2720_i2c_id,
.probe = msm_sensor_i2c_probe,
.driver = {
.name = SENSOR_NAME,
},
};
static struct msm_sensor_i2c_client ov2720_sensor_i2c_client = {
.addr_type = MSM_SENSOR_I2C_WORD_ADDR,
};
static int ov2720_sensor_v4l2_probe(const struct msm_camera_sensor_info *info,
struct v4l2_subdev *sdev, struct msm_sensor_ctrl *s)
{
return msm_sensor_v4l2_probe(&ov2720_s_ctrl, info, sdev, s);
}
static int ov2720_probe(struct platform_device *pdev)
{
return msm_sensor_register(pdev, ov2720_sensor_v4l2_probe);
}
struct platform_driver ov2720_driver = {
.probe = ov2720_probe,
.driver = {
.name = PLATFORM_DRIVER_NAME,
.owner = THIS_MODULE,
},
};
static int __init msm_sensor_init_module(void)
{
return platform_driver_register(&ov2720_driver);
}
static struct v4l2_subdev_core_ops ov2720_subdev_core_ops;
static struct v4l2_subdev_video_ops ov2720_subdev_video_ops = {
.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
};
static struct v4l2_subdev_ops ov2720_subdev_ops = {
.core = &ov2720_subdev_core_ops,
.video = &ov2720_subdev_video_ops,
};
static struct msm_sensor_ctrl_t ov2720_s_ctrl = {
.msm_sensor_reg = {
.default_data_type = MSM_SENSOR_I2C_BYTE_DATA,
.start_stream_conf = ov2720_start_settings,
.start_stream_conf_size = ARRAY_SIZE(ov2720_start_settings),
.stop_stream_conf = ov2720_stop_settings,
.stop_stream_conf_size = ARRAY_SIZE(ov2720_stop_settings),
.group_hold_on_conf = ov2720_groupon_settings,
.group_hold_on_conf_size = ARRAY_SIZE(ov2720_groupon_settings),
.group_hold_off_conf = ov2720_groupoff_settings,
.group_hold_off_conf_size =
ARRAY_SIZE(ov2720_groupoff_settings),
.init_settings = &ov2720_init_conf[0],
.init_size = ARRAY_SIZE(ov2720_init_conf),
.res_settings = &ov2720_confs[0],
.num_conf = ARRAY_SIZE(ov2720_confs),
},
.sensor_i2c_client = &ov2720_sensor_i2c_client,
.sensor_i2c_addr = 0x6C,
.sensor_id_addr = 0x300A,
.sensor_id = 0x2720,
.frame_length_lines_addr = 0x380e,
.coarse_int_time_addr = 0x3501,
.global_gain_addr = 0x3508,
.line_length_pck_addr = 0x380c,
.frame_length_lines_array_addr = 14,
.line_length_pck_array_addr = 12,
.vert_offset = 6,
.cam_mode = MSM_SENSOR_MODE_INVALID,
.camera_type = FRONT_CAMERA_2D,
.config_csi_flag = 1,
.csi_params = &ov2720_csi_params,
.msm_sensor_mutex = &ov2720_mut,
.msm_sensor_i2c_driver = &ov2720_i2c_driver,
.sensor_v4l2_subdev_info = ov2720_subdev_info,
.sensor_v4l2_subdev_info_size = ARRAY_SIZE(ov2720_subdev_info),
.sensor_v4l2_subdev_ops = &ov2720_subdev_ops,
.func_tbl = {
.sensor_start_stream = msm_sensor_start_stream,
.sensor_stop_stream = msm_sensor_stop_stream,
.sensor_group_hold_on = msm_sensor_group_hold_on,
.sensor_group_hold_off = msm_sensor_group_hold_off,
.sensor_get_prev_lines_pf = msm_sensor_get_prev_lines_pf,
.sensor_get_prev_pixels_pl = msm_sensor_get_prev_pixels_pl,
.sensor_get_pict_lines_pf = msm_sensor_get_pict_lines_pf,
.sensor_get_pict_pixels_pl = msm_sensor_get_pict_pixels_pl,
.sensor_get_pict_max_exp_lc = msm_sensor_get_pict_max_exp_lc,
.sensor_get_pict_fps = msm_sensor_get_pict_fps,
.sensor_set_fps = msm_sensor_set_fps,
.sensor_write_exp_gain = ov2720_write_exp_gain,
.sensor_setting = ov2720_sensor_setting,
.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
.sensor_mode_init = msm_sensor_mode_init,
.sensor_config = ov2720_sensor_config,
.sensor_open_init = ov2720_sensor_open_init,
.sensor_release = ov2720_sensor_release,
.sensor_power_up = ov2720_power_up,
.sensor_power_down = ov2720_power_down,
.sensor_probe = msm_sensor_probe,
},
};
module_init(msm_sensor_init_module);
MODULE_DESCRIPTION("Omnivision 2MP Bayer sensor driver");
MODULE_LICENSE("GPL v2");