/* 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 #include #include "imx074.h" /*SENSOR REGISTER DEFINES*/ #define IMX074_EEPROM_SLAVE_ADDR 0x52 #define REG_GROUPED_PARAMETER_HOLD 0x0104 #define GROUPED_PARAMETER_HOLD_OFF 0x00 #define GROUPED_PARAMETER_HOLD 0x01 #define REG_MODE_SELECT 0x100 #define MODE_SELECT_STANDBY_MODE 0x00 #define MODE_SELECT_STREAM 0x01 /* Integration Time */ #define REG_COARSE_INTEGRATION_TIME_HI 0x0202 #define REG_COARSE_INTEGRATION_TIME_LO 0x0203 /* Gain */ #define REG_ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204 #define REG_ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205 /* PLL registers */ #define REG_PLL_MULTIPLIER 0x0307 #define REG_PRE_PLL_CLK_DIV 0x0305 #define REG_PLSTATIM 0x302b #define REG_3024 0x3024 #define REG_IMAGE_ORIENTATION 0x0101 #define REG_VNDMY_ABLMGSHLMT 0x300a #define REG_Y_OPBADDR_START_DI 0x3014 #define REG_3015 0x3015 #define REG_301C 0x301C #define REG_302C 0x302C #define REG_3031 0x3031 #define REG_3041 0x3041 #define REG_3051 0x3051 #define REG_3053 0x3053 #define REG_3057 0x3057 #define REG_305C 0x305C #define REG_305D 0x305D #define REG_3060 0x3060 #define REG_3065 0x3065 #define REG_30AA 0x30AA #define REG_30AB 0x30AB #define REG_30B0 0x30B0 #define REG_30B2 0x30B2 #define REG_30D3 0x30D3 #define REG_3106 0x3106 #define REG_310C 0x310C #define REG_3304 0x3304 #define REG_3305 0x3305 #define REG_3306 0x3306 #define REG_3307 0x3307 #define REG_3308 0x3308 #define REG_3309 0x3309 #define REG_330A 0x330A #define REG_330B 0x330B #define REG_330C 0x330C #define REG_330D 0x330D #define REG_330F 0x330F #define REG_3381 0x3381 /* mode setting */ #define REG_FRAME_LENGTH_LINES_HI 0x0340 #define REG_FRAME_LENGTH_LINES_LO 0x0341 #define REG_YADDR_START 0x0347 #define REG_YAAAR_END 0x034b #define REG_X_OUTPUT_SIZE_MSB 0x034c #define REG_X_OUTPUT_SIZE_LSB 0x034d #define REG_Y_OUTPUT_SIZE_MSB 0x034e #define REG_Y_OUTPUT_SIZE_LSB 0x034f #define REG_X_EVEN_INC 0x0381 #define REG_X_ODD_INC 0x0383 #define REG_Y_EVEN_INC 0x0385 #define REG_Y_ODD_INC 0x0387 #define REG_HMODEADD 0x3001 #define REG_VMODEADD 0x3016 #define REG_VAPPLINE_START 0x3069 #define REG_VAPPLINE_END 0x306b #define REG_SHUTTER 0x3086 #define REG_HADDAVE 0x30e8 #define REG_LANESEL 0x3301 /* Test Pattern */ #define REG_TEST_PATTERN_MODE 0x0601 #define REG_LINE_LENGTH_PCK_HI 0x0342 #define REG_LINE_LENGTH_PCK_LO 0x0343 /*..... TYPE DECLARATIONS.....*/ #define IMX074_OFFSET 3 #define IMX074_DEFAULT_MASTER_CLK_RATE 24000000 /* Full Size */ #define IMX074_FULL_SIZE_WIDTH 4208 #define IMX074_FULL_SIZE_HEIGHT 3120 #define IMX074_FULL_SIZE_DUMMY_PIXELS 0 #define IMX074_FULL_SIZE_DUMMY_LINES 0 /* Quarter Size */ #define IMX074_QTR_SIZE_WIDTH 2104 #define IMX074_QTR_SIZE_HEIGHT 1560 #define IMX074_QTR_SIZE_DUMMY_PIXELS 0 #define IMX074_QTR_SIZE_DUMMY_LINES 0 /* Blanking as measured on the scope */ /* Full Size */ #define IMX074_HRZ_FULL_BLK_PIXELS 264 #define IMX074_VER_FULL_BLK_LINES 96 /* Quarter Size */ #define IMX074_HRZ_QTR_BLK_PIXELS 2368 #define IMX074_VER_QTR_BLK_LINES 21 #define Q8 0x100 #define Q10 0x400 #define IMX074_AF_I2C_SLAVE_ID 0x72 #define IMX074_STEPS_NEAR_TO_CLOSEST_INF 52 #define IMX074_TOTAL_STEPS_NEAR_TO_FAR 52 static uint32_t imx074_l_region_code_per_step = 2; struct imx074_work_t { struct work_struct work; }; static struct imx074_work_t *imx074_sensorw; static struct i2c_client *imx074_client; struct imx074_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; int16_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 imx074_resolution_t prev_res; enum imx074_resolution_t pict_res; enum imx074_resolution_t curr_res; enum imx074_test_mode_t set_test; unsigned short imgaddr; }; static uint8_t imx074_delay_msecs_stdby = 5; static uint16_t imx074_delay_msecs_stream = 5; static int32_t config_csi; static struct imx074_ctrl_t *imx074_ctrl; static DECLARE_WAIT_QUEUE_HEAD(imx074_wait_queue); DEFINE_MUTEX(imx074_mut); /*=============================================================*/ static int imx074_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 = 2, .buf = rxdata, }, }; if (i2c_transfer(imx074_client->adapter, msgs, 2) < 0) { CDBG("imx074_i2c_rxdata failed!\n"); return -EIO; } return 0; } static int32_t imx074_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(imx074_client->adapter, msg, 1) < 0) { CDBG("imx074_i2c_txdata faild 0x%x\n", imx074_client->addr); return -EIO; } return 0; } static int32_t imx074_i2c_read(unsigned short raddr, unsigned short *rdata, int rlen) { int32_t rc = 0; unsigned char buf[2]; if (!rdata) return -EIO; memset(buf, 0, sizeof(buf)); buf[0] = (raddr & 0xFF00) >> 8; buf[1] = (raddr & 0x00FF); rc = imx074_i2c_rxdata(imx074_client->addr, buf, rlen); if (rc < 0) { CDBG("imx074_i2c_read 0x%x failed!\n", raddr); return rc; } *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]); return rc; } static int imx074_af_i2c_rxdata_b(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(imx074_client->adapter, msgs, 2) < 0) { CDBG("imx074_i2c_rxdata_b failed!\n"); return -EIO; } return 0; } static int32_t imx074_i2c_read_w_eeprom(unsigned short raddr, unsigned short *rdata) { int32_t rc; unsigned char buf; if (!rdata) return -EIO; /* Read 2 bytes in sequence */ buf = (raddr & 0x00FF); rc = imx074_af_i2c_rxdata_b(IMX074_EEPROM_SLAVE_ADDR, &buf, 1); if (rc < 0) { CDBG("imx074_i2c_read_eeprom 0x%x failed!\n", raddr); return rc; } *rdata = buf<<8; /* Read Second byte of data */ buf = (raddr & 0x00FF) + 1; rc = imx074_af_i2c_rxdata_b(IMX074_EEPROM_SLAVE_ADDR, &buf, 1); if (rc < 0) { CDBG("imx074_i2c_read_eeprom 0x%x failed!\n", raddr); return rc; } *rdata |= buf; return rc; } static int32_t imx074_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 = imx074_i2c_txdata(imx074_client->addr, buf, 3); if (rc < 0) { CDBG("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n", waddr, bdata); } return rc; } static int16_t imx074_i2c_write_b_af(unsigned short saddr, unsigned short baddr, unsigned short bdata) { int32_t rc; unsigned char buf[2]; memset(buf, 0, sizeof(buf)); buf[0] = baddr; buf[1] = bdata; rc = imx074_i2c_txdata(saddr, buf, 2); if (rc < 0) CDBG("AFi2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!", saddr, baddr, bdata); return rc; } static int32_t imx074_i2c_write_w_table(struct imx074_i2c_reg_conf const *reg_conf_tbl, int num) { int i; int32_t rc = -EIO; for (i = 0; i < num; i++) { rc = imx074_i2c_write_b_sensor(reg_conf_tbl->waddr, reg_conf_tbl->wdata); if (rc < 0) break; reg_conf_tbl++; } return rc; } static int16_t imx074_af_init(void) { int32_t rc; /* Initialize waveform */ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x01, 0xA9); rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x02, 0xD2); rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x03, 0x0C); rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x04, 0x14); rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x05, 0xB6); rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x06, 0x4F); return rc; } static void imx074_get_pict_fps(uint16_t fps, uint16_t *pfps) { /* input fps is preview fps in Q8 format */ uint16_t preview_frame_length_lines, snapshot_frame_length_lines; uint32_t divider, d1; uint32_t pclk_mult;/*Q10 */ /* Total frame_length_lines and line_length_pck for preview */ preview_frame_length_lines = IMX074_QTR_SIZE_HEIGHT + IMX074_VER_QTR_BLK_LINES; /* Total frame_length_lines and line_length_pck for snapshot */ snapshot_frame_length_lines = IMX074_FULL_SIZE_HEIGHT + IMX074_VER_FULL_BLK_LINES; d1 = preview_frame_length_lines * 0x00010000 / snapshot_frame_length_lines; pclk_mult = (uint32_t) ((imx074_regs.reg_pat[RES_CAPTURE].pll_multiplier * 0x00010000) / (imx074_regs.reg_pat[RES_PREVIEW].pll_multiplier)); divider = d1 * pclk_mult / 0x00010000; *pfps = (uint16_t) (fps * divider / 0x00010000); } static uint16_t imx074_get_prev_lines_pf(void) { if (imx074_ctrl->prev_res == QTR_SIZE) return IMX074_QTR_SIZE_HEIGHT + IMX074_VER_QTR_BLK_LINES; else return IMX074_FULL_SIZE_HEIGHT + IMX074_VER_FULL_BLK_LINES; } static uint16_t imx074_get_prev_pixels_pl(void) { if (imx074_ctrl->prev_res == QTR_SIZE) return IMX074_QTR_SIZE_WIDTH + IMX074_HRZ_QTR_BLK_PIXELS; else return IMX074_FULL_SIZE_WIDTH + IMX074_HRZ_FULL_BLK_PIXELS; } static uint16_t imx074_get_pict_lines_pf(void) { if (imx074_ctrl->pict_res == QTR_SIZE) return IMX074_QTR_SIZE_HEIGHT + IMX074_VER_QTR_BLK_LINES; else return IMX074_FULL_SIZE_HEIGHT + IMX074_VER_FULL_BLK_LINES; } static uint16_t imx074_get_pict_pixels_pl(void) { if (imx074_ctrl->pict_res == QTR_SIZE) return IMX074_QTR_SIZE_WIDTH + IMX074_HRZ_QTR_BLK_PIXELS; else return IMX074_FULL_SIZE_WIDTH + IMX074_HRZ_FULL_BLK_PIXELS; } static uint32_t imx074_get_pict_max_exp_lc(void) { if (imx074_ctrl->pict_res == QTR_SIZE) return (IMX074_QTR_SIZE_HEIGHT + IMX074_VER_QTR_BLK_LINES)*24; else return (IMX074_FULL_SIZE_HEIGHT + IMX074_VER_FULL_BLK_LINES)*24; } static int32_t imx074_set_fps(struct fps_cfg *fps) { uint16_t total_lines_per_frame; int32_t rc = 0; imx074_ctrl->fps_divider = fps->fps_div; imx074_ctrl->pict_fps_divider = fps->pict_fps_div; if (imx074_ctrl->curr_res == QTR_SIZE) { total_lines_per_frame = (uint16_t)(((IMX074_QTR_SIZE_HEIGHT + IMX074_VER_QTR_BLK_LINES) * imx074_ctrl->fps_divider) / 0x400); } else { total_lines_per_frame = (uint16_t)(((IMX074_FULL_SIZE_HEIGHT + IMX074_VER_FULL_BLK_LINES) * imx074_ctrl->pict_fps_divider) / 0x400); } if (imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI, ((total_lines_per_frame & 0xFF00) >> 8)) < 0) return rc; if (imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO, (total_lines_per_frame & 0x00FF)) < 0) return rc; return rc; } static int32_t imx074_write_exp_gain(uint16_t gain, uint32_t line) { static uint16_t max_legal_gain = 0x00E0; uint8_t gain_msb, gain_lsb; uint8_t intg_time_msb, intg_time_lsb; uint8_t frame_length_line_msb, frame_length_line_lsb; uint16_t frame_length_lines; int32_t rc = -1; CDBG("imx074_write_exp_gain : gain = %d line = %d", gain, line); if (imx074_ctrl->curr_res == QTR_SIZE) { frame_length_lines = IMX074_QTR_SIZE_HEIGHT + IMX074_VER_QTR_BLK_LINES; frame_length_lines = frame_length_lines * imx074_ctrl->fps_divider / 0x400; } else { frame_length_lines = IMX074_FULL_SIZE_HEIGHT + IMX074_VER_FULL_BLK_LINES; frame_length_lines = frame_length_lines * imx074_ctrl->pict_fps_divider / 0x400; } if (line > (frame_length_lines - IMX074_OFFSET)) frame_length_lines = line + IMX074_OFFSET; CDBG("imx074 setting line = %d\n", line); CDBG("imx074 setting frame_length_lines = %d\n", frame_length_lines); if (gain > max_legal_gain) /* range: 0 to 224 */ gain = max_legal_gain; /* update gain registers */ gain_msb = (uint8_t) ((gain & 0xFF00) >> 8); gain_lsb = (uint8_t) (gain & 0x00FF); frame_length_line_msb = (uint8_t) ((frame_length_lines & 0xFF00) >> 8); frame_length_line_lsb = (uint8_t) (frame_length_lines & 0x00FF); /* update line count registers */ intg_time_msb = (uint8_t) ((line & 0xFF00) >> 8); intg_time_lsb = (uint8_t) (line & 0x00FF); rc = imx074_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); if (rc < 0) return rc; CDBG("imx074 setting REG_ANALOGUE_GAIN_CODE_GLOBAL_HI = 0x%X\n", gain_msb); rc = imx074_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_HI, gain_msb); if (rc < 0) return rc; CDBG("imx074 setting REG_ANALOGUE_GAIN_CODE_GLOBAL_LO = 0x%X\n", gain_lsb); rc = imx074_i2c_write_b_sensor(REG_ANALOGUE_GAIN_CODE_GLOBAL_LO, gain_lsb); if (rc < 0) return rc; CDBG("imx074 setting REG_FRAME_LENGTH_LINES_HI = 0x%X\n", frame_length_line_msb); rc = imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_HI, frame_length_line_msb); if (rc < 0) return rc; CDBG("imx074 setting REG_FRAME_LENGTH_LINES_LO = 0x%X\n", frame_length_line_lsb); rc = imx074_i2c_write_b_sensor(REG_FRAME_LENGTH_LINES_LO, frame_length_line_lsb); if (rc < 0) return rc; CDBG("imx074 setting REG_COARSE_INTEGRATION_TIME_HI = 0x%X\n", intg_time_msb); rc = imx074_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_HI, intg_time_msb); if (rc < 0) return rc; CDBG("imx074 setting REG_COARSE_INTEGRATION_TIME_LO = 0x%X\n", intg_time_lsb); rc = imx074_i2c_write_b_sensor(REG_COARSE_INTEGRATION_TIME_LO, intg_time_lsb); if (rc < 0) return rc; rc = imx074_i2c_write_b_sensor(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD_OFF); if (rc < 0) return rc; return rc; } static int32_t imx074_set_pict_exp_gain(uint16_t gain, uint32_t line) { int32_t rc = 0; rc = imx074_write_exp_gain(gain, line); return rc; } static int32_t imx074_move_focus(int direction, int32_t num_steps) { int32_t step_direction, dest_step_position, bit_mask; int32_t rc = 0; if (num_steps == 0) return rc; if (direction == MOVE_NEAR) { step_direction = 1; bit_mask = 0x80; } else if (direction == MOVE_FAR) { step_direction = -1; bit_mask = 0x00; } else { CDBG("imx074_move_focus: Illegal focus direction"); return -EINVAL; } dest_step_position = imx074_ctrl->curr_step_pos + (step_direction * num_steps); if (dest_step_position < 0) dest_step_position = 0; else if (dest_step_position > IMX074_TOTAL_STEPS_NEAR_TO_FAR) dest_step_position = IMX074_TOTAL_STEPS_NEAR_TO_FAR; rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00, ((num_steps * imx074_l_region_code_per_step) | bit_mask)); CDBG("%s: Index: %d\n", __func__, dest_step_position); imx074_ctrl->curr_step_pos = dest_step_position; return rc; } static int32_t imx074_set_default_focus(uint8_t af_step) { int32_t rc; /* Initialize to infinity */ rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00, 0x7F); rc = imx074_i2c_write_b_af(IMX074_AF_I2C_SLAVE_ID, 0x00, 0x7F); imx074_ctrl->curr_step_pos = 0; return rc; } static int32_t imx074_test(enum imx074_test_mode_t mo) { int32_t rc = 0; if (mo == TEST_OFF) return rc; else { /* Set mo to 2 inorder to enable test pattern*/ if (imx074_i2c_write_b_sensor(REG_TEST_PATTERN_MODE, (uint8_t) mo) < 0) { return rc; } } return rc; } static int32_t imx074_sensor_setting(int update_type, int rt) { int32_t rc = 0; struct msm_camera_csi_params imx074_csi_params; switch (update_type) { case REG_INIT: if (rt == RES_PREVIEW || rt == RES_CAPTURE) { struct imx074_i2c_reg_conf init_tbl[] = { {REG_PRE_PLL_CLK_DIV, imx074_regs.reg_pat_init[0]. pre_pll_clk_div}, {REG_PLSTATIM, imx074_regs.reg_pat_init[0]. plstatim}, {REG_3024, imx074_regs.reg_pat_init[0]. reg_3024}, {REG_IMAGE_ORIENTATION, imx074_regs.reg_pat_init[0]. image_orientation}, {REG_VNDMY_ABLMGSHLMT, imx074_regs.reg_pat_init[0]. vndmy_ablmgshlmt}, {REG_Y_OPBADDR_START_DI, imx074_regs.reg_pat_init[0]. y_opbaddr_start_di}, {REG_3015, imx074_regs.reg_pat_init[0]. reg_0x3015}, {REG_301C, imx074_regs.reg_pat_init[0]. reg_0x301c}, {REG_302C, imx074_regs.reg_pat_init[0]. reg_0x302c}, {REG_3031, imx074_regs.reg_pat_init[0].reg_0x3031}, {REG_3041, imx074_regs.reg_pat_init[0].reg_0x3041}, {REG_3051, imx074_regs.reg_pat_init[0].reg_0x3051}, {REG_3053, imx074_regs.reg_pat_init[0].reg_0x3053}, {REG_3057, imx074_regs.reg_pat_init[0].reg_0x3057}, {REG_305C, imx074_regs.reg_pat_init[0].reg_0x305c}, {REG_305D, imx074_regs.reg_pat_init[0].reg_0x305d}, {REG_3060, imx074_regs.reg_pat_init[0].reg_0x3060}, {REG_3065, imx074_regs.reg_pat_init[0].reg_0x3065}, {REG_30AA, imx074_regs.reg_pat_init[0].reg_0x30aa}, {REG_30AB, imx074_regs.reg_pat_init[0].reg_0x30ab}, {REG_30B0, imx074_regs.reg_pat_init[0].reg_0x30b0}, {REG_30B2, imx074_regs.reg_pat_init[0].reg_0x30b2}, {REG_30D3, imx074_regs.reg_pat_init[0].reg_0x30d3}, {REG_3106, imx074_regs.reg_pat_init[0].reg_0x3106}, {REG_310C, imx074_regs.reg_pat_init[0].reg_0x310c}, {REG_3304, imx074_regs.reg_pat_init[0].reg_0x3304}, {REG_3305, imx074_regs.reg_pat_init[0].reg_0x3305}, {REG_3306, imx074_regs.reg_pat_init[0].reg_0x3306}, {REG_3307, imx074_regs.reg_pat_init[0].reg_0x3307}, {REG_3308, imx074_regs.reg_pat_init[0].reg_0x3308}, {REG_3309, imx074_regs.reg_pat_init[0].reg_0x3309}, {REG_330A, imx074_regs.reg_pat_init[0].reg_0x330a}, {REG_330B, imx074_regs.reg_pat_init[0].reg_0x330b}, {REG_330C, imx074_regs.reg_pat_init[0].reg_0x330c}, {REG_330D, imx074_regs.reg_pat_init[0].reg_0x330d}, {REG_330F, imx074_regs.reg_pat_init[0].reg_0x330f}, {REG_3381, imx074_regs.reg_pat_init[0].reg_0x3381}, }; struct imx074_i2c_reg_conf init_mode_tbl[] = { {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD}, {REG_PLL_MULTIPLIER, imx074_regs.reg_pat[rt]. pll_multiplier}, {REG_FRAME_LENGTH_LINES_HI, imx074_regs.reg_pat[rt]. frame_length_lines_hi}, {REG_FRAME_LENGTH_LINES_LO, imx074_regs.reg_pat[rt]. frame_length_lines_lo}, {REG_YADDR_START , imx074_regs.reg_pat[rt]. y_addr_start}, {REG_YAAAR_END, imx074_regs.reg_pat[rt]. y_add_end}, {REG_X_OUTPUT_SIZE_MSB, imx074_regs.reg_pat[rt]. x_output_size_msb}, {REG_X_OUTPUT_SIZE_LSB, imx074_regs.reg_pat[rt]. x_output_size_lsb}, {REG_Y_OUTPUT_SIZE_MSB, imx074_regs.reg_pat[rt]. y_output_size_msb}, {REG_Y_OUTPUT_SIZE_LSB , imx074_regs.reg_pat[rt]. y_output_size_lsb}, {REG_X_EVEN_INC, imx074_regs.reg_pat[rt]. x_even_inc}, {REG_X_ODD_INC, imx074_regs.reg_pat[rt]. x_odd_inc}, {REG_Y_EVEN_INC, imx074_regs.reg_pat[rt]. y_even_inc}, {REG_Y_ODD_INC, imx074_regs.reg_pat[rt]. y_odd_inc}, {REG_HMODEADD, imx074_regs.reg_pat[rt]. hmodeadd}, {REG_VMODEADD, imx074_regs.reg_pat[rt]. vmodeadd}, {REG_VAPPLINE_START, imx074_regs.reg_pat[rt]. vapplinepos_start}, {REG_VAPPLINE_END, imx074_regs.reg_pat[rt]. vapplinepos_end}, {REG_SHUTTER, imx074_regs.reg_pat[rt]. shutter}, {REG_HADDAVE, imx074_regs.reg_pat[rt]. haddave}, {REG_LANESEL, imx074_regs.reg_pat[rt]. lanesel}, {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD_OFF}, }; /* reset fps_divider */ imx074_ctrl->fps = 30 * Q8; imx074_ctrl->fps_divider = 1 * 0x400; /* stop streaming */ rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT, MODE_SELECT_STANDBY_MODE); if (rc < 0) return rc; msleep(imx074_delay_msecs_stdby); rc = imx074_i2c_write_w_table(&init_tbl[0], ARRAY_SIZE(init_tbl)); if (rc < 0) return rc; rc = imx074_i2c_write_w_table(&init_mode_tbl[0], ARRAY_SIZE(init_mode_tbl)); if (rc < 0) return rc; rc = imx074_test(imx074_ctrl->set_test); return rc; } break; case UPDATE_PERIODIC: if (rt == RES_PREVIEW || rt == RES_CAPTURE) { struct imx074_i2c_reg_conf mode_tbl[] = { {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD}, {REG_PLL_MULTIPLIER, imx074_regs.reg_pat[rt]. pll_multiplier}, {REG_FRAME_LENGTH_LINES_HI, imx074_regs.reg_pat[rt]. frame_length_lines_hi}, {REG_FRAME_LENGTH_LINES_LO, imx074_regs.reg_pat[rt]. frame_length_lines_lo}, {REG_YADDR_START , imx074_regs.reg_pat[rt]. y_addr_start}, {REG_YAAAR_END, imx074_regs.reg_pat[rt]. y_add_end}, {REG_X_OUTPUT_SIZE_MSB, imx074_regs.reg_pat[rt]. x_output_size_msb}, {REG_X_OUTPUT_SIZE_LSB, imx074_regs.reg_pat[rt]. x_output_size_lsb}, {REG_Y_OUTPUT_SIZE_MSB, imx074_regs.reg_pat[rt]. y_output_size_msb}, {REG_Y_OUTPUT_SIZE_LSB , imx074_regs.reg_pat[rt]. y_output_size_lsb}, {REG_X_EVEN_INC, imx074_regs.reg_pat[rt]. x_even_inc}, {REG_X_ODD_INC, imx074_regs.reg_pat[rt]. x_odd_inc}, {REG_Y_EVEN_INC, imx074_regs.reg_pat[rt]. y_even_inc}, {REG_Y_ODD_INC, imx074_regs.reg_pat[rt]. y_odd_inc}, {REG_HMODEADD, imx074_regs.reg_pat[rt]. hmodeadd}, {REG_VMODEADD, imx074_regs.reg_pat[rt]. vmodeadd}, {REG_VAPPLINE_START, imx074_regs.reg_pat[rt]. vapplinepos_start}, {REG_VAPPLINE_END, imx074_regs.reg_pat[rt]. vapplinepos_end}, {REG_SHUTTER, imx074_regs.reg_pat[rt]. shutter}, {REG_HADDAVE, imx074_regs.reg_pat[rt]. haddave}, {REG_LANESEL, imx074_regs.reg_pat[rt]. lanesel}, {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD_OFF}, }; /* stop streaming */ rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT, MODE_SELECT_STANDBY_MODE); msleep(imx074_delay_msecs_stdby); if (config_csi == 0) { imx074_csi_params.lane_cnt = 4; imx074_csi_params.data_format = CSI_10BIT; imx074_csi_params.lane_assign = 0xe4; imx074_csi_params.dpcm_scheme = 0; imx074_csi_params.settle_cnt = 0x14; rc = msm_camio_csi_config(&imx074_csi_params); /*imx074_delay_msecs_stdby*/ msleep(imx074_delay_msecs_stream); config_csi = 1; } rc = imx074_i2c_write_w_table(&mode_tbl[0], ARRAY_SIZE(mode_tbl)); if (rc < 0) return rc; rc = imx074_i2c_write_b_sensor(REG_MODE_SELECT, MODE_SELECT_STREAM); if (rc < 0) return rc; msleep(imx074_delay_msecs_stream); } break; default: rc = -EINVAL; break; } return rc; } static int32_t imx074_video_config(int mode) { int32_t rc = 0; int rt; /* change sensor resolution if needed */ if (imx074_ctrl->prev_res == QTR_SIZE) { rt = RES_PREVIEW; } else { rt = RES_CAPTURE; } if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0) return rc; imx074_ctrl->curr_res = imx074_ctrl->prev_res; imx074_ctrl->sensormode = mode; return rc; } static int32_t imx074_snapshot_config(int mode) { int32_t rc = 0; int rt = RES_PREVIEW; /* TODO: Used without initialization, guessing. */ /* change sensor resolution if needed */ if (imx074_ctrl->curr_res != imx074_ctrl->pict_res) { if (imx074_ctrl->pict_res == QTR_SIZE) { rt = RES_PREVIEW; } else { rt = RES_CAPTURE; } } if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0) return rc; imx074_ctrl->curr_res = imx074_ctrl->pict_res; imx074_ctrl->sensormode = mode; return rc; } static int32_t imx074_raw_snapshot_config(int mode) { int32_t rc = 0; int rt = RES_PREVIEW; /* TODO: Used without initialization, guessing. */ /* change sensor resolution if needed */ if (imx074_ctrl->curr_res != imx074_ctrl->pict_res) { if (imx074_ctrl->pict_res == QTR_SIZE) { rt = RES_PREVIEW; } else { rt = RES_CAPTURE; } } if (imx074_sensor_setting(UPDATE_PERIODIC, rt) < 0) return rc; imx074_ctrl->curr_res = imx074_ctrl->pict_res; imx074_ctrl->sensormode = mode; return rc; } static int32_t imx074_set_sensor_mode(int mode, int res) { int32_t rc = 0; switch (mode) { case SENSOR_PREVIEW_MODE: rc = imx074_video_config(mode); break; case SENSOR_SNAPSHOT_MODE: rc = imx074_snapshot_config(mode); break; case SENSOR_RAW_SNAPSHOT_MODE: rc = imx074_raw_snapshot_config(mode); break; default: rc = -EINVAL; break; } return rc; } static int32_t imx074_power_down(void) { imx074_i2c_write_b_sensor(REG_MODE_SELECT, MODE_SELECT_STANDBY_MODE); msleep(imx074_delay_msecs_stdby); return 0; } static int imx074_probe_init_done(const struct msm_camera_sensor_info *data) { gpio_set_value_cansleep(data->sensor_reset, 0); gpio_direction_input(data->sensor_reset); gpio_free(data->sensor_reset); return 0; } static int imx074_read_eeprom_data(struct sensor_cfg_data *cfg) { int32_t rc = 0; uint16_t eepromdata = 0; uint8_t addr = 0; addr = 0x10; rc = imx074_i2c_read_w_eeprom(addr, &eepromdata); if (rc < 0) { CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr); return rc; } cfg->cfg.calib_info.r_over_g = eepromdata; addr = 0x12; rc = imx074_i2c_read_w_eeprom(addr, &eepromdata); if (rc < 0) { CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr); return rc; } cfg->cfg.calib_info.b_over_g = eepromdata; addr = 0x14; rc = imx074_i2c_read_w_eeprom(addr, &eepromdata); if (rc < 0) { CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr); return rc; } cfg->cfg.calib_info.gr_over_gb = eepromdata; addr = 0x1A; rc = imx074_i2c_read_w_eeprom(addr, &eepromdata); if (rc < 0) { CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr); return rc; } cfg->cfg.calib_info.macro_2_inf = eepromdata; addr = 0x1C; rc = imx074_i2c_read_w_eeprom(addr, &eepromdata); if (rc < 0) { CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr); return rc; } cfg->cfg.calib_info.inf_2_macro = eepromdata; addr = 0x1E; rc = imx074_i2c_read_w_eeprom(addr, &eepromdata); if (rc < 0) { CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr); return rc; } cfg->cfg.calib_info.stroke_amt = eepromdata; addr = 0x20; rc = imx074_i2c_read_w_eeprom(addr, &eepromdata); if (rc < 0) { CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr); return rc; } cfg->cfg.calib_info.af_pos_1m = eepromdata; addr = 0x22; rc = imx074_i2c_read_w_eeprom(addr, &eepromdata); if (rc < 0) { CDBG("%s: Error Reading EEPROM @ 0x%x\n", __func__, addr); return rc; } cfg->cfg.calib_info.af_pos_inf = eepromdata; return rc; } static int imx074_probe_init_sensor(const struct msm_camera_sensor_info *data) { int32_t rc = 0; unsigned short chipidl, chipidh; CDBG("%s: %d\n", __func__, __LINE__); rc = gpio_request(data->sensor_reset, "imx074"); CDBG(" imx074_probe_init_sensor \n"); if (!rc) { CDBG("sensor_reset = %d\n", rc); gpio_direction_output(data->sensor_reset, 0); usleep_range(5000, 6000); gpio_set_value_cansleep(data->sensor_reset, 1); usleep_range(5000, 6000); } else { CDBG("gpio reset fail"); goto init_probe_done; } CDBG("imx074_probe_init_sensor is called\n"); /* 3. Read sensor Model ID: */ rc = imx074_i2c_read(0x0000, &chipidh, 1); if (rc < 0) { CDBG("Model read failed\n"); goto init_probe_fail; } rc = imx074_i2c_read(0x0001, &chipidl, 1); if (rc < 0) { CDBG("Model read failed\n"); goto init_probe_fail; } CDBG("imx074 model_id = 0x%x 0x%x\n", chipidh, chipidl); /* 4. Compare sensor ID to IMX074 ID: */ if (chipidh != 0x00 || chipidl != 0x74) { rc = -ENODEV; CDBG("imx074_probe_init_sensor fail chip id doesnot match\n"); goto init_probe_fail; } goto init_probe_done; init_probe_fail: CDBG("imx074_probe_init_sensor fails\n"); imx074_probe_init_done(data); init_probe_done: CDBG(" imx074_probe_init_sensor finishes\n"); return rc; } static int32_t imx074_poweron_af(void) { int32_t rc = 0; CDBG("imx074 enable AF actuator, gpio = %d\n", imx074_ctrl->sensordata->vcm_pwd); rc = gpio_request(imx074_ctrl->sensordata->vcm_pwd, "imx074"); if (!rc) { gpio_direction_output(imx074_ctrl->sensordata->vcm_pwd, 1); msleep(20); rc = imx074_af_init(); if (rc < 0) CDBG("imx074 AF initialisation failed\n"); } else { CDBG("%s: AF PowerON gpio_request failed %d\n", __func__, rc); } return rc; } static void imx074_poweroff_af(void) { gpio_set_value_cansleep(imx074_ctrl->sensordata->vcm_pwd, 0); gpio_free(imx074_ctrl->sensordata->vcm_pwd); } /* camsensor_iu060f_imx074_reset */ int imx074_sensor_open_init(const struct msm_camera_sensor_info *data) { int32_t rc = 0; CDBG("%s: %d\n", __func__, __LINE__); CDBG("Calling imx074_sensor_open_init\n"); imx074_ctrl = kzalloc(sizeof(struct imx074_ctrl_t), GFP_KERNEL); if (!imx074_ctrl) { CDBG("imx074_init failed!\n"); rc = -ENOMEM; goto init_done; } imx074_ctrl->fps_divider = 1 * 0x00000400; imx074_ctrl->pict_fps_divider = 1 * 0x00000400; imx074_ctrl->fps = 30 * Q8; imx074_ctrl->set_test = TEST_OFF; imx074_ctrl->prev_res = QTR_SIZE; imx074_ctrl->pict_res = FULL_SIZE; imx074_ctrl->curr_res = INVALID_SIZE; config_csi = 0; if (data) imx074_ctrl->sensordata = data; /* enable mclk first */ msm_camio_clk_rate_set(IMX074_DEFAULT_MASTER_CLK_RATE); usleep_range(1000, 2000); rc = imx074_probe_init_sensor(data); if (rc < 0) { CDBG("Calling imx074_sensor_open_init fail\n"); goto probe_fail; } rc = imx074_sensor_setting(REG_INIT, RES_PREVIEW); if (rc < 0) { CDBG("imx074_sensor_setting failed\n"); goto init_fail; } if (machine_is_msm8x60_fluid()) rc = imx074_poweron_af(); else rc = imx074_af_init(); if (rc < 0) { CDBG("AF initialisation failed\n"); goto init_fail; } else goto init_done; probe_fail: CDBG(" imx074_sensor_open_init probe fail\n"); kfree(imx074_ctrl); return rc; init_fail: CDBG(" imx074_sensor_open_init fail\n"); imx074_probe_init_done(data); kfree(imx074_ctrl); init_done: CDBG("imx074_sensor_open_init done\n"); return rc; } static int imx074_init_client(struct i2c_client *client) { /* Initialize the MSM_CAMI2C Chip */ init_waitqueue_head(&imx074_wait_queue); return 0; } static const struct i2c_device_id imx074_i2c_id[] = { {"imx074", 0}, { } }; static int imx074_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc = 0; CDBG("imx074_probe called!\n"); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { CDBG("i2c_check_functionality failed\n"); goto probe_failure; } imx074_sensorw = kzalloc(sizeof(struct imx074_work_t), GFP_KERNEL); if (!imx074_sensorw) { CDBG("kzalloc failed.\n"); rc = -ENOMEM; goto probe_failure; } i2c_set_clientdata(client, imx074_sensorw); imx074_init_client(client); imx074_client = client; CDBG("imx074_probe successed! rc = %d\n", rc); return 0; probe_failure: CDBG("imx074_probe failed! rc = %d\n", rc); return rc; } static int __exit imx074_remove(struct i2c_client *client) { struct imx074_work_t_t *sensorw = i2c_get_clientdata(client); free_irq(client->irq, sensorw); imx074_client = NULL; kfree(sensorw); return 0; } static struct i2c_driver imx074_i2c_driver = { .id_table = imx074_i2c_id, .probe = imx074_i2c_probe, .remove = __exit_p(imx074_i2c_remove), .driver = { .name = "imx074", }, }; int imx074_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(&imx074_mut); CDBG("imx074_sensor_config: cfgtype = %d\n", cdata.cfgtype); switch (cdata.cfgtype) { case CFG_GET_PICT_FPS: imx074_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 = imx074_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 = imx074_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 = imx074_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 = imx074_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 = imx074_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 = imx074_set_fps(&(cdata.cfg.fps)); break; case CFG_SET_EXP_GAIN: rc = imx074_write_exp_gain( cdata.cfg.exp_gain.gain, cdata.cfg.exp_gain.line); break; case CFG_SET_PICT_EXP_GAIN: rc = imx074_set_pict_exp_gain( cdata.cfg.exp_gain.gain, cdata.cfg.exp_gain.line); break; case CFG_SET_MODE: rc = imx074_set_sensor_mode(cdata.mode, cdata.rs); break; case CFG_PWR_DOWN: rc = imx074_power_down(); break; case CFG_GET_CALIB_DATA: rc = imx074_read_eeprom_data(&cdata); if (rc < 0) break; if (copy_to_user((void *)argp, &cdata, sizeof(cdata))) rc = -EFAULT; break; case CFG_MOVE_FOCUS: rc = imx074_move_focus( cdata.cfg.focus.dir, cdata.cfg.focus.steps); break; case CFG_SET_DEFAULT_FOCUS: rc = imx074_set_default_focus( cdata.cfg.focus.steps); break; case CFG_GET_AF_MAX_STEPS: cdata.max_steps = IMX074_STEPS_NEAR_TO_CLOSEST_INF; if (copy_to_user((void *)argp, &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_SET_EFFECT: default: rc = -EFAULT; break; } mutex_unlock(&imx074_mut); return rc; } static int imx074_sensor_release(void) { int rc = -EBADF; mutex_lock(&imx074_mut); if (machine_is_msm8x60_fluid()) imx074_poweroff_af(); imx074_power_down(); gpio_set_value_cansleep(imx074_ctrl->sensordata->sensor_reset, 0); msleep(5); gpio_direction_input(imx074_ctrl->sensordata->sensor_reset); gpio_free(imx074_ctrl->sensordata->sensor_reset); kfree(imx074_ctrl); imx074_ctrl = NULL; CDBG("imx074_release completed\n"); mutex_unlock(&imx074_mut); return rc; } static int imx074_sensor_probe(const struct msm_camera_sensor_info *info, struct msm_sensor_ctrl *s) { int rc = 0; rc = i2c_add_driver(&imx074_i2c_driver); if (rc < 0 || imx074_client == NULL) { rc = -ENOTSUPP; goto probe_fail; } msm_camio_clk_rate_set(IMX074_DEFAULT_MASTER_CLK_RATE); rc = imx074_probe_init_sensor(info); if (rc < 0) goto probe_fail; s->s_init = imx074_sensor_open_init; s->s_release = imx074_sensor_release; s->s_config = imx074_sensor_config; s->s_mount_angle = info->sensor_platform_info->mount_angle; imx074_probe_init_done(info); return rc; probe_fail: CDBG("imx074_sensor_probe: SENSOR PROBE FAILS!\n"); i2c_del_driver(&imx074_i2c_driver); return rc; } static int __imx074_probe(struct platform_device *pdev) { return msm_camera_drv_start(pdev, imx074_sensor_probe); } static struct platform_driver msm_camera_driver = { .probe = __imx074_probe, .driver = { .name = "msm_camera_imx074", .owner = THIS_MODULE, }, }; static int __init imx074_init(void) { return platform_driver_register(&msm_camera_driver); } module_init(imx074_init); MODULE_DESCRIPTION("Sony 13 MP Bayer sensor driver"); MODULE_LICENSE("GPL v2");