| /* Copyright (c) 2012, 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/delay.h> |
| #include <linux/debugfs.h> |
| #include <linux/types.h> |
| #include <linux/i2c.h> |
| #include <linux/uaccess.h> |
| #include <linux/miscdevice.h> |
| #include <linux/slab.h> |
| #include <linux/gpio.h> |
| #include <linux/bitops.h> |
| #include <linux/leds.h> |
| #include <mach/camera.h> |
| #include <media/msm_camera.h> |
| #include "ov5647.h" |
| |
| /* 16bit address - 8 bit context register structure */ |
| #define Q8 0x00000100 |
| #define Q10 0x00000400 |
| |
| #define REG_OV5647_GAIN_MSB 0x350A |
| #define REG_OV5647_GAIN_LSB 0x350B |
| #define REG_OV5647_LINE_HSB 0x3500 |
| #define REG_OV5647_LINE_MSB 0x3501 |
| #define REG_OV5647_LINE_LSB 0x3502 |
| |
| /* MCLK */ |
| #define OV5647_MASTER_CLK_RATE 24000000 |
| |
| /* AF Total steps parameters */ |
| #define OV5647_TOTAL_STEPS_NEAR_TO_FAR 32 |
| |
| #define OV5647_REG_PREV_FRAME_LEN_1 31 |
| #define OV5647_REG_PREV_FRAME_LEN_2 32 |
| #define OV5647_REG_PREV_LINE_LEN_1 33 |
| #define OV5647_REG_PREV_LINE_LEN_2 34 |
| |
| #define OV5647_REG_SNAP_FRAME_LEN_1 15 |
| #define OV5647_REG_SNAP_FRAME_LEN_2 16 |
| #define OV5647_REG_SNAP_LINE_LEN_1 17 |
| #define OV5647_REG_SNAP_LINE_LEN_2 18 |
| #define MSB 1 |
| #define LSB 0 |
| |
| /* Debug switch */ |
| #ifdef CDBG |
| #undef CDBG |
| #endif |
| #ifdef CDBG_HIGH |
| #undef CDBG_HIGH |
| #endif |
| |
| /*#define OV5647_VERBOSE_DGB*/ |
| |
| #ifdef OV5647_VERBOSE_DGB |
| #define CDBG(fmt, args...) pr_debug(fmt, ##args) |
| #define CDBG_HIGH(fmt, args...) pr_debug(fmt, ##args) |
| #else |
| #define CDBG(fmt, args...) do { } while (0) |
| #define CDBG_HIGH(fmt, args...) pr_debug(fmt, ##args) |
| #endif |
| |
| /*for debug*/ |
| #ifdef CDBG |
| #undef CDBG |
| #endif |
| #define CDBG(fmt, args...) printk(fmt, ##args) |
| |
| static uint8_t mode_mask = 0x09; |
| struct ov5647_work_t { |
| struct work_struct work; |
| }; |
| |
| static struct ov5647_work_t *ov5647_sensorw; |
| static struct ov5647_work_t *ov5647_af_sensorw; |
| static struct i2c_client *ov5647_af_client; |
| static struct i2c_client *ov5647_client; |
| |
| struct ov5647_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 */ |
| uint16_t fps; |
| |
| uint16_t curr_lens_pos; |
| uint16_t curr_step_pos; |
| uint16_t my_reg_gain; |
| uint32_t my_reg_line_count; |
| uint16_t total_lines_per_frame; |
| |
| enum ov5647_resolution_t prev_res; |
| enum ov5647_resolution_t pict_res; |
| enum ov5647_resolution_t curr_res; |
| enum ov5647_test_mode_t set_test; |
| }; |
| |
| static bool CSI_CONFIG; |
| static struct ov5647_ctrl_t *ov5647_ctrl; |
| |
| static DECLARE_WAIT_QUEUE_HEAD(ov5647_wait_queue); |
| static DECLARE_WAIT_QUEUE_HEAD(ov5647_af_wait_queue); |
| DEFINE_MUTEX(ov5647_mut); |
| |
| static uint16_t prev_line_length_pck; |
| static uint16_t prev_frame_length_lines; |
| static uint16_t snap_line_length_pck; |
| static uint16_t snap_frame_length_lines; |
| |
| static int ov5647_i2c_rxdata(unsigned short saddr, |
| unsigned char *rxdata, int length) |
| { |
| struct i2c_msg msgs[] = { |
| { |
| .addr = saddr, |
| .flags = 0, |
| .len = 2, |
| .buf = rxdata, |
| }, |
| { |
| .addr = saddr, |
| .flags = I2C_M_RD, |
| .len = 1, |
| .buf = rxdata, |
| }, |
| }; |
| if (i2c_transfer(ov5647_client->adapter, msgs, 2) < 0) { |
| CDBG("ov5647_i2c_rxdata faild 0x%x\n", saddr); |
| return -EIO; |
| } |
| return 0; |
| } |
| |
| static int32_t ov5647_i2c_txdata(unsigned short saddr, |
| unsigned char *txdata, int length) |
| { |
| struct i2c_msg msg[] = { |
| { |
| .addr = saddr, |
| .flags = 0, |
| .len = length, |
| .buf = txdata, |
| }, |
| }; |
| if (i2c_transfer(ov5647_client->adapter, msg, 1) < 0) { |
| CDBG("ov5647_i2c_txdata faild 0x%x\n", saddr); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int32_t ov5647_i2c_read(unsigned short raddr, |
| unsigned short *rdata) |
| { |
| int32_t rc = 0; |
| unsigned char buf[2]; |
| |
| if (!rdata) |
| return -EIO; |
| CDBG("%s:saddr:0x%x raddr:0x%x data:0x%x", |
| __func__, ov5647_client->addr, raddr, *rdata); |
| memset(buf, 0, sizeof(buf)); |
| buf[0] = (raddr & 0xFF00) >> 8; |
| buf[1] = (raddr & 0x00FF); |
| rc = ov5647_i2c_rxdata(ov5647_client->addr >> 1, buf, 1); |
| if (rc < 0) { |
| CDBG("ov5647_i2c_read 0x%x failed!\n", raddr); |
| return rc; |
| } |
| *rdata = buf[0]; |
| CDBG("ov5647_i2c_read 0x%x val = 0x%x!\n", raddr, *rdata); |
| |
| return rc; |
| } |
| |
| static int32_t ov5647_i2c_write_b_sensor(unsigned short waddr, uint8_t bdata) |
| { |
| int32_t rc = -EFAULT; |
| unsigned char buf[3]; |
| |
| memset(buf, 0, sizeof(buf)); |
| buf[0] = (waddr & 0xFF00) >> 8; |
| buf[1] = (waddr & 0x00FF); |
| buf[2] = bdata; |
| CDBG("i2c_write_b addr = 0x%x, val = 0x%x\n", waddr, bdata); |
| rc = ov5647_i2c_txdata(ov5647_client->addr >> 1, buf, 3); |
| if (rc < 0) { |
| pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n", |
| waddr, bdata); |
| } |
| return rc; |
| } |
| |
| static int32_t ov5647_i2c_write_b_table(struct ov5647_i2c_reg_conf const |
| *reg_conf_tbl, int num) |
| { |
| int i; |
| int32_t rc = -EIO; |
| |
| for (i = 0; i < num; i++) { |
| rc = ov5647_i2c_write_b_sensor(reg_conf_tbl->waddr, |
| reg_conf_tbl->wdata); |
| if (rc < 0) |
| break; |
| reg_conf_tbl++; |
| } |
| return rc; |
| } |
| |
| static int32_t ov5647_af_i2c_txdata(unsigned short saddr, |
| unsigned char *txdata, int length) |
| { |
| struct i2c_msg msg[] = { |
| { |
| .addr = saddr, |
| .flags = 0, |
| .len = length, |
| .buf = txdata, |
| }, |
| }; |
| if (i2c_transfer(ov5647_af_client->adapter, msg, 1) < 0) { |
| pr_err("ov5647_af_i2c_txdata faild 0x%x\n", saddr); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int32_t ov5647_af_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 = ov5647_af_i2c_txdata(ov5647_af_client->addr, buf, 2); |
| if (rc < 0) { |
| pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n", |
| waddr, bdata); |
| } |
| return rc; |
| } |
| |
| static void ov5647_start_stream(void) |
| { |
| CDBG("CAMERA_DBG: 0x4202 0x0, stream on...\r\n"); |
| ov5647_i2c_write_b_sensor(0x4202, 0x00);/* streaming on */ |
| } |
| |
| static void ov5647_stop_stream(void) |
| { |
| CDBG("CAMERA_DBG: 0x4202 0xf, stream off...\r\n"); |
| ov5647_i2c_write_b_sensor(0x4202, 0x0f);/* streaming off */ |
| } |
| |
| static void ov5647_group_hold_on(void) |
| { |
| ov5647_i2c_write_b_sensor(0x0104, 0x01); |
| } |
| |
| static void ov5647_group_hold_off(void) |
| { |
| ov5647_i2c_write_b_sensor(0x0104, 0x0); |
| } |
| |
| static void ov5647_get_pict_fps(uint16_t fps, uint16_t *pfps) |
| { |
| /* input fps is preview fps in Q8 format */ |
| uint32_t divider, d1, d2; |
| uint32_t preview_pclk = 0x37, snapshot_pclk = 0x4f; |
| |
| d1 = (prev_frame_length_lines * 0x00000400) / snap_frame_length_lines; |
| d2 = (prev_line_length_pck * 0x00000400) / snap_line_length_pck; |
| divider = (d1 * d2*preview_pclk/snapshot_pclk) / 0x400; |
| CDBG(KERN_ERR "ov5647_get_pict_fps divider = %d", divider); |
| /*Verify PCLK settings and frame sizes.*/ |
| *pfps = (uint16_t) (fps * divider / 0x400); |
| } |
| |
| static uint16_t ov5647_get_prev_lines_pf(void) |
| { |
| if (ov5647_ctrl->prev_res == QTR_SIZE) |
| return prev_frame_length_lines; |
| else |
| return snap_frame_length_lines; |
| } |
| |
| static uint16_t ov5647_get_prev_pixels_pl(void) |
| { |
| if (ov5647_ctrl->prev_res == QTR_SIZE) |
| return prev_line_length_pck; |
| else |
| return snap_line_length_pck; |
| } |
| |
| static uint16_t ov5647_get_pict_lines_pf(void) |
| { |
| if (ov5647_ctrl->pict_res == QTR_SIZE) |
| return prev_frame_length_lines; |
| else |
| return snap_frame_length_lines; |
| } |
| |
| static uint16_t ov5647_get_pict_pixels_pl(void) |
| { |
| if (ov5647_ctrl->pict_res == QTR_SIZE) |
| return prev_line_length_pck; |
| else |
| return snap_line_length_pck; |
| } |
| |
| static uint32_t ov5647_get_pict_max_exp_lc(void) |
| { |
| return snap_frame_length_lines * 24; |
| } |
| |
| static int32_t ov5647_set_fps(struct fps_cfg *fps) |
| { |
| uint16_t total_lines_per_frame; |
| int32_t rc = 0; |
| |
| ov5647_ctrl->fps_divider = fps->fps_div; |
| ov5647_ctrl->pict_fps_divider = fps->pict_fps_div; |
| |
| if (ov5647_ctrl->sensormode == SENSOR_PREVIEW_MODE) { |
| total_lines_per_frame = (uint16_t) |
| ((prev_frame_length_lines * ov5647_ctrl->fps_divider) / 0x400); |
| } else { |
| total_lines_per_frame = (uint16_t) |
| ((snap_frame_length_lines * ov5647_ctrl->fps_divider) / 0x400); |
| } |
| |
| ov5647_group_hold_on(); |
| rc = ov5647_i2c_write_b_sensor(0x0340, |
| ((total_lines_per_frame & 0xFF00) >> 8)); |
| rc = ov5647_i2c_write_b_sensor(0x0341, |
| (total_lines_per_frame & 0x00FF)); |
| ov5647_group_hold_off(); |
| |
| return rc; |
| } |
| |
| static inline uint8_t ov5647_byte(uint16_t word, uint8_t offset) |
| { |
| return word >> (offset * BITS_PER_BYTE); |
| } |
| |
| static int32_t ov5647_write_exp_gain(uint16_t gain, uint32_t line) |
| { |
| int rc = 0; |
| uint16_t max_line; |
| u8 intg_time_hsb, intg_time_msb, intg_time_lsb; |
| uint8_t gain_lsb, gain_hsb; |
| ov5647_ctrl->my_reg_gain = gain; |
| ov5647_ctrl->my_reg_line_count = (uint16_t)line; |
| |
| CDBG(KERN_ERR "preview exposure setting 0x%x, 0x%x, %d", |
| gain, line, line); |
| |
| gain_lsb = (uint8_t) (ov5647_ctrl->my_reg_gain); |
| gain_hsb = (uint8_t)((ov5647_ctrl->my_reg_gain & 0x300)>>8); |
| /* adjust frame rate */ |
| if (line > 980) { |
| rc = ov5647_i2c_write_b_sensor(0x380E, |
| (uint8_t)((line+4) >> 8)) ; |
| rc = ov5647_i2c_write_b_sensor(0x380F, |
| (uint8_t)((line+4) & 0x00FF)) ; |
| max_line = line + 4; |
| } else if (max_line > 984) { |
| rc = ov5647_i2c_write_b_sensor(0x380E, |
| (uint8_t)(984 >> 8)) ; |
| rc = ov5647_i2c_write_b_sensor(0x380F, |
| (uint8_t)(984 & 0x00FF)) ; |
| max_line = 984; |
| } |
| |
| line = line<<4; |
| /* ov5647 need this operation */ |
| intg_time_hsb = (u8)(line>>16); |
| intg_time_msb = (u8) ((line & 0xFF00) >> 8); |
| intg_time_lsb = (u8) (line & 0x00FF); |
| |
| ov5647_group_hold_on(); |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_HSB, intg_time_hsb) ; |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_MSB, intg_time_msb) ; |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_LSB, intg_time_lsb) ; |
| |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_MSB, gain_hsb) ; |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_LSB, gain_lsb) ; |
| ov5647_group_hold_off(); |
| |
| return rc; |
| } |
| |
| |
| static int32_t ov5647_set_pict_exp_gain(uint16_t gain, uint32_t line) |
| { |
| uint16_t max_line; |
| int rc = 0; |
| uint8_t gain_lsb, gain_hsb; |
| u8 intg_time_hsb, intg_time_msb, intg_time_lsb; |
| |
| ov5647_ctrl->my_reg_gain = gain; |
| ov5647_ctrl->my_reg_line_count = (uint16_t)line; |
| |
| gain_lsb = (uint8_t) (ov5647_ctrl->my_reg_gain); |
| gain_hsb = (uint8_t)((ov5647_ctrl->my_reg_gain & 0x300)>>8); |
| |
| CDBG(KERN_ERR "snapshot exposure seting 0x%x, 0x%x, %d" |
| , gain, line, line); |
| |
| if (line > 1964) { |
| rc = ov5647_i2c_write_b_sensor(0x380E, |
| (uint8_t)((line+4) >> 8)) ; |
| rc = ov5647_i2c_write_b_sensor(0x380F, |
| (uint8_t)((line+4) & 0x00FF)) ; |
| max_line = line + 4; |
| } else if (max_line > 1968) { |
| rc = ov5647_i2c_write_b_sensor(0x380E, |
| (uint8_t)(1968 >> 8)) ; |
| rc = ov5647_i2c_write_b_sensor(0x380F, |
| (uint8_t)(1968 & 0x00FF)) ; |
| max_line = 1968; |
| } |
| line = line<<4; |
| /* ov5647 need this operation */ |
| intg_time_hsb = (u8)(line>>16); |
| intg_time_msb = (u8) ((line & 0xFF00) >> 8); |
| intg_time_lsb = (u8) (line & 0x00FF); |
| |
| /* FIXME for BLC trigger */ |
| ov5647_group_hold_on(); |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_HSB, intg_time_hsb) ; |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_MSB, intg_time_msb) ; |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_LSB, intg_time_lsb) ; |
| |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_MSB, gain_hsb) ; |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_LSB, gain_lsb - 1) ; |
| |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_HSB, intg_time_hsb) ; |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_MSB, intg_time_msb) ; |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_LINE_LSB, intg_time_lsb) ; |
| |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_MSB, gain_hsb) ; |
| rc = ov5647_i2c_write_b_sensor(REG_OV5647_GAIN_LSB, gain_lsb) ; |
| ov5647_group_hold_off(); |
| |
| msleep(500); |
| return rc; |
| |
| } |
| |
| static int32_t ov5647_move_focus(int direction, int32_t num_steps) |
| { |
| uint8_t code_val_msb = 0; |
| uint8_t code_val_lsb = 0; |
| int16_t step_direction, actual_step, next_position; |
| int rc; |
| |
| if (num_steps == 0) |
| return 0; |
| |
| if (direction == MOVE_NEAR) |
| step_direction = 20; |
| else if (direction == MOVE_FAR) |
| step_direction = -20; |
| else |
| return -EINVAL; |
| |
| actual_step = (int16_t)(step_direction * num_steps); |
| next_position = (int16_t)ov5647_ctrl->curr_lens_pos + actual_step; |
| if (next_position < 0) { |
| CDBG(KERN_ERR "%s: OV5647 position(=%d) out of range", |
| __func__, next_position); |
| next_position = 0; |
| } |
| if (next_position > 0x3FF) { |
| CDBG(KERN_ERR "%s: OV5647 position(=%d) out of range", |
| __func__, next_position); |
| next_position = 0x3FF; |
| } |
| ov5647_ctrl->curr_lens_pos = next_position; |
| |
| code_val_msb = (uint8_t)((ov5647_ctrl->curr_lens_pos & 0x03FF) >> 4); |
| code_val_lsb = (uint8_t)((ov5647_ctrl->curr_lens_pos & 0x000F) << 4); |
| code_val_lsb |= mode_mask; |
| |
| rc = ov5647_af_i2c_write_b_sensor(code_val_msb, code_val_lsb); |
| /* DAC Setting */ |
| if (rc != 0) { |
| CDBG(KERN_ERR "%s: WRITE ERROR lsb = 0x%x, msb = 0x%x", |
| __func__, code_val_lsb, code_val_msb); |
| } else { |
| CDBG(KERN_ERR "%s: Successful lsb = 0x%x, msb = 0x%x", |
| __func__, code_val_lsb, code_val_msb); |
| /* delay may set based on the steps moved |
| when I2C write successful */ |
| msleep(100); |
| } |
| return 0; |
| } |
| |
| static int32_t ov5647_set_default_focus(uint8_t af_step) |
| { |
| uint8_t code_val_msb = 0; |
| uint8_t code_val_lsb = 0; |
| int rc = 0; |
| |
| ov5647_ctrl->curr_lens_pos = 200; |
| |
| |
| code_val_msb = (ov5647_ctrl->curr_lens_pos & 0x03FF) >> 4; |
| code_val_lsb = (ov5647_ctrl->curr_lens_pos & 0x000F) << 4; |
| code_val_lsb |= mode_mask; |
| |
| CDBG(KERN_ERR "ov5647_set_default_focus:lens pos = %d", |
| ov5647_ctrl->curr_lens_pos); |
| rc = ov5647_af_i2c_write_b_sensor(code_val_msb, code_val_lsb); |
| /* DAC Setting */ |
| if (rc != 0) |
| CDBG(KERN_ERR "%s: WRITE ERROR lsb = 0x%x, msb = 0x%x", |
| __func__, code_val_lsb, code_val_msb); |
| else |
| CDBG(KERN_ERR "%s: WRITE successful lsb = 0x%x, msb = 0x%x", |
| __func__, code_val_lsb, code_val_msb); |
| |
| usleep_range(10000, 11000); |
| return 0; |
| } |
| |
| static int32_t ov5647_test(enum ov5647_test_mode_t mo) |
| { |
| int32_t rc = 0; |
| |
| if (mo != TEST_OFF) |
| rc = ov5647_i2c_write_b_sensor(0x0601, (uint8_t) mo); |
| |
| return rc; |
| } |
| |
| static void ov5647_reset_sensor(void) |
| { |
| ov5647_i2c_write_b_sensor(0x103, 0x1); |
| } |
| |
| |
| static int32_t ov5647_sensor_setting(int update_type, int rt) |
| { |
| |
| int32_t rc = 0; |
| struct msm_camera_csi_params ov5647_csi_params; |
| |
| ov5647_stop_stream(); |
| |
| /* wait for clk/data really stop */ |
| if ((rt == RES_CAPTURE) || (CSI_CONFIG == 0)) |
| msleep(66); |
| else |
| msleep(266); |
| |
| CDBG("CAMERA_DBG1: 0x4800 regVal:0x25\r\n"); |
| ov5647_i2c_write_b_sensor(0x4800, 0x25);/* streaming off */ |
| |
| usleep_range(10000, 11000); |
| |
| if (update_type == REG_INIT) { |
| ov5647_reset_sensor(); |
| ov5647_i2c_write_b_table(ov5647_regs.rec_settings, |
| ov5647_regs.rec_size); |
| CSI_CONFIG = 0; |
| } else if (update_type == UPDATE_PERIODIC) { |
| /* turn off flash when preview */ |
| |
| if (rt == RES_PREVIEW) { |
| ov5647_i2c_write_b_table(ov5647_regs.reg_prev, |
| ov5647_regs.reg_prev_size); |
| CDBG("CAMERA_DBG:preview settings...\r\n"); |
| } else { |
| ov5647_i2c_write_b_table(ov5647_regs.reg_snap, |
| ov5647_regs.reg_snap_size); |
| CDBG("CAMERA_DBG:snapshot settings...\r\n"); |
| } |
| |
| msleep(20); |
| if (!CSI_CONFIG) { |
| msm_camio_vfe_clk_rate_set(192000000); |
| ov5647_csi_params.data_format = CSI_8BIT; |
| ov5647_csi_params.lane_cnt = 2; |
| ov5647_csi_params.lane_assign = 0xe4; |
| ov5647_csi_params.dpcm_scheme = 0; |
| ov5647_csi_params.settle_cnt = 10; |
| rc = msm_camio_csi_config(&ov5647_csi_params); |
| msleep(20); |
| CSI_CONFIG = 1; |
| /* exit powerdown state */ |
| ov5647_i2c_write_b_sensor(0x0100, 0x01); |
| } |
| CDBG("CAMERA_DBG: 0x4800 regVal:0x04\r\n"); |
| /* streaming on */ |
| ov5647_i2c_write_b_sensor(0x4800, 0x04); |
| msleep(266); |
| ov5647_start_stream(); |
| msleep(30); |
| } |
| return rc; |
| } |
| |
| static int32_t ov5647_video_config(int mode) |
| { |
| int32_t rc = 0; |
| int rt; |
| CDBG("video config\n"); |
| /* change sensor resolution if needed */ |
| if (ov5647_ctrl->prev_res == QTR_SIZE) |
| rt = RES_PREVIEW; |
| else |
| rt = RES_CAPTURE; |
| if (ov5647_sensor_setting(UPDATE_PERIODIC, rt) < 0) |
| return rc; |
| if (ov5647_ctrl->set_test) { |
| if (ov5647_test(ov5647_ctrl->set_test) < 0) |
| return rc; |
| } |
| |
| ov5647_ctrl->curr_res = ov5647_ctrl->prev_res; |
| ov5647_ctrl->sensormode = mode; |
| return rc; |
| } |
| |
| static int32_t ov5647_snapshot_config(int mode) |
| { |
| int32_t rc = 0; |
| int rt; |
| |
| /*change sensor resolution if needed */ |
| if (ov5647_ctrl->curr_res != ov5647_ctrl->pict_res) { |
| if (ov5647_ctrl->pict_res == QTR_SIZE) |
| rt = RES_PREVIEW; |
| else |
| rt = RES_CAPTURE; |
| if (ov5647_sensor_setting(UPDATE_PERIODIC, rt) < 0) |
| return rc; |
| } |
| |
| ov5647_ctrl->curr_res = ov5647_ctrl->pict_res; |
| ov5647_ctrl->sensormode = mode; |
| return rc; |
| } |
| |
| static int32_t ov5647_raw_snapshot_config(int mode) |
| { |
| int32_t rc = 0; |
| int rt; |
| |
| /* change sensor resolution if needed */ |
| if (ov5647_ctrl->curr_res != ov5647_ctrl->pict_res) { |
| if (ov5647_ctrl->pict_res == QTR_SIZE) |
| rt = RES_PREVIEW; |
| else |
| rt = RES_CAPTURE; |
| if (ov5647_sensor_setting(UPDATE_PERIODIC, rt) < 0) |
| return rc; |
| } |
| |
| ov5647_ctrl->curr_res = ov5647_ctrl->pict_res; |
| ov5647_ctrl->sensormode = mode; |
| return rc; |
| } |
| |
| static int32_t ov5647_set_sensor_mode(int mode, |
| int res) |
| { |
| int32_t rc = 0; |
| |
| switch (mode) { |
| case SENSOR_PREVIEW_MODE: |
| rc = ov5647_video_config(mode); |
| break; |
| case SENSOR_SNAPSHOT_MODE: |
| rc = ov5647_snapshot_config(mode); |
| break; |
| case SENSOR_RAW_SNAPSHOT_MODE: |
| rc = ov5647_raw_snapshot_config(mode); |
| break; |
| default: |
| rc = -EINVAL; |
| break; |
| } |
| return rc; |
| } |
| |
| static int32_t ov5647_power_down(void) |
| { |
| ov5647_stop_stream(); |
| return 0; |
| } |
| |
| static int ov5647_probe_init_done(const struct msm_camera_sensor_info *data) |
| { |
| CDBG("probe done\n"); |
| gpio_direction_output(data->sensor_pwd, 1); |
| return 0; |
| } |
| |
| static int ov5647_probe_init_sensor(const struct msm_camera_sensor_info *data) |
| { |
| int32_t rc = 0; |
| uint16_t regaddress1 = 0x300a; |
| uint16_t regaddress2 = 0x300b; |
| uint16_t chipid1 = 0; |
| uint16_t chipid2 = 0; |
| |
| CDBG("%s: %d\n", __func__, __LINE__); |
| |
| gpio_direction_output(data->sensor_pwd, 0); |
| usleep_range(4000, 4100); |
| gpio_direction_output(data->sensor_reset, 1); |
| usleep_range(2000, 2100); |
| |
| ov5647_i2c_read(regaddress1, &chipid1); |
| if (chipid1 != 0x56) { |
| rc = -ENODEV; |
| pr_err("ov5647_probe_init_sensor fail chip id doesnot match\n"); |
| goto init_probe_fail; |
| } |
| |
| ov5647_i2c_read(regaddress2, &chipid2); |
| if (chipid2 != 0x47) { |
| rc = -ENODEV; |
| pr_err("ov5647_probe_init_sensor fail chip id doesnot match\n"); |
| goto init_probe_fail; |
| } |
| |
| pr_err("ID1: 0x%x\n", chipid1); |
| pr_err("ID2: 0x%x\n", chipid2); |
| goto init_probe_done; |
| |
| init_probe_fail: |
| pr_err(" ov5647_probe_init_sensor fails\n"); |
| ov5647_probe_init_done(data); |
| return rc; |
| init_probe_done: |
| pr_debug(" ov5647_probe_init_sensor finishes\n"); |
| gpio_direction_output(data->sensor_pwd, 1); |
| return rc; |
| } |
| |
| |
| static int ov5647_sensor_open_init(const struct msm_camera_sensor_info *data) |
| { |
| int32_t rc = 0; |
| |
| CDBG("%s: %d\n", __func__, __LINE__); |
| CDBG("Calling ov5647_sensor_open_init\n"); |
| |
| ov5647_ctrl = kzalloc(sizeof(struct ov5647_ctrl_t), GFP_KERNEL); |
| if (!ov5647_ctrl) { |
| CDBG("ov5647_init failed!\n"); |
| rc = -ENOMEM; |
| goto init_done; |
| } |
| ov5647_ctrl->fps_divider = 1 * 0x00000400; |
| ov5647_ctrl->pict_fps_divider = 1 * 0x00000400; |
| ov5647_ctrl->set_test = TEST_OFF; |
| ov5647_ctrl->prev_res = QTR_SIZE; |
| ov5647_ctrl->pict_res = FULL_SIZE; |
| |
| if (data) |
| ov5647_ctrl->sensordata = data; |
| |
| prev_frame_length_lines = 0x3d8; |
| |
| prev_line_length_pck = 0x768*2; |
| |
| snap_frame_length_lines = 0x7b0; |
| |
| snap_line_length_pck = 0xa8c; |
| |
| /* enable mclk first */ |
| msm_camio_clk_rate_set(OV5647_MASTER_CLK_RATE); |
| |
| gpio_direction_output(data->sensor_pwd, 1); |
| gpio_direction_output(data->sensor_reset, 0); |
| usleep_range(10000, 11000); |
| /* power on camera ldo and vreg */ |
| if (ov5647_ctrl->sensordata->pmic_gpio_enable) |
| lcd_camera_power_onoff(1); |
| usleep_range(10000, 11000); /*waiting for ldo stable*/ |
| gpio_direction_output(data->sensor_pwd, 0); |
| msleep(20); |
| gpio_direction_output(data->sensor_reset, 1); |
| msleep(25); |
| |
| CDBG("init settings\n"); |
| if (ov5647_ctrl->prev_res == QTR_SIZE) |
| rc = ov5647_sensor_setting(REG_INIT, RES_PREVIEW); |
| else |
| rc = ov5647_sensor_setting(REG_INIT, RES_CAPTURE); |
| ov5647_ctrl->fps = 30 * Q8; |
| |
| /* enable AF actuator */ |
| if (ov5647_ctrl->sensordata->vcm_enable) { |
| CDBG("enable AF actuator, gpio = %d\n", |
| ov5647_ctrl->sensordata->vcm_pwd); |
| rc = gpio_request(ov5647_ctrl->sensordata->vcm_pwd, |
| "ov5647_af"); |
| if (!rc) |
| gpio_direction_output( |
| ov5647_ctrl->sensordata->vcm_pwd, |
| 1); |
| else { |
| pr_err("ov5647_ctrl gpio request failed!\n"); |
| goto init_fail; |
| } |
| msleep(20); |
| rc = ov5647_set_default_focus(0); |
| if (rc < 0) { |
| gpio_direction_output(ov5647_ctrl->sensordata->vcm_pwd, |
| 0); |
| gpio_free(ov5647_ctrl->sensordata->vcm_pwd); |
| } |
| } |
| if (rc < 0) |
| goto init_fail; |
| else |
| goto init_done; |
| init_fail: |
| CDBG("init_fail\n"); |
| ov5647_probe_init_done(data); |
| /* No need to power OFF camera ldo and vreg |
| affects Display while resume */ |
| init_done: |
| CDBG("init_done\n"); |
| return rc; |
| } |
| |
| static int ov5647_i2c_remove(struct i2c_client *client) |
| { |
| return 0; |
| } |
| |
| static int ov5647_init_client(struct i2c_client *client) |
| { |
| /* Initialize the MSM_CAMI2C Chip */ |
| init_waitqueue_head(&ov5647_wait_queue); |
| return 0; |
| } |
| |
| static int ov5647_af_init_client(struct i2c_client *client) |
| { |
| /* Initialize the MSM_CAMI2C Chip */ |
| init_waitqueue_head(&ov5647_af_wait_queue); |
| return 0; |
| } |
| |
| static const struct i2c_device_id ov5647_af_i2c_id[] = { |
| {"ov5647_af", 0}, |
| { } |
| }; |
| |
| static int ov5647_af_i2c_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int rc = 0; |
| CDBG("ov5647_af_probe called!\n"); |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| CDBG("i2c_check_functionality failed\n"); |
| goto probe_failure; |
| } |
| |
| ov5647_af_sensorw = kzalloc(sizeof(struct ov5647_work_t), GFP_KERNEL); |
| if (!ov5647_af_sensorw) { |
| CDBG("kzalloc failed.\n"); |
| rc = -ENOMEM; |
| goto probe_failure; |
| } |
| |
| i2c_set_clientdata(client, ov5647_af_sensorw); |
| ov5647_af_init_client(client); |
| ov5647_af_client = client; |
| |
| msleep(50); |
| |
| CDBG("ov5647_af_probe successed! rc = %d\n", rc); |
| return 0; |
| |
| probe_failure: |
| CDBG("ov5647_af_probe failed! rc = %d\n", rc); |
| return rc; |
| } |
| |
| static const struct i2c_device_id ov5647_i2c_id[] = { |
| {"ov5647", 0}, {} |
| }; |
| |
| static int ov5647_i2c_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int rc = 0; |
| CDBG("ov5647_probe called!\n"); |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| CDBG("i2c_check_functionality failed\n"); |
| goto probe_failure; |
| } |
| |
| ov5647_sensorw = kzalloc(sizeof(struct ov5647_work_t), GFP_KERNEL); |
| if (!ov5647_sensorw) { |
| CDBG("kzalloc failed.\n"); |
| rc = -ENOMEM; |
| goto probe_failure; |
| } |
| |
| i2c_set_clientdata(client, ov5647_sensorw); |
| ov5647_init_client(client); |
| ov5647_client = client; |
| |
| msleep(50); |
| |
| CDBG("ov5647_probe successed! rc = %d\n", rc); |
| return 0; |
| |
| probe_failure: |
| CDBG("ov5647_probe failed! rc = %d\n", rc); |
| return rc; |
| } |
| |
| static int __devexit ov5647_remove(struct i2c_client *client) |
| { |
| struct ov5647_work_t *sensorw = i2c_get_clientdata(client); |
| free_irq(client->irq, sensorw); |
| ov5647_client = NULL; |
| kfree(sensorw); |
| return 0; |
| } |
| |
| static int __devexit ov5647_af_remove(struct i2c_client *client) |
| { |
| struct ov5647_work_t *ov5647_af = i2c_get_clientdata(client); |
| free_irq(client->irq, ov5647_af); |
| ov5647_af_client = NULL; |
| kfree(ov5647_af); |
| return 0; |
| } |
| |
| static struct i2c_driver ov5647_i2c_driver = { |
| .id_table = ov5647_i2c_id, |
| .probe = ov5647_i2c_probe, |
| .remove = ov5647_i2c_remove, |
| .driver = { |
| .name = "ov5647", |
| }, |
| }; |
| |
| static struct i2c_driver ov5647_af_i2c_driver = { |
| .id_table = ov5647_af_i2c_id, |
| .probe = ov5647_af_i2c_probe, |
| .remove = __exit_p(ov5647_af_i2c_remove), |
| .driver = { |
| .name = "ov5647_af", |
| }, |
| }; |
| |
| int ov5647_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(&ov5647_mut); |
| CDBG("ov5647_sensor_config: cfgtype = %d\n", |
| cdata.cfgtype); |
| switch (cdata.cfgtype) { |
| case CFG_GET_PICT_FPS: |
| ov5647_get_pict_fps( |
| cdata.cfg.gfps.prevfps, |
| &(cdata.cfg.gfps.pictfps)); |
| |
| if (copy_to_user((void *)argp, |
| &cdata, |
| sizeof(struct sensor_cfg_data))) |
| rc = -EFAULT; |
| break; |
| case CFG_GET_PREV_L_PF: |
| cdata.cfg.prevl_pf = |
| ov5647_get_prev_lines_pf(); |
| |
| if (copy_to_user((void *)argp, |
| &cdata, |
| sizeof(struct sensor_cfg_data))) |
| rc = -EFAULT; |
| break; |
| case CFG_GET_PREV_P_PL: |
| cdata.cfg.prevp_pl = |
| ov5647_get_prev_pixels_pl(); |
| |
| if (copy_to_user((void *)argp, |
| &cdata, |
| sizeof(struct sensor_cfg_data))) |
| rc = -EFAULT; |
| break; |
| case CFG_GET_PICT_L_PF: |
| cdata.cfg.pictl_pf = |
| ov5647_get_pict_lines_pf(); |
| |
| if (copy_to_user((void *)argp, |
| &cdata, |
| sizeof(struct sensor_cfg_data))) |
| rc = -EFAULT; |
| break; |
| case CFG_GET_PICT_P_PL: |
| cdata.cfg.pictp_pl = |
| ov5647_get_pict_pixels_pl(); |
| if (copy_to_user((void *)argp, |
| &cdata, |
| sizeof(struct sensor_cfg_data))) |
| rc = -EFAULT; |
| break; |
| case CFG_GET_PICT_MAX_EXP_LC: |
| cdata.cfg.pict_max_exp_lc = |
| ov5647_get_pict_max_exp_lc(); |
| |
| if (copy_to_user((void *)argp, |
| &cdata, |
| sizeof(struct sensor_cfg_data))) |
| rc = -EFAULT; |
| break; |
| case CFG_SET_FPS: |
| case CFG_SET_PICT_FPS: |
| rc = ov5647_set_fps(&(cdata.cfg.fps)); |
| break; |
| case CFG_SET_EXP_GAIN: |
| rc = ov5647_write_exp_gain(cdata.cfg.exp_gain.gain, |
| cdata.cfg.exp_gain.line); |
| break; |
| case CFG_SET_PICT_EXP_GAIN: |
| rc = ov5647_set_pict_exp_gain(cdata.cfg.exp_gain.gain, |
| cdata.cfg.exp_gain.line); |
| break; |
| case CFG_SET_MODE: |
| rc = ov5647_set_sensor_mode(cdata.mode, cdata.rs); |
| break; |
| case CFG_PWR_DOWN: |
| rc = ov5647_power_down(); |
| break; |
| case CFG_MOVE_FOCUS: |
| rc = ov5647_move_focus(cdata.cfg.focus.dir, |
| cdata.cfg.focus.steps); |
| break; |
| case CFG_SET_DEFAULT_FOCUS: |
| rc = ov5647_set_default_focus(cdata.cfg.focus.steps); |
| break; |
| |
| case CFG_GET_AF_MAX_STEPS: |
| cdata.max_steps = OV5647_TOTAL_STEPS_NEAR_TO_FAR; |
| if (copy_to_user((void *)argp, |
| &cdata, |
| sizeof(struct sensor_cfg_data))) |
| rc = -EFAULT; |
| break; |
| case CFG_SET_EFFECT: |
| rc = ov5647_set_default_focus(cdata.cfg.effect); |
| break; |
| default: |
| rc = -EFAULT; |
| break; |
| } |
| mutex_unlock(&ov5647_mut); |
| |
| return rc; |
| } |
| |
| static int ov5647_sensor_release(void) |
| { |
| int rc = -EBADF; |
| unsigned short rdata; |
| |
| mutex_lock(&ov5647_mut); |
| ov5647_power_down(); |
| msleep(20); |
| ov5647_i2c_read(0x3018, &rdata); |
| rdata |= 0x18; /*set bit 3 bit 4 to 1*/ |
| ov5647_i2c_write_b_sensor(0x3018, rdata);/*write back*/ |
| msleep(20); |
| |
| gpio_set_value(ov5647_ctrl->sensordata->sensor_pwd, 1); |
| usleep_range(5000, 5100); |
| if (ov5647_ctrl->sensordata->vcm_enable) { |
| gpio_direction_output(ov5647_ctrl->sensordata->vcm_pwd, 0); |
| gpio_free(ov5647_ctrl->sensordata->vcm_pwd); |
| } |
| |
| /* No need to power OFF camera ldo and vreg |
| affects Display while resume */ |
| |
| kfree(ov5647_ctrl); |
| ov5647_ctrl = NULL; |
| CDBG("ov5647_release completed\n"); |
| mutex_unlock(&ov5647_mut); |
| |
| return rc; |
| } |
| |
| static int ov5647_sensor_probe(const struct msm_camera_sensor_info *info, |
| struct msm_sensor_ctrl *s) |
| { |
| int rc = 0; |
| |
| CDBG("%s E\n", __func__); |
| |
| gpio_direction_output(info->sensor_pwd, 1); |
| gpio_direction_output(info->sensor_reset, 0); |
| usleep_range(1000, 1100); |
| /* turn on ldo and vreg */ |
| if (info->pmic_gpio_enable) |
| lcd_camera_power_onoff(1); |
| |
| rc = i2c_add_driver(&ov5647_i2c_driver); |
| if (rc < 0 || ov5647_client == NULL) { |
| rc = -ENOTSUPP; |
| CDBG("I2C add driver ov5647 failed"); |
| goto probe_fail_2; |
| } |
| if (info->vcm_enable) { |
| rc = i2c_add_driver(&ov5647_af_i2c_driver); |
| if (rc < 0 || ov5647_af_client == NULL) { |
| rc = -ENOTSUPP; |
| CDBG("I2C add driver ov5647 af failed"); |
| goto probe_fail_3; |
| } |
| } |
| msm_camio_clk_rate_set(OV5647_MASTER_CLK_RATE); |
| |
| rc = ov5647_probe_init_sensor(info); |
| if (rc < 0) |
| goto probe_fail_1; |
| |
| s->s_init = ov5647_sensor_open_init; |
| s->s_release = ov5647_sensor_release; |
| s->s_config = ov5647_sensor_config; |
| s->s_mount_angle = info->sensor_platform_info->mount_angle; |
| gpio_set_value(info->sensor_pwd, 1); |
| ov5647_probe_init_done(info); |
| /* turn off ldo and vreg */ |
| if (info->pmic_gpio_enable) |
| lcd_camera_power_onoff(0); |
| |
| CDBG("%s X", __func__); |
| return rc; |
| |
| probe_fail_3: |
| i2c_del_driver(&ov5647_af_i2c_driver); |
| probe_fail_2: |
| i2c_del_driver(&ov5647_i2c_driver); |
| probe_fail_1: |
| /* turn off ldo and vreg */ |
| if (info->pmic_gpio_enable) |
| lcd_camera_power_onoff(0); |
| CDBG("ov5647_sensor_probe: SENSOR PROBE FAILS!\n"); |
| CDBG("%s X", __func__); |
| return rc; |
| } |
| |
| static int __devinit ov5647_probe(struct platform_device *pdev) |
| { |
| return msm_camera_drv_start(pdev, ov5647_sensor_probe); |
| } |
| |
| static struct platform_driver msm_camera_driver = { |
| .probe = ov5647_probe, |
| .driver = { |
| .name = "msm_camera_ov5647", |
| .owner = THIS_MODULE, |
| }, |
| }; |
| |
| static int __init ov5647_init(void) |
| { |
| return platform_driver_register(&msm_camera_driver); |
| } |
| |
| module_init(ov5647_init); |
| MODULE_DESCRIPTION("Omnivision 5 MP Bayer sensor driver"); |
| MODULE_LICENSE("GPL v2"); |