/* Copyright (c) 2010-2011, The Linux Foundation. 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 #include #include #include #include #include #include #include #include "ov7692.h" /*============================================================= SENSOR REGISTER DEFINES ==============================================================*/ #define Q8 0x00000100 /* Omnivision8810 product ID register address */ #define REG_OV7692_MODEL_ID_MSB 0x0A #define REG_OV7692_MODEL_ID_LSB 0x0B #define OV7692_MODEL_ID 0x7692 /* Omnivision8810 product ID */ /* Time in milisecs for waiting for the sensor to reset */ #define OV7692_RESET_DELAY_MSECS 66 #define OV7692_DEFAULT_CLOCK_RATE 24000000 /* Registers*/ /* Color bar pattern selection */ #define OV7692_COLOR_BAR_PATTERN_SEL_REG 0x82 /* Color bar enabling control */ #define OV7692_COLOR_BAR_ENABLE_REG 0x601 /* Time in milisecs for waiting for the sensor to reset*/ #define OV7692_RESET_DELAY_MSECS 66 /*============================================================================ DATA DECLARATIONS ============================================================================*/ /* 96MHz PCLK @ 24MHz MCLK */ struct reg_addr_val_pair_struct ov7692_init_settings_array[] = { {0x12, 0x80}, {0x0e, 0x08}, {0x69, 0x52}, {0x1e, 0xb3}, {0x48, 0x42}, {0xff, 0x01}, {0xae, 0xa0}, {0xa8, 0x26}, {0xb4, 0xc0}, {0xb5, 0x40}, {0xff, 0x00}, {0x0c, 0x00}, {0x62, 0x10}, {0x12, 0x00}, {0x17, 0x65}, {0x18, 0xa4}, {0x19, 0x0a}, {0x1a, 0xf6}, {0x3e, 0x30}, {0x64, 0x0a}, {0xff, 0x01}, {0xb4, 0xc0}, {0xff, 0x00}, {0x67, 0x20}, {0x81, 0x3f}, {0xcc, 0x02}, {0xcd, 0x80}, {0xce, 0x01}, {0xcf, 0xe0}, {0xc8, 0x02}, {0xc9, 0x80}, {0xca, 0x01}, {0xcb, 0xe0}, {0xd0, 0x48}, {0x82, 0x03}, {0x0e, 0x00}, {0x70, 0x00}, {0x71, 0x34}, {0x74, 0x28}, {0x75, 0x98}, {0x76, 0x00}, {0x77, 0x64}, {0x78, 0x01}, {0x79, 0xc2}, {0x7a, 0x4e}, {0x7b, 0x1f}, {0x7c, 0x00}, {0x11, 0x00}, {0x20, 0x00}, {0x21, 0x23}, {0x50, 0x9a}, {0x51, 0x80}, {0x4c, 0x7d}, {0x0e, 0x00}, {0x80, 0x7f}, {0x85, 0x10}, {0x86, 0x00}, {0x87, 0x00}, {0x88, 0x00}, {0x89, 0x2a}, {0x8a, 0x26}, {0x8b, 0x22}, {0xbb, 0x7a}, {0xbc, 0x69}, {0xbd, 0x11}, {0xbe, 0x13}, {0xbf, 0x81}, {0xc0, 0x96}, {0xc1, 0x1e}, {0xb7, 0x05}, {0xb8, 0x09}, {0xb9, 0x00}, {0xba, 0x18}, {0x5a, 0x1f}, {0x5b, 0x9f}, {0x5c, 0x6a}, {0x5d, 0x42}, {0x24, 0x78}, {0x25, 0x68}, {0x26, 0xb3}, {0xa3, 0x0b}, {0xa4, 0x15}, {0xa5, 0x2a}, {0xa6, 0x51}, {0xa7, 0x63}, {0xa8, 0x74}, {0xa9, 0x83}, {0xaa, 0x91}, {0xab, 0x9e}, {0xac, 0xaa}, {0xad, 0xbe}, {0xae, 0xce}, {0xaf, 0xe5}, {0xb0, 0xf3}, {0xb1, 0xfb}, {0xb2, 0x06}, {0x8c, 0x5c}, {0x8d, 0x11}, {0x8e, 0x12}, {0x8f, 0x19}, {0x90, 0x50}, {0x91, 0x20}, {0x92, 0x96}, {0x93, 0x80}, {0x94, 0x13}, {0x95, 0x1b}, {0x96, 0xff}, {0x97, 0x00}, {0x98, 0x3d}, {0x99, 0x36}, {0x9a, 0x51}, {0x9b, 0x43}, {0x9c, 0xf0}, {0x9d, 0xf0}, {0x9e, 0xf0}, {0x9f, 0xff}, {0xa0, 0x68}, {0xa1, 0x62}, {0xa2, 0x0e}, }; static bool OV7692_CSI_CONFIG; /* 816x612, 24MHz MCLK 96MHz PCLK */ uint32_t OV7692_FULL_SIZE_WIDTH = 640; uint32_t OV7692_FULL_SIZE_HEIGHT = 480; uint32_t OV7692_QTR_SIZE_WIDTH = 640; uint32_t OV7692_QTR_SIZE_HEIGHT = 480; uint32_t OV7692_HRZ_FULL_BLK_PIXELS = 16; uint32_t OV7692_VER_FULL_BLK_LINES = 12; uint32_t OV7692_HRZ_QTR_BLK_PIXELS = 16; uint32_t OV7692_VER_QTR_BLK_LINES = 12; struct ov7692_work_t { struct work_struct work; }; static struct ov7692_work_t *ov7692_sensorw; static struct i2c_client *ov7692_client; struct ov7692_ctrl_t { const struct msm_camera_sensor_info *sensordata; uint32_t sensormode; uint32_t fps_divider; /* init to 1 * 0x00000400 */ uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ uint32_t fps; int32_t curr_lens_pos; uint32_t curr_step_pos; uint32_t my_reg_gain; uint32_t my_reg_line_count; uint32_t total_lines_per_frame; enum ov7692_resolution_t prev_res; enum ov7692_resolution_t pict_res; enum ov7692_resolution_t curr_res; enum ov7692_test_mode_t set_test; unsigned short imgaddr; }; static struct ov7692_ctrl_t *ov7692_ctrl; static DECLARE_WAIT_QUEUE_HEAD(ov7692_wait_queue); DEFINE_MUTEX(ov7692_mut); /*=============================================================*/ static int ov7692_i2c_rxdata(unsigned short saddr, unsigned char *rxdata, int length) { struct i2c_msg msgs[] = { { .addr = saddr, .flags = 0, .len = 1, .buf = rxdata, }, { .addr = saddr, .flags = I2C_M_RD, .len = 1, .buf = rxdata, }, }; if (i2c_transfer(ov7692_client->adapter, msgs, 2) < 0) { CDBG("ov7692_i2c_rxdata failed!\n"); return -EIO; } return 0; } static int32_t ov7692_i2c_txdata(unsigned short saddr, unsigned char *txdata, int length) { struct i2c_msg msg[] = { { .addr = saddr, .flags = 0, .len = 2, .buf = txdata, }, }; if (i2c_transfer(ov7692_client->adapter, msg, 1) < 0) { CDBG("ov7692_i2c_txdata faild 0x%x\n", ov7692_client->addr); return -EIO; } return 0; } static int32_t ov7692_i2c_read(uint8_t raddr, uint8_t *rdata, int rlen) { int32_t rc = 0; unsigned char buf[1]; if (!rdata) return -EIO; memset(buf, 0, sizeof(buf)); buf[0] = raddr; rc = ov7692_i2c_rxdata(ov7692_client->addr >> 1, buf, rlen); if (rc < 0) { CDBG("ov7692_i2c_read 0x%x failed!\n", raddr); return rc; } *rdata = buf[0]; return rc; } static int32_t ov7692_i2c_write_b_sensor(uint8_t waddr, uint8_t bdata) { int32_t rc = -EFAULT; unsigned char buf[2]; memset(buf, 0, sizeof(buf)); buf[0] = waddr; buf[1] = bdata; CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata); rc = ov7692_i2c_txdata(ov7692_client->addr >> 1, buf, 2); if (rc < 0) CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n", waddr, bdata); return rc; } static int32_t ov7692_sensor_setting(int update_type, int rt) { int32_t i, array_length; int32_t rc = 0; struct msm_camera_csi_params ov7692_csi_params; switch (update_type) { case REG_INIT: OV7692_CSI_CONFIG = 0; ov7692_i2c_write_b_sensor(0x0e, 0x08); return rc; break; case UPDATE_PERIODIC: if (!OV7692_CSI_CONFIG) { ov7692_csi_params.lane_cnt = 1; ov7692_csi_params.data_format = CSI_8BIT; ov7692_csi_params.lane_assign = 0xe4; ov7692_csi_params.dpcm_scheme = 0; ov7692_csi_params.settle_cnt = 0x14; rc = msm_camio_csi_config(&ov7692_csi_params); msleep(10); array_length = sizeof(ov7692_init_settings_array) / sizeof(ov7692_init_settings_array[0]); for (i = 0; i < array_length; i++) { rc = ov7692_i2c_write_b_sensor( ov7692_init_settings_array[i].reg_addr, ov7692_init_settings_array[i].reg_val); if (rc < 0) return rc; } OV7692_CSI_CONFIG = 1; msleep(20); return rc; } break; default: rc = -EINVAL; break; } return rc; } static int32_t ov7692_video_config(int mode) { int32_t rc = 0; int rt; /* change sensor resolution if needed */ rt = RES_PREVIEW; if (ov7692_sensor_setting(UPDATE_PERIODIC, rt) < 0) return rc; ov7692_ctrl->curr_res = ov7692_ctrl->prev_res; ov7692_ctrl->sensormode = mode; return rc; } static int32_t ov7692_set_sensor_mode(int mode, int res) { int32_t rc = 0; switch (mode) { case SENSOR_PREVIEW_MODE: rc = ov7692_video_config(mode); break; case SENSOR_SNAPSHOT_MODE: case SENSOR_RAW_SNAPSHOT_MODE: break; default: rc = -EINVAL; break; } return rc; } static int32_t ov7692_power_down(void) { return 0; } static int ov7692_probe_init_sensor(const struct msm_camera_sensor_info *data) { uint8_t model_id_msb, model_id_lsb = 0; uint16_t model_id; int32_t rc = 0; /*The reset pin is not physically connected to the sensor. The standby pin will do the reset hence there is no need to request the gpio reset*/ /* Read sensor Model ID: */ rc = ov7692_i2c_read(REG_OV7692_MODEL_ID_MSB, &model_id_msb, 1); if (rc < 0) goto init_probe_fail; rc = ov7692_i2c_read(REG_OV7692_MODEL_ID_LSB, &model_id_lsb, 1); if (rc < 0) goto init_probe_fail; model_id = (model_id_msb << 8) | ((model_id_lsb & 0x00FF)) ; CDBG("ov7692 model_id = 0x%x, 0x%x, 0x%x\n", model_id, model_id_msb, model_id_lsb); /* 4. Compare sensor ID to OV7692 ID: */ if (model_id != OV7692_MODEL_ID) { rc = -ENODEV; goto init_probe_fail; } goto init_probe_done; init_probe_fail: pr_warning(" ov7692_probe_init_sensor fails\n"); init_probe_done: CDBG(" ov7692_probe_init_sensor finishes\n"); return rc; } int ov7692_sensor_open_init(const struct msm_camera_sensor_info *data) { int32_t rc = 0; CDBG("%s: %d\n", __func__, __LINE__); CDBG("Calling ov7692_sensor_open_init\n"); ov7692_ctrl = kzalloc(sizeof(struct ov7692_ctrl_t), GFP_KERNEL); if (!ov7692_ctrl) { CDBG("ov7692_init failed!\n"); rc = -ENOMEM; goto init_done; } ov7692_ctrl->fps_divider = 1 * 0x00000400; ov7692_ctrl->pict_fps_divider = 1 * 0x00000400; ov7692_ctrl->fps = 30 * Q8; ov7692_ctrl->set_test = TEST_OFF; ov7692_ctrl->prev_res = QTR_SIZE; ov7692_ctrl->pict_res = FULL_SIZE; ov7692_ctrl->curr_res = INVALID_SIZE; if (data) ov7692_ctrl->sensordata = data; /* enable mclk first */ msm_camio_clk_rate_set(24000000); msleep(20); rc = ov7692_probe_init_sensor(data); if (rc < 0) { CDBG("Calling ov7692_sensor_open_init fail\n"); goto init_fail; } rc = ov7692_sensor_setting(REG_INIT, RES_PREVIEW); if (rc < 0) goto init_fail; else goto init_done; init_fail: CDBG(" ov7692_sensor_open_init fail\n"); kfree(ov7692_ctrl); init_done: CDBG("ov7692_sensor_open_init done\n"); return rc; } static int ov7692_init_client(struct i2c_client *client) { /* Initialize the MSM_CAMI2C Chip */ init_waitqueue_head(&ov7692_wait_queue); return 0; } static const struct i2c_device_id ov7692_i2c_id[] = { {"ov7692", 0}, { } }; static int ov7692_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc = 0; CDBG("ov7692_i2c_probe called!\n"); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { CDBG("i2c_check_functionality failed\n"); goto probe_failure; } ov7692_sensorw = kzalloc(sizeof(struct ov7692_work_t), GFP_KERNEL); if (!ov7692_sensorw) { CDBG("kzalloc failed.\n"); rc = -ENOMEM; goto probe_failure; } i2c_set_clientdata(client, ov7692_sensorw); ov7692_init_client(client); ov7692_client = client; CDBG("ov7692_i2c_probe success! rc = %d\n", rc); return 0; probe_failure: CDBG("ov7692_i2c_probe failed! rc = %d\n", rc); return rc; } static int __exit ov7692_remove(struct i2c_client *client) { struct ov7692_work_t_t *sensorw = i2c_get_clientdata(client); free_irq(client->irq, sensorw); ov7692_client = NULL; kfree(sensorw); return 0; } static struct i2c_driver ov7692_i2c_driver = { .id_table = ov7692_i2c_id, .probe = ov7692_i2c_probe, .remove = __exit_p(ov7692_i2c_remove), .driver = { .name = "ov7692", }, }; int ov7692_sensor_config(void __user *argp) { struct sensor_cfg_data cdata; long rc = 0; if (copy_from_user(&cdata, (void *)argp, sizeof(struct sensor_cfg_data))) return -EFAULT; mutex_lock(&ov7692_mut); CDBG("ov7692_sensor_config: cfgtype = %d\n", cdata.cfgtype); switch (cdata.cfgtype) { case CFG_SET_MODE: rc = ov7692_set_sensor_mode(cdata.mode, cdata.rs); break; case CFG_PWR_DOWN: rc = ov7692_power_down(); break; case CFG_SET_EFFECT: break; default: rc = -EFAULT; break; } mutex_unlock(&ov7692_mut); return rc; } static int ov7692_sensor_release(void) { int rc = -EBADF; mutex_lock(&ov7692_mut); ov7692_power_down(); kfree(ov7692_ctrl); ov7692_ctrl = NULL; CDBG("ov7692_release completed\n"); mutex_unlock(&ov7692_mut); return rc; } static int ov7692_sensor_probe(const struct msm_camera_sensor_info *info, struct msm_sensor_ctrl *s) { int rc = 0; rc = i2c_add_driver(&ov7692_i2c_driver); if (rc < 0 || ov7692_client == NULL) { rc = -ENOTSUPP; goto probe_fail; } msm_camio_clk_rate_set(24000000); rc = ov7692_probe_init_sensor(info); if (rc < 0) goto probe_fail; s->s_init = ov7692_sensor_open_init; s->s_release = ov7692_sensor_release; s->s_config = ov7692_sensor_config; s->s_camera_type = FRONT_CAMERA_2D; s->s_mount_angle = 0; return rc; probe_fail: CDBG("ov7692_sensor_probe: SENSOR PROBE FAILS!\n"); i2c_del_driver(&ov7692_i2c_driver); return rc; } static int __ov7692_probe(struct platform_device *pdev) { return msm_camera_drv_start(pdev, ov7692_sensor_probe); } static struct platform_driver msm_camera_driver = { .probe = __ov7692_probe, .driver = { .name = "msm_camera_ov7692", .owner = THIS_MODULE, }, }; static int __init ov7692_init(void) { return platform_driver_register(&msm_camera_driver); } module_init(ov7692_init); MODULE_DESCRIPTION("OMNI VGA YUV sensor driver"); MODULE_LICENSE("GPL v2");