| /* |
| * Copyright (C) 2008-2009 QUALCOMM Incorporated. |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/types.h> |
| #include <linux/slab.h> |
| #include <linux/i2c.h> |
| #include <linux/uaccess.h> |
| #include <linux/miscdevice.h> |
| #include <linux/kernel.h> |
| #include <media/msm_camera.h> |
| #include <mach/gpio.h> |
| #include <mach/camera.h> |
| #include <asm/mach-types.h> |
| #include "mt9t013.h" |
| |
| /*============================================================= |
| SENSOR REGISTER DEFINES |
| ==============================================================*/ |
| #define MT9T013_REG_MODEL_ID 0x0000 |
| #define MT9T013_MODEL_ID 0x2600 |
| #define REG_GROUPED_PARAMETER_HOLD 0x0104 |
| #define GROUPED_PARAMETER_HOLD 0x0100 |
| #define GROUPED_PARAMETER_UPDATE 0x0000 |
| #define REG_COARSE_INT_TIME 0x3012 |
| #define REG_VT_PIX_CLK_DIV 0x0300 |
| #define REG_VT_SYS_CLK_DIV 0x0302 |
| #define REG_PRE_PLL_CLK_DIV 0x0304 |
| #define REG_PLL_MULTIPLIER 0x0306 |
| #define REG_OP_PIX_CLK_DIV 0x0308 |
| #define REG_OP_SYS_CLK_DIV 0x030A |
| #define REG_SCALE_M 0x0404 |
| #define REG_FRAME_LENGTH_LINES 0x300A |
| #define REG_LINE_LENGTH_PCK 0x300C |
| #define REG_X_ADDR_START 0x3004 |
| #define REG_Y_ADDR_START 0x3002 |
| #define REG_X_ADDR_END 0x3008 |
| #define REG_Y_ADDR_END 0x3006 |
| #define REG_X_OUTPUT_SIZE 0x034C |
| #define REG_Y_OUTPUT_SIZE 0x034E |
| #define REG_FINE_INT_TIME 0x3014 |
| #define REG_ROW_SPEED 0x3016 |
| #define MT9T013_REG_RESET_REGISTER 0x301A |
| #define MT9T013_RESET_REGISTER_PWON 0x10CC |
| #define MT9T013_RESET_REGISTER_PWOFF 0x1008 /* 0x10C8 stop streaming*/ |
| #define REG_READ_MODE 0x3040 |
| #define REG_GLOBAL_GAIN 0x305E |
| #define REG_TEST_PATTERN_MODE 0x3070 |
| |
| |
| enum mt9t013_test_mode { |
| TEST_OFF, |
| TEST_1, |
| TEST_2, |
| TEST_3 |
| }; |
| |
| enum mt9t013_resolution { |
| QTR_SIZE, |
| FULL_SIZE, |
| INVALID_SIZE |
| }; |
| |
| enum mt9t013_reg_update { |
| REG_INIT, /* registers that need to be updated during initialization */ |
| UPDATE_PERIODIC, /* registers that needs periodic I2C writes */ |
| UPDATE_ALL, /* all registers will be updated */ |
| UPDATE_INVALID |
| }; |
| |
| enum mt9t013_setting { |
| RES_PREVIEW, |
| RES_CAPTURE |
| }; |
| |
| /* actuator's Slave Address */ |
| #define MT9T013_AF_I2C_ADDR 0x18 |
| |
| /* |
| * AF Total steps parameters |
| */ |
| #define MT9T013_TOTAL_STEPS_NEAR_TO_FAR 30 |
| |
| /* |
| * Time in milisecs for waiting for the sensor to reset. |
| */ |
| #define MT9T013_RESET_DELAY_MSECS 66 |
| |
| /* for 30 fps preview */ |
| #define MT9T013_DEFAULT_CLOCK_RATE 24000000 |
| #define MT9T013_DEFAULT_MAX_FPS 26 |
| |
| |
| /* FIXME: Changes from here */ |
| struct mt9t013_work { |
| struct work_struct work; |
| }; |
| |
| static struct mt9t013_work *mt9t013_sensorw; |
| static struct i2c_client *mt9t013_client; |
| |
| struct mt9t013_ctrl { |
| const struct msm_camera_sensor_info *sensordata; |
| |
| int sensormode; |
| uint32_t fps_divider; /* init to 1 * 0x00000400 */ |
| uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ |
| |
| uint16_t curr_lens_pos; |
| uint16_t init_curr_lens_pos; |
| uint16_t my_reg_gain; |
| uint32_t my_reg_line_count; |
| |
| enum mt9t013_resolution prev_res; |
| enum mt9t013_resolution pict_res; |
| enum mt9t013_resolution curr_res; |
| enum mt9t013_test_mode set_test; |
| |
| unsigned short imgaddr; |
| }; |
| |
| |
| static struct mt9t013_ctrl *mt9t013_ctrl; |
| static DECLARE_WAIT_QUEUE_HEAD(mt9t013_wait_queue); |
| DECLARE_MUTEX(mt9t013_sem); |
| |
| extern struct mt9t013_reg mt9t013_regs; /* from mt9t013_reg.c */ |
| |
| static int mt9t013_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 = length, |
| .buf = rxdata, |
| }, |
| }; |
| |
| if (i2c_transfer(mt9t013_client->adapter, msgs, 2) < 0) { |
| pr_err("mt9t013_i2c_rxdata failed!\n"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int32_t mt9t013_i2c_read_w(unsigned short saddr, |
| unsigned short raddr, unsigned short *rdata) |
| { |
| int32_t rc = 0; |
| unsigned char buf[4]; |
| |
| if (!rdata) |
| return -EIO; |
| |
| memset(buf, 0, sizeof(buf)); |
| |
| buf[0] = (raddr & 0xFF00)>>8; |
| buf[1] = (raddr & 0x00FF); |
| |
| rc = mt9t013_i2c_rxdata(saddr, buf, 2); |
| if (rc < 0) |
| return rc; |
| |
| *rdata = buf[0] << 8 | buf[1]; |
| |
| if (rc < 0) |
| pr_err("mt9t013_i2c_read failed!\n"); |
| |
| return rc; |
| } |
| |
| static int32_t mt9t013_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(mt9t013_client->adapter, msg, 1) < 0) { |
| pr_err("mt9t013_i2c_txdata failed\n"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int32_t mt9t013_i2c_write_b(unsigned short saddr, |
| unsigned short waddr, unsigned short wdata) |
| { |
| int32_t rc = -EIO; |
| unsigned char buf[2]; |
| |
| memset(buf, 0, sizeof(buf)); |
| buf[0] = waddr; |
| buf[1] = wdata; |
| rc = mt9t013_i2c_txdata(saddr, buf, 2); |
| |
| if (rc < 0) |
| pr_err("i2c_write failed, addr = 0x%x, val = 0x%x!\n", |
| waddr, wdata); |
| |
| return rc; |
| } |
| |
| static int32_t mt9t013_i2c_write_w(unsigned short saddr, |
| unsigned short waddr, unsigned short wdata) |
| { |
| int32_t rc = -EIO; |
| unsigned char buf[4]; |
| |
| memset(buf, 0, sizeof(buf)); |
| buf[0] = (waddr & 0xFF00)>>8; |
| buf[1] = (waddr & 0x00FF); |
| buf[2] = (wdata & 0xFF00)>>8; |
| buf[3] = (wdata & 0x00FF); |
| |
| rc = mt9t013_i2c_txdata(saddr, buf, 4); |
| |
| if (rc < 0) |
| pr_err("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", |
| waddr, wdata); |
| |
| return rc; |
| } |
| |
| static int32_t mt9t013_i2c_write_w_table( |
| struct mt9t013_i2c_reg_conf *reg_conf_tbl, int num_of_items_in_table) |
| { |
| int i; |
| int32_t rc = -EIO; |
| |
| for (i = 0; i < num_of_items_in_table; i++) { |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| reg_conf_tbl->waddr, reg_conf_tbl->wdata); |
| if (rc < 0) |
| break; |
| reg_conf_tbl++; |
| } |
| |
| return rc; |
| } |
| |
| static int32_t mt9t013_test(enum mt9t013_test_mode mo) |
| { |
| int32_t rc = 0; |
| |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_HOLD); |
| if (rc < 0) |
| return rc; |
| |
| if (mo == TEST_OFF) |
| return 0; |
| else { |
| rc = mt9t013_i2c_write_w_table(mt9t013_regs.ttbl, |
| mt9t013_regs.ttbl_size); |
| if (rc < 0) |
| return rc; |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_TEST_PATTERN_MODE, (uint16_t)mo); |
| if (rc < 0) |
| return rc; |
| } |
| |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_UPDATE); |
| if (rc < 0) |
| return rc; |
| |
| return rc; |
| } |
| |
| static int32_t mt9t013_set_lc(void) |
| { |
| int32_t rc; |
| |
| rc = mt9t013_i2c_write_w_table(mt9t013_regs.lctbl, mt9t013_regs.lctbl_size); |
| if (rc < 0) |
| return rc; |
| |
| return rc; |
| } |
| |
| static int32_t mt9t013_set_default_focus(uint8_t af_step) |
| { |
| int32_t rc = 0; |
| uint8_t code_val_msb, code_val_lsb; |
| code_val_msb = 0x01; |
| code_val_lsb = af_step; |
| |
| /* Write the digital code for current to the actuator */ |
| rc = mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1, |
| code_val_msb, code_val_lsb); |
| |
| mt9t013_ctrl->curr_lens_pos = 0; |
| mt9t013_ctrl->init_curr_lens_pos = 0; |
| return rc; |
| } |
| |
| static void mt9t013_get_pict_fps(uint16_t fps, uint16_t *pfps) |
| { |
| /* input fps is preview fps in Q8 format */ |
| uint32_t divider; /*Q10 */ |
| uint32_t pclk_mult; /*Q10 */ |
| |
| if (mt9t013_ctrl->prev_res == QTR_SIZE) { |
| divider = |
| (uint32_t)( |
| ((mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines * |
| mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck) * |
| 0x00000400) / |
| (mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines * |
| mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck)); |
| |
| pclk_mult = |
| (uint32_t) ((mt9t013_regs.reg_pat[RES_CAPTURE].pll_multiplier * |
| 0x00000400) / |
| (mt9t013_regs.reg_pat[RES_PREVIEW].pll_multiplier)); |
| |
| } else { |
| /* full size resolution used for preview. */ |
| divider = 0x00000400; /*1.0 */ |
| pclk_mult = 0x00000400; /*1.0 */ |
| } |
| |
| /* Verify PCLK settings and frame sizes. */ |
| *pfps = |
| (uint16_t) (fps * divider * pclk_mult / |
| 0x00000400 / 0x00000400); |
| } |
| |
| static uint16_t mt9t013_get_prev_lines_pf(void) |
| { |
| if (mt9t013_ctrl->prev_res == QTR_SIZE) |
| return mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines; |
| else |
| return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines; |
| } |
| |
| static uint16_t mt9t013_get_prev_pixels_pl(void) |
| { |
| if (mt9t013_ctrl->prev_res == QTR_SIZE) |
| return mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck; |
| else |
| return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck; |
| } |
| |
| static uint16_t mt9t013_get_pict_lines_pf(void) |
| { |
| return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines; |
| } |
| |
| static uint16_t mt9t013_get_pict_pixels_pl(void) |
| { |
| return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck; |
| } |
| |
| static uint32_t mt9t013_get_pict_max_exp_lc(void) |
| { |
| uint16_t snapshot_lines_per_frame; |
| |
| if (mt9t013_ctrl->pict_res == QTR_SIZE) { |
| snapshot_lines_per_frame = |
| mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1; |
| } else { |
| snapshot_lines_per_frame = |
| mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1; |
| } |
| |
| return snapshot_lines_per_frame * 24; |
| } |
| |
| static int32_t mt9t013_set_fps(struct fps_cfg *fps) |
| { |
| /* input is new fps in Q8 format */ |
| int32_t rc = 0; |
| |
| mt9t013_ctrl->fps_divider = fps->fps_div; |
| mt9t013_ctrl->pict_fps_divider = fps->pict_fps_div; |
| |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_HOLD); |
| if (rc < 0) |
| return -EBUSY; |
| |
| CDBG("mt9t013_set_fps: fps_div is %d, frame_rate is %d\n", |
| fps->fps_div, |
| (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW]. |
| frame_length_lines * |
| fps->fps_div/0x00000400)); |
| |
| CDBG("mt9t013_set_fps: fps_mult is %d, frame_rate is %d\n", |
| fps->f_mult, |
| (uint16_t)(mt9t013_regs.reg_pat[RES_PREVIEW]. |
| line_length_pck * |
| fps->f_mult / 0x00000400)); |
| |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_LINE_LENGTH_PCK, |
| (uint16_t) ( |
| mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck * |
| fps->f_mult / 0x00000400)); |
| if (rc < 0) |
| return rc; |
| |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_UPDATE); |
| if (rc < 0) |
| return rc; |
| |
| return rc; |
| } |
| |
| static int32_t mt9t013_write_exp_gain(uint16_t gain, uint32_t line) |
| { |
| const uint16_t max_legal_gain = 0x01FF; |
| uint32_t line_length_ratio = 0x00000400; |
| enum mt9t013_setting setting; |
| int32_t rc = 0; |
| |
| if (mt9t013_ctrl->sensormode == SENSOR_PREVIEW_MODE) { |
| mt9t013_ctrl->my_reg_gain = gain; |
| mt9t013_ctrl->my_reg_line_count = (uint16_t) line; |
| } |
| |
| if (gain > max_legal_gain) |
| gain = max_legal_gain; |
| |
| /* Verify no overflow */ |
| if (mt9t013_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { |
| line = (uint32_t) (line * mt9t013_ctrl->fps_divider / |
| 0x00000400); |
| |
| setting = RES_PREVIEW; |
| |
| } else { |
| line = (uint32_t) (line * mt9t013_ctrl->pict_fps_divider / |
| 0x00000400); |
| |
| setting = RES_CAPTURE; |
| } |
| |
| /*Set digital gain to 1 */ |
| gain |= 0x0200; |
| |
| if ((mt9t013_regs.reg_pat[setting].frame_length_lines - 1) < line) { |
| |
| line_length_ratio = |
| (uint32_t) (line * 0x00000400) / |
| (mt9t013_regs.reg_pat[setting].frame_length_lines - 1); |
| } else |
| line_length_ratio = 0x00000400; |
| |
| /* There used to be PARAMETER_HOLD register write before and |
| * after REG_GLOBAL_GAIN & REG_COARSE_INIT_TIME. This causes |
| * aec oscillation. Hence removed. */ |
| |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, REG_GLOBAL_GAIN, gain); |
| if (rc < 0) |
| return rc; |
| |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_COARSE_INT_TIME, |
| (uint16_t)((uint32_t) line * 0x00000400 / |
| line_length_ratio)); |
| if (rc < 0) |
| return rc; |
| |
| return rc; |
| } |
| |
| static int32_t mt9t013_set_pict_exp_gain(uint16_t gain, uint32_t line) |
| { |
| int32_t rc = 0; |
| |
| rc = mt9t013_write_exp_gain(gain, line); |
| if (rc < 0) |
| return rc; |
| |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| MT9T013_REG_RESET_REGISTER, |
| 0x10CC | 0x0002); |
| |
| mdelay(5); |
| |
| /* camera_timed_wait(snapshot_wait*exposure_ratio); */ |
| return rc; |
| } |
| |
| static int32_t mt9t013_setting(enum mt9t013_reg_update rupdate, |
| enum mt9t013_setting rt) |
| { |
| int32_t rc = 0; |
| |
| switch (rupdate) { |
| case UPDATE_PERIODIC: { |
| |
| if (rt == RES_PREVIEW || rt == RES_CAPTURE) { |
| #if 0 |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| MT9T013_REG_RESET_REGISTER, |
| MT9T013_RESET_REGISTER_PWOFF); |
| if (rc < 0) |
| return rc; |
| #endif |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_VT_PIX_CLK_DIV, |
| mt9t013_regs.reg_pat[rt].vt_pix_clk_div); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_VT_SYS_CLK_DIV, |
| mt9t013_regs.reg_pat[rt].vt_sys_clk_div); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_PRE_PLL_CLK_DIV, |
| mt9t013_regs.reg_pat[rt].pre_pll_clk_div); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_PLL_MULTIPLIER, |
| mt9t013_regs.reg_pat[rt].pll_multiplier); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_OP_PIX_CLK_DIV, |
| mt9t013_regs.reg_pat[rt].op_pix_clk_div); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_OP_SYS_CLK_DIV, |
| mt9t013_regs.reg_pat[rt].op_sys_clk_div); |
| if (rc < 0) |
| return rc; |
| |
| mdelay(5); |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_HOLD); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_ROW_SPEED, |
| mt9t013_regs.reg_pat[rt].row_speed); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_X_ADDR_START, |
| mt9t013_regs.reg_pat[rt].x_addr_start); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_X_ADDR_END, |
| mt9t013_regs.reg_pat[rt].x_addr_end); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_Y_ADDR_START, |
| mt9t013_regs.reg_pat[rt].y_addr_start); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_Y_ADDR_END, |
| mt9t013_regs.reg_pat[rt].y_addr_end); |
| if (rc < 0) |
| return rc; |
| |
| if (machine_is_sapphire()) { |
| if (rt == 0) |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_READ_MODE, |
| 0x046F); |
| else |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_READ_MODE, |
| 0x0027); |
| } else |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_READ_MODE, |
| mt9t013_regs.reg_pat[rt].read_mode); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_SCALE_M, |
| mt9t013_regs.reg_pat[rt].scale_m); |
| if (rc < 0) |
| return rc; |
| |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_X_OUTPUT_SIZE, |
| mt9t013_regs.reg_pat[rt].x_output_size); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_Y_OUTPUT_SIZE, |
| mt9t013_regs.reg_pat[rt].y_output_size); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_LINE_LENGTH_PCK, |
| mt9t013_regs.reg_pat[rt].line_length_pck); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_FRAME_LENGTH_LINES, |
| (mt9t013_regs.reg_pat[rt].frame_length_lines * |
| mt9t013_ctrl->fps_divider / 0x00000400)); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_COARSE_INT_TIME, |
| mt9t013_regs.reg_pat[rt].coarse_int_time); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_FINE_INT_TIME, |
| mt9t013_regs.reg_pat[rt].fine_int_time); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_UPDATE); |
| if (rc < 0) |
| return rc; |
| |
| rc = mt9t013_test(mt9t013_ctrl->set_test); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| MT9T013_REG_RESET_REGISTER, |
| MT9T013_RESET_REGISTER_PWON); |
| if (rc < 0) |
| return rc; |
| |
| mdelay(5); |
| |
| return rc; |
| } |
| } |
| break; |
| |
| /*CAMSENSOR_REG_UPDATE_PERIODIC */ |
| case REG_INIT: { |
| if (rt == RES_PREVIEW || rt == RES_CAPTURE) { |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| MT9T013_REG_RESET_REGISTER, |
| MT9T013_RESET_REGISTER_PWOFF); |
| if (rc < 0) |
| /* MODE_SELECT, stop streaming */ |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_VT_PIX_CLK_DIV, |
| mt9t013_regs.reg_pat[rt].vt_pix_clk_div); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_VT_SYS_CLK_DIV, |
| mt9t013_regs.reg_pat[rt].vt_sys_clk_div); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_PRE_PLL_CLK_DIV, |
| mt9t013_regs.reg_pat[rt].pre_pll_clk_div); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_PLL_MULTIPLIER, |
| mt9t013_regs.reg_pat[rt].pll_multiplier); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_OP_PIX_CLK_DIV, |
| mt9t013_regs.reg_pat[rt].op_pix_clk_div); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_OP_SYS_CLK_DIV, |
| mt9t013_regs.reg_pat[rt].op_sys_clk_div); |
| if (rc < 0) |
| return rc; |
| |
| mdelay(5); |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_HOLD); |
| if (rc < 0) |
| return rc; |
| |
| /* additional power saving mode ok around 38.2MHz */ |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| 0x3084, 0x2409); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| 0x3092, 0x0A49); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| 0x3094, 0x4949); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| 0x3096, 0x4949); |
| if (rc < 0) |
| return rc; |
| |
| /* Set preview or snapshot mode */ |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_ROW_SPEED, |
| mt9t013_regs.reg_pat[rt].row_speed); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_X_ADDR_START, |
| mt9t013_regs.reg_pat[rt].x_addr_start); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_X_ADDR_END, |
| mt9t013_regs.reg_pat[rt].x_addr_end); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_Y_ADDR_START, |
| mt9t013_regs.reg_pat[rt].y_addr_start); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_Y_ADDR_END, |
| mt9t013_regs.reg_pat[rt].y_addr_end); |
| if (rc < 0) |
| return rc; |
| |
| if (machine_is_sapphire()) { |
| if (rt == 0) |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_READ_MODE, |
| 0x046F); |
| else |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_READ_MODE, |
| 0x0027); |
| } else |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_READ_MODE, |
| mt9t013_regs.reg_pat[rt].read_mode); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_SCALE_M, |
| mt9t013_regs.reg_pat[rt].scale_m); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_X_OUTPUT_SIZE, |
| mt9t013_regs.reg_pat[rt].x_output_size); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_Y_OUTPUT_SIZE, |
| mt9t013_regs.reg_pat[rt].y_output_size); |
| if (rc < 0) |
| return 0; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_LINE_LENGTH_PCK, |
| mt9t013_regs.reg_pat[rt].line_length_pck); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_FRAME_LENGTH_LINES, |
| mt9t013_regs.reg_pat[rt].frame_length_lines); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_COARSE_INT_TIME, |
| mt9t013_regs.reg_pat[rt].coarse_int_time); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_FINE_INT_TIME, |
| mt9t013_regs.reg_pat[rt].fine_int_time); |
| if (rc < 0) |
| return rc; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_UPDATE); |
| if (rc < 0) |
| return rc; |
| |
| /* load lens shading */ |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_HOLD); |
| if (rc < 0) |
| return rc; |
| |
| /* most likely needs to be written only once. */ |
| rc = mt9t013_set_lc(); |
| if (rc < 0) |
| return -EBUSY; |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_UPDATE); |
| if (rc < 0) |
| return rc; |
| |
| rc = mt9t013_test(mt9t013_ctrl->set_test); |
| if (rc < 0) |
| return rc; |
| |
| mdelay(5); |
| |
| rc = |
| mt9t013_i2c_write_w(mt9t013_client->addr, |
| MT9T013_REG_RESET_REGISTER, |
| MT9T013_RESET_REGISTER_PWON); |
| if (rc < 0) |
| /* MODE_SELECT, stop streaming */ |
| return rc; |
| |
| CDBG("!!! mt9t013 !!! PowerOn is done!\n"); |
| mdelay(5); |
| return rc; |
| } |
| } /* case CAMSENSOR_REG_INIT: */ |
| break; |
| |
| /*CAMSENSOR_REG_INIT */ |
| default: |
| rc = -EINVAL; |
| break; |
| } /* switch (rupdate) */ |
| |
| return rc; |
| } |
| |
| static int32_t mt9t013_video_config(int mode, int res) |
| { |
| int32_t rc; |
| |
| switch (res) { |
| case QTR_SIZE: |
| rc = mt9t013_setting(UPDATE_PERIODIC, RES_PREVIEW); |
| if (rc < 0) |
| return rc; |
| CDBG("sensor configuration done!\n"); |
| break; |
| |
| case FULL_SIZE: |
| rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); |
| if (rc < 0) |
| return rc; |
| break; |
| |
| default: |
| return -EINVAL; |
| } /* switch */ |
| |
| mt9t013_ctrl->prev_res = res; |
| mt9t013_ctrl->curr_res = res; |
| mt9t013_ctrl->sensormode = mode; |
| |
| return mt9t013_write_exp_gain(mt9t013_ctrl->my_reg_gain, |
| mt9t013_ctrl->my_reg_line_count); |
| } |
| |
| static int32_t mt9t013_snapshot_config(int mode) |
| { |
| int32_t rc = 0; |
| |
| rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); |
| if (rc < 0) |
| return rc; |
| |
| mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res; |
| mt9t013_ctrl->sensormode = mode; |
| return rc; |
| } |
| |
| static int32_t mt9t013_raw_snapshot_config(int mode) |
| { |
| int32_t rc = 0; |
| |
| rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); |
| if (rc < 0) |
| return rc; |
| |
| mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res; |
| mt9t013_ctrl->sensormode = mode; |
| return rc; |
| } |
| |
| static int32_t mt9t013_power_down(void) |
| { |
| int32_t rc = 0; |
| |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| MT9T013_REG_RESET_REGISTER, |
| MT9T013_RESET_REGISTER_PWOFF); |
| if (rc >= 0) |
| mdelay(5); |
| return rc; |
| } |
| |
| static int32_t mt9t013_move_focus(int direction, int32_t num_steps) |
| { |
| int16_t step_direction; |
| int16_t actual_step; |
| int16_t next_position; |
| int16_t break_steps[4]; |
| uint8_t code_val_msb, code_val_lsb; |
| int16_t i; |
| |
| if (num_steps > MT9T013_TOTAL_STEPS_NEAR_TO_FAR) |
| num_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR; |
| else if (num_steps == 0) |
| return -EINVAL; |
| |
| if (direction == MOVE_NEAR) |
| step_direction = 4; |
| else if (direction == MOVE_FAR) |
| step_direction = -4; |
| else |
| return -EINVAL; |
| |
| if (mt9t013_ctrl->curr_lens_pos < mt9t013_ctrl->init_curr_lens_pos) |
| mt9t013_ctrl->curr_lens_pos = mt9t013_ctrl->init_curr_lens_pos; |
| |
| actual_step = |
| (int16_t) (step_direction * |
| (int16_t) num_steps); |
| |
| for (i = 0; i < 4; i++) |
| break_steps[i] = |
| actual_step / 4 * (i + 1) - actual_step / 4 * i; |
| |
| for (i = 0; i < 4; i++) { |
| next_position = |
| (int16_t) |
| (mt9t013_ctrl->curr_lens_pos + break_steps[i]); |
| |
| if (next_position > 255) |
| next_position = 255; |
| else if (next_position < 0) |
| next_position = 0; |
| |
| code_val_msb = |
| ((next_position >> 4) << 2) | |
| ((next_position << 4) >> 6); |
| |
| code_val_lsb = |
| ((next_position & 0x03) << 6); |
| |
| /* Writing the digital code for current to the actuator */ |
| if (mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1, |
| code_val_msb, code_val_lsb) < 0) |
| return -EBUSY; |
| |
| /* Storing the current lens Position */ |
| mt9t013_ctrl->curr_lens_pos = next_position; |
| |
| if (i < 3) |
| mdelay(1); |
| } /* for */ |
| |
| return 0; |
| } |
| |
| static int mt9t013_sensor_init_done(const struct msm_camera_sensor_info *data) |
| { |
| gpio_direction_output(data->sensor_reset, 0); |
| gpio_free(data->sensor_reset); |
| return 0; |
| } |
| |
| static int mt9t013_probe_init_sensor(const struct msm_camera_sensor_info *data) |
| { |
| int rc; |
| uint16_t chipid; |
| |
| rc = gpio_request(data->sensor_reset, "mt9t013"); |
| if (!rc) |
| gpio_direction_output(data->sensor_reset, 1); |
| else |
| goto init_probe_done; |
| |
| mdelay(20); |
| |
| /* RESET the sensor image part via I2C command */ |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| MT9T013_REG_RESET_REGISTER, 0x1009); |
| if (rc < 0) |
| goto init_probe_fail; |
| |
| /* 3. Read sensor Model ID: */ |
| rc = mt9t013_i2c_read_w(mt9t013_client->addr, |
| MT9T013_REG_MODEL_ID, &chipid); |
| |
| if (rc < 0) |
| goto init_probe_fail; |
| |
| CDBG("mt9t013 model_id = 0x%x\n", chipid); |
| |
| /* 4. Compare sensor ID to MT9T012VC ID: */ |
| if (chipid != MT9T013_MODEL_ID) { |
| rc = -ENODEV; |
| goto init_probe_fail; |
| } |
| |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| 0x3064, 0x0805); |
| if (rc < 0) |
| goto init_probe_fail; |
| |
| mdelay(MT9T013_RESET_DELAY_MSECS); |
| |
| goto init_probe_done; |
| |
| /* sensor: output enable */ |
| #if 0 |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| MT9T013_REG_RESET_REGISTER, |
| MT9T013_RESET_REGISTER_PWON); |
| |
| /* if this fails, the sensor is not the MT9T013 */ |
| rc = mt9t013_set_default_focus(0); |
| #endif |
| |
| init_probe_fail: |
| gpio_direction_output(data->sensor_reset, 0); |
| gpio_free(data->sensor_reset); |
| init_probe_done: |
| return rc; |
| } |
| |
| static int32_t mt9t013_poweron_af(void) |
| { |
| int32_t rc = 0; |
| |
| /* enable AF actuator */ |
| CDBG("enable AF actuator, gpio = %d\n", |
| mt9t013_ctrl->sensordata->vcm_pwd); |
| rc = gpio_request(mt9t013_ctrl->sensordata->vcm_pwd, "mt9t013"); |
| if (!rc) { |
| gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 0); |
| mdelay(20); |
| rc = mt9t013_set_default_focus(0); |
| } else |
| pr_err("%s, gpio_request failed (%d)!\n", __func__, rc); |
| return rc; |
| } |
| |
| static void mt9t013_poweroff_af(void) |
| { |
| gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 1); |
| gpio_free(mt9t013_ctrl->sensordata->vcm_pwd); |
| } |
| |
| int mt9t013_sensor_open_init(const struct msm_camera_sensor_info *data) |
| { |
| int32_t rc; |
| |
| mt9t013_ctrl = kzalloc(sizeof(struct mt9t013_ctrl), GFP_KERNEL); |
| if (!mt9t013_ctrl) { |
| pr_err("mt9t013_init failed!\n"); |
| rc = -ENOMEM; |
| goto init_done; |
| } |
| |
| mt9t013_ctrl->fps_divider = 1 * 0x00000400; |
| mt9t013_ctrl->pict_fps_divider = 1 * 0x00000400; |
| mt9t013_ctrl->set_test = TEST_OFF; |
| mt9t013_ctrl->prev_res = QTR_SIZE; |
| mt9t013_ctrl->pict_res = FULL_SIZE; |
| |
| if (data) |
| mt9t013_ctrl->sensordata = data; |
| |
| /* enable mclk first */ |
| msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE); |
| mdelay(20); |
| |
| msm_camio_camif_pad_reg_reset(); |
| mdelay(20); |
| |
| rc = mt9t013_probe_init_sensor(data); |
| if (rc < 0) |
| goto init_fail; |
| |
| if (mt9t013_ctrl->prev_res == QTR_SIZE) |
| rc = mt9t013_setting(REG_INIT, RES_PREVIEW); |
| else |
| rc = mt9t013_setting(REG_INIT, RES_CAPTURE); |
| |
| if (rc >= 0) |
| rc = mt9t013_poweron_af(); |
| |
| if (rc < 0) |
| goto init_fail; |
| else |
| goto init_done; |
| |
| init_fail: |
| kfree(mt9t013_ctrl); |
| init_done: |
| return rc; |
| } |
| |
| static int mt9t013_init_client(struct i2c_client *client) |
| { |
| /* Initialize the MSM_CAMI2C Chip */ |
| init_waitqueue_head(&mt9t013_wait_queue); |
| return 0; |
| } |
| |
| |
| static int32_t mt9t013_set_sensor_mode(int mode, int res) |
| { |
| int32_t rc = 0; |
| rc = mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_HOLD); |
| if (rc < 0) |
| return rc; |
| |
| switch (mode) { |
| case SENSOR_PREVIEW_MODE: |
| rc = mt9t013_video_config(mode, res); |
| break; |
| |
| case SENSOR_SNAPSHOT_MODE: |
| rc = mt9t013_snapshot_config(mode); |
| break; |
| |
| case SENSOR_RAW_SNAPSHOT_MODE: |
| rc = mt9t013_raw_snapshot_config(mode); |
| break; |
| |
| default: |
| return -EINVAL; |
| } |
| |
| /* FIXME: what should we do if rc < 0? */ |
| if (rc >= 0) |
| return mt9t013_i2c_write_w(mt9t013_client->addr, |
| REG_GROUPED_PARAMETER_HOLD, |
| GROUPED_PARAMETER_UPDATE); |
| return rc; |
| } |
| |
| int mt9t013_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; |
| |
| down(&mt9t013_sem); |
| |
| CDBG("mt9t013_sensor_config: cfgtype = %d\n", cdata.cfgtype); |
| switch (cdata.cfgtype) { |
| case CFG_GET_PICT_FPS: |
| mt9t013_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 = mt9t013_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 = mt9t013_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 = mt9t013_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 = |
| mt9t013_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 = |
| mt9t013_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 = mt9t013_set_fps(&(cdata.cfg.fps)); |
| break; |
| |
| case CFG_SET_EXP_GAIN: |
| rc = mt9t013_write_exp_gain(cdata.cfg.exp_gain.gain, |
| cdata.cfg.exp_gain.line); |
| break; |
| |
| case CFG_SET_PICT_EXP_GAIN: |
| rc = mt9t013_set_pict_exp_gain(cdata.cfg.exp_gain.gain, |
| cdata.cfg.exp_gain.line); |
| break; |
| |
| case CFG_SET_MODE: |
| rc = mt9t013_set_sensor_mode(cdata.mode, cdata.rs); |
| break; |
| |
| case CFG_PWR_DOWN: |
| rc = mt9t013_power_down(); |
| break; |
| |
| case CFG_MOVE_FOCUS: |
| rc = mt9t013_move_focus(cdata.cfg.focus.dir, |
| cdata.cfg.focus.steps); |
| break; |
| |
| case CFG_SET_DEFAULT_FOCUS: |
| rc = mt9t013_set_default_focus(cdata.cfg.focus.steps); |
| break; |
| |
| case CFG_GET_AF_MAX_STEPS: |
| cdata.max_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR; |
| if (copy_to_user((void *)argp, |
| &cdata, |
| sizeof(struct sensor_cfg_data))) |
| rc = -EFAULT; |
| break; |
| |
| case CFG_SET_EFFECT: |
| default: |
| rc = -EINVAL; |
| break; |
| } |
| |
| up(&mt9t013_sem); |
| return rc; |
| } |
| |
| int mt9t013_sensor_release(void) |
| { |
| int rc = -EBADF; |
| |
| down(&mt9t013_sem); |
| |
| mt9t013_poweroff_af(); |
| mt9t013_power_down(); |
| |
| gpio_direction_output(mt9t013_ctrl->sensordata->sensor_reset, |
| 0); |
| gpio_free(mt9t013_ctrl->sensordata->sensor_reset); |
| |
| kfree(mt9t013_ctrl); |
| |
| up(&mt9t013_sem); |
| CDBG("mt9t013_release completed!\n"); |
| return rc; |
| } |
| |
| static int mt9t013_i2c_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int rc = 0; |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| rc = -ENOTSUPP; |
| goto probe_failure; |
| } |
| |
| mt9t013_sensorw = |
| kzalloc(sizeof(struct mt9t013_work), GFP_KERNEL); |
| |
| if (!mt9t013_sensorw) { |
| rc = -ENOMEM; |
| goto probe_failure; |
| } |
| |
| i2c_set_clientdata(client, mt9t013_sensorw); |
| mt9t013_init_client(client); |
| mt9t013_client = client; |
| mt9t013_client->addr = mt9t013_client->addr >> 1; |
| mdelay(50); |
| |
| CDBG("i2c probe ok\n"); |
| return 0; |
| |
| probe_failure: |
| kfree(mt9t013_sensorw); |
| mt9t013_sensorw = NULL; |
| pr_err("i2c probe failure %d\n", rc); |
| return rc; |
| } |
| |
| static const struct i2c_device_id mt9t013_i2c_id[] = { |
| { "mt9t013", 0}, |
| { } |
| }; |
| |
| static struct i2c_driver mt9t013_i2c_driver = { |
| .id_table = mt9t013_i2c_id, |
| .probe = mt9t013_i2c_probe, |
| .remove = __exit_p(mt9t013_i2c_remove), |
| .driver = { |
| .name = "mt9t013", |
| }, |
| }; |
| |
| static int mt9t013_sensor_probe( |
| const struct msm_camera_sensor_info *info, |
| struct msm_sensor_ctrl *s) |
| { |
| /* We expect this driver to match with the i2c device registered |
| * in the board file immediately. */ |
| int rc = i2c_add_driver(&mt9t013_i2c_driver); |
| if (rc < 0 || mt9t013_client == NULL) { |
| rc = -ENOTSUPP; |
| goto probe_done; |
| } |
| |
| /* enable mclk first */ |
| msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE); |
| mdelay(20); |
| |
| rc = mt9t013_probe_init_sensor(info); |
| if (rc < 0) { |
| i2c_del_driver(&mt9t013_i2c_driver); |
| goto probe_done; |
| } |
| |
| s->s_init = mt9t013_sensor_open_init; |
| s->s_release = mt9t013_sensor_release; |
| s->s_config = mt9t013_sensor_config; |
| mt9t013_sensor_init_done(info); |
| |
| probe_done: |
| return rc; |
| } |
| |
| static int __mt9t013_probe(struct platform_device *pdev) |
| { |
| return msm_camera_drv_start(pdev, mt9t013_sensor_probe); |
| } |
| |
| static struct platform_driver msm_camera_driver = { |
| .probe = __mt9t013_probe, |
| .driver = { |
| .name = "msm_camera_mt9t013", |
| .owner = THIS_MODULE, |
| }, |
| }; |
| |
| static int __init mt9t013_init(void) |
| { |
| return platform_driver_register(&msm_camera_driver); |
| } |
| |
| module_init(mt9t013_init); |