| /* |
| * Copyright (C) 2008-2009 QUALCOMM Incorporated. |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/slab.h> |
| #include <linux/types.h> |
| #include <linux/i2c.h> |
| #include <linux/uaccess.h> |
| #include <linux/miscdevice.h> |
| #include <media/msm_camera.h> |
| #include <mach/gpio.h> |
| #include <mach/camera.h> |
| #include "s5k3e2fx.h" |
| |
| #define S5K3E2FX_REG_MODEL_ID 0x0000 |
| #define S5K3E2FX_MODEL_ID 0x3E2F |
| |
| /* PLL Registers */ |
| #define REG_PRE_PLL_CLK_DIV 0x0305 |
| #define REG_PLL_MULTIPLIER_MSB 0x0306 |
| #define REG_PLL_MULTIPLIER_LSB 0x0307 |
| #define REG_VT_PIX_CLK_DIV 0x0301 |
| #define REG_VT_SYS_CLK_DIV 0x0303 |
| #define REG_OP_PIX_CLK_DIV 0x0309 |
| #define REG_OP_SYS_CLK_DIV 0x030B |
| |
| /* Data Format Registers */ |
| #define REG_CCP_DATA_FORMAT_MSB 0x0112 |
| #define REG_CCP_DATA_FORMAT_LSB 0x0113 |
| |
| /* Output Size */ |
| #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 |
| |
| /* Binning */ |
| #define REG_X_EVEN_INC 0x0381 |
| #define REG_X_ODD_INC 0x0383 |
| #define REG_Y_EVEN_INC 0x0385 |
| #define REG_Y_ODD_INC 0x0387 |
| /*Reserved register */ |
| #define REG_BINNING_ENABLE 0x3014 |
| |
| /* Frame Fotmat */ |
| #define REG_FRAME_LENGTH_LINES_MSB 0x0340 |
| #define REG_FRAME_LENGTH_LINES_LSB 0x0341 |
| #define REG_LINE_LENGTH_PCK_MSB 0x0342 |
| #define REG_LINE_LENGTH_PCK_LSB 0x0343 |
| |
| /* MSR setting */ |
| /* Reserved registers */ |
| #define REG_SHADE_CLK_ENABLE 0x30AC |
| #define REG_SEL_CCP 0x30C4 |
| #define REG_VPIX 0x3024 |
| #define REG_CLAMP_ON 0x3015 |
| #define REG_OFFSET 0x307E |
| |
| /* CDS timing settings */ |
| /* Reserved registers */ |
| #define REG_LD_START 0x3000 |
| #define REG_LD_END 0x3001 |
| #define REG_SL_START 0x3002 |
| #define REG_SL_END 0x3003 |
| #define REG_RX_START 0x3004 |
| #define REG_S1_START 0x3005 |
| #define REG_S1_END 0x3006 |
| #define REG_S1S_START 0x3007 |
| #define REG_S1S_END 0x3008 |
| #define REG_S3_START 0x3009 |
| #define REG_S3_END 0x300A |
| #define REG_CMP_EN_START 0x300B |
| #define REG_CLP_SL_START 0x300C |
| #define REG_CLP_SL_END 0x300D |
| #define REG_OFF_START 0x300E |
| #define REG_RMP_EN_START 0x300F |
| #define REG_TX_START 0x3010 |
| #define REG_TX_END 0x3011 |
| #define REG_STX_WIDTH 0x3012 |
| #define REG_TYPE1_AF_ENABLE 0x3130 |
| #define DRIVER_ENABLED 0x0001 |
| #define AUTO_START_ENABLED 0x0010 |
| #define REG_NEW_POSITION 0x3131 |
| #define REG_3152_RESERVED 0x3152 |
| #define REG_315A_RESERVED 0x315A |
| #define REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB 0x0204 |
| #define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB 0x0205 |
| #define REG_FINE_INTEGRATION_TIME 0x0200 |
| #define REG_COARSE_INTEGRATION_TIME 0x0202 |
| #define REG_COARSE_INTEGRATION_TIME_LSB 0x0203 |
| |
| /* Mode select register */ |
| #define S5K3E2FX_REG_MODE_SELECT 0x0100 |
| #define S5K3E2FX_MODE_SELECT_STREAM 0x01 /* start streaming */ |
| #define S5K3E2FX_MODE_SELECT_SW_STANDBY 0x00 /* software standby */ |
| #define S5K3E2FX_REG_SOFTWARE_RESET 0x0103 |
| #define S5K3E2FX_SOFTWARE_RESET 0x01 |
| #define REG_TEST_PATTERN_MODE 0x0601 |
| |
| struct reg_struct { |
| uint8_t pre_pll_clk_div; /* 0x0305 */ |
| uint8_t pll_multiplier_msb; /* 0x0306 */ |
| uint8_t pll_multiplier_lsb; /* 0x0307 */ |
| uint8_t vt_pix_clk_div; /* 0x0301 */ |
| uint8_t vt_sys_clk_div; /* 0x0303 */ |
| uint8_t op_pix_clk_div; /* 0x0309 */ |
| uint8_t op_sys_clk_div; /* 0x030B */ |
| uint8_t ccp_data_format_msb; /* 0x0112 */ |
| uint8_t ccp_data_format_lsb; /* 0x0113 */ |
| uint8_t x_output_size_msb; /* 0x034C */ |
| uint8_t x_output_size_lsb; /* 0x034D */ |
| uint8_t y_output_size_msb; /* 0x034E */ |
| uint8_t y_output_size_lsb; /* 0x034F */ |
| uint8_t x_even_inc; /* 0x0381 */ |
| uint8_t x_odd_inc; /* 0x0383 */ |
| uint8_t y_even_inc; /* 0x0385 */ |
| uint8_t y_odd_inc; /* 0x0387 */ |
| uint8_t binning_enable; /* 0x3014 */ |
| uint8_t frame_length_lines_msb; /* 0x0340 */ |
| uint8_t frame_length_lines_lsb; /* 0x0341 */ |
| uint8_t line_length_pck_msb; /* 0x0342 */ |
| uint8_t line_length_pck_lsb; /* 0x0343 */ |
| uint8_t shade_clk_enable ; /* 0x30AC */ |
| uint8_t sel_ccp; /* 0x30C4 */ |
| uint8_t vpix; /* 0x3024 */ |
| uint8_t clamp_on; /* 0x3015 */ |
| uint8_t offset; /* 0x307E */ |
| uint8_t ld_start; /* 0x3000 */ |
| uint8_t ld_end; /* 0x3001 */ |
| uint8_t sl_start; /* 0x3002 */ |
| uint8_t sl_end; /* 0x3003 */ |
| uint8_t rx_start; /* 0x3004 */ |
| uint8_t s1_start; /* 0x3005 */ |
| uint8_t s1_end; /* 0x3006 */ |
| uint8_t s1s_start; /* 0x3007 */ |
| uint8_t s1s_end; /* 0x3008 */ |
| uint8_t s3_start; /* 0x3009 */ |
| uint8_t s3_end; /* 0x300A */ |
| uint8_t cmp_en_start; /* 0x300B */ |
| uint8_t clp_sl_start; /* 0x300C */ |
| uint8_t clp_sl_end; /* 0x300D */ |
| uint8_t off_start; /* 0x300E */ |
| uint8_t rmp_en_start; /* 0x300F */ |
| uint8_t tx_start; /* 0x3010 */ |
| uint8_t tx_end; /* 0x3011 */ |
| uint8_t stx_width; /* 0x3012 */ |
| uint8_t reg_3152_reserved; /* 0x3152 */ |
| uint8_t reg_315A_reserved; /* 0x315A */ |
| uint8_t analogue_gain_code_global_msb; /* 0x0204 */ |
| uint8_t analogue_gain_code_global_lsb; /* 0x0205 */ |
| uint8_t fine_integration_time; /* 0x0200 */ |
| uint8_t coarse_integration_time; /* 0x0202 */ |
| uint32_t size_h; |
| uint32_t blk_l; |
| uint32_t size_w; |
| uint32_t blk_p; |
| }; |
| |
| struct reg_struct s5k3e2fx_reg_pat[2] = { |
| { /* Preview */ |
| 0x06, /* pre_pll_clk_div REG=0x0305 */ |
| 0x00, /* pll_multiplier_msb REG=0x0306 */ |
| 0x88, /* pll_multiplier_lsb REG=0x0307 */ |
| 0x0a, /* vt_pix_clk_div REG=0x0301 */ |
| 0x01, /* vt_sys_clk_div REG=0x0303 */ |
| 0x0a, /* op_pix_clk_div REG=0x0309 */ |
| 0x01, /* op_sys_clk_div REG=0x030B */ |
| 0x0a, /* ccp_data_format_msb REG=0x0112 */ |
| 0x0a, /* ccp_data_format_lsb REG=0x0113 */ |
| 0x05, /* x_output_size_msb REG=0x034C */ |
| 0x10, /* x_output_size_lsb REG=0x034D */ |
| 0x03, /* y_output_size_msb REG=0x034E */ |
| 0xcc, /* y_output_size_lsb REG=0x034F */ |
| |
| /* enable binning for preview */ |
| 0x01, /* x_even_inc REG=0x0381 */ |
| 0x01, /* x_odd_inc REG=0x0383 */ |
| 0x01, /* y_even_inc REG=0x0385 */ |
| 0x03, /* y_odd_inc REG=0x0387 */ |
| 0x06, /* binning_enable REG=0x3014 */ |
| |
| 0x03, /* frame_length_lines_msb REG=0x0340 */ |
| 0xde, /* frame_length_lines_lsb REG=0x0341 */ |
| 0x0a, /* line_length_pck_msb REG=0x0342 */ |
| 0xac, /* line_length_pck_lsb REG=0x0343 */ |
| 0x81, /* shade_clk_enable REG=0x30AC */ |
| 0x01, /* sel_ccp REG=0x30C4 */ |
| 0x04, /* vpix REG=0x3024 */ |
| 0x00, /* clamp_on REG=0x3015 */ |
| 0x02, /* offset REG=0x307E */ |
| 0x03, /* ld_start REG=0x3000 */ |
| 0x9c, /* ld_end REG=0x3001 */ |
| 0x02, /* sl_start REG=0x3002 */ |
| 0x9e, /* sl_end REG=0x3003 */ |
| 0x05, /* rx_start REG=0x3004 */ |
| 0x0f, /* s1_start REG=0x3005 */ |
| 0x24, /* s1_end REG=0x3006 */ |
| 0x7c, /* s1s_start REG=0x3007 */ |
| 0x9a, /* s1s_end REG=0x3008 */ |
| 0x10, /* s3_start REG=0x3009 */ |
| 0x14, /* s3_end REG=0x300A */ |
| 0x10, /* cmp_en_start REG=0x300B */ |
| 0x04, /* clp_sl_start REG=0x300C */ |
| 0x26, /* clp_sl_end REG=0x300D */ |
| 0x02, /* off_start REG=0x300E */ |
| 0x0e, /* rmp_en_start REG=0x300F */ |
| 0x30, /* tx_start REG=0x3010 */ |
| 0x4e, /* tx_end REG=0x3011 */ |
| 0x1E, /* stx_width REG=0x3012 */ |
| 0x08, /* reg_3152_reserved REG=0x3152 */ |
| 0x10, /* reg_315A_reserved REG=0x315A */ |
| 0x00, /* analogue_gain_code_global_msb REG=0x0204 */ |
| 0x80, /* analogue_gain_code_global_lsb REG=0x0205 */ |
| 0x02, /* fine_integration_time REG=0x0200 */ |
| 0x03, /* coarse_integration_time REG=0x0202 */ |
| 972, |
| 18, |
| 1296, |
| 1436 |
| }, |
| { /* Snapshot */ |
| 0x06, /* pre_pll_clk_div REG=0x0305 */ |
| 0x00, /* pll_multiplier_msb REG=0x0306 */ |
| 0x88, /* pll_multiplier_lsb REG=0x0307 */ |
| 0x0a, /* vt_pix_clk_div REG=0x0301 */ |
| 0x01, /* vt_sys_clk_div REG=0x0303 */ |
| 0x0a, /* op_pix_clk_div REG=0x0309 */ |
| 0x01, /* op_sys_clk_div REG=0x030B */ |
| 0x0a, /* ccp_data_format_msb REG=0x0112 */ |
| 0x0a, /* ccp_data_format_lsb REG=0x0113 */ |
| 0x0a, /* x_output_size_msb REG=0x034C */ |
| 0x30, /* x_output_size_lsb REG=0x034D */ |
| 0x07, /* y_output_size_msb REG=0x034E */ |
| 0xa8, /* y_output_size_lsb REG=0x034F */ |
| |
| /* disable binning for snapshot */ |
| 0x01, /* x_even_inc REG=0x0381 */ |
| 0x01, /* x_odd_inc REG=0x0383 */ |
| 0x01, /* y_even_inc REG=0x0385 */ |
| 0x01, /* y_odd_inc REG=0x0387 */ |
| 0x00, /* binning_enable REG=0x3014 */ |
| |
| 0x07, /* frame_length_lines_msb REG=0x0340 */ |
| 0xb6, /* frame_length_lines_lsb REG=0x0341 */ |
| 0x0a, /* line_length_pck_msb REG=0x0342 */ |
| 0xac, /* line_length_pck_lsb REG=0x0343 */ |
| 0x81, /* shade_clk_enable REG=0x30AC */ |
| 0x01, /* sel_ccp REG=0x30C4 */ |
| 0x04, /* vpix REG=0x3024 */ |
| 0x00, /* clamp_on REG=0x3015 */ |
| 0x02, /* offset REG=0x307E */ |
| 0x03, /* ld_start REG=0x3000 */ |
| 0x9c, /* ld_end REG=0x3001 */ |
| 0x02, /* sl_start REG=0x3002 */ |
| 0x9e, /* sl_end REG=0x3003 */ |
| 0x05, /* rx_start REG=0x3004 */ |
| 0x0f, /* s1_start REG=0x3005 */ |
| 0x24, /* s1_end REG=0x3006 */ |
| 0x7c, /* s1s_start REG=0x3007 */ |
| 0x9a, /* s1s_end REG=0x3008 */ |
| 0x10, /* s3_start REG=0x3009 */ |
| 0x14, /* s3_end REG=0x300A */ |
| 0x10, /* cmp_en_start REG=0x300B */ |
| 0x04, /* clp_sl_start REG=0x300C */ |
| 0x26, /* clp_sl_end REG=0x300D */ |
| 0x02, /* off_start REG=0x300E */ |
| 0x0e, /* rmp_en_start REG=0x300F */ |
| 0x30, /* tx_start REG=0x3010 */ |
| 0x4e, /* tx_end REG=0x3011 */ |
| 0x1E, /* stx_width REG=0x3012 */ |
| 0x08, /* reg_3152_reserved REG=0x3152 */ |
| 0x10, /* reg_315A_reserved REG=0x315A */ |
| 0x00, /* analogue_gain_code_global_msb REG=0x0204 */ |
| 0x80, /* analogue_gain_code_global_lsb REG=0x0205 */ |
| 0x02, /* fine_integration_time REG=0x0200 */ |
| 0x03, /* coarse_integration_time REG=0x0202 */ |
| 1960, |
| 14, |
| 2608, |
| 124 |
| } |
| }; |
| |
| struct s5k3e2fx_work { |
| struct work_struct work; |
| }; |
| static struct s5k3e2fx_work *s5k3e2fx_sensorw; |
| static struct i2c_client *s5k3e2fx_client; |
| |
| struct s5k3e2fx_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 msm_s_resolution prev_res; |
| enum msm_s_resolution pict_res; |
| enum msm_s_resolution curr_res; |
| enum msm_s_test_mode set_test; |
| }; |
| |
| struct s5k3e2fx_i2c_reg_conf { |
| unsigned short waddr; |
| unsigned char bdata; |
| }; |
| |
| static struct s5k3e2fx_ctrl *s5k3e2fx_ctrl; |
| static DECLARE_WAIT_QUEUE_HEAD(s5k3e2fx_wait_queue); |
| DECLARE_MUTEX(s5k3e2fx_sem); |
| |
| static int s5k3e2fx_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(s5k3e2fx_client->adapter, msgs, 2) < 0) { |
| CDBG("s5k3e2fx_i2c_rxdata failed!\n"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int32_t s5k3e2fx_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(s5k3e2fx_client->adapter, msg, 1) < 0) { |
| CDBG("s5k3e2fx_i2c_txdata failed\n"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int32_t s5k3e2fx_i2c_write_b(unsigned short saddr, unsigned short waddr, |
| unsigned char bdata) |
| { |
| int32_t rc = -EIO; |
| unsigned char buf[4]; |
| |
| memset(buf, 0, sizeof(buf)); |
| buf[0] = (waddr & 0xFF00)>>8; |
| buf[1] = (waddr & 0x00FF); |
| buf[2] = bdata; |
| |
| rc = s5k3e2fx_i2c_txdata(saddr, buf, 3); |
| |
| if (rc < 0) |
| CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", |
| waddr, bdata); |
| |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_i2c_write_table( |
| struct s5k3e2fx_i2c_reg_conf *reg_cfg_tbl, int num) |
| { |
| int i; |
| int32_t rc = -EIO; |
| for (i = 0; i < num; i++) { |
| if (rc < 0) |
| break; |
| reg_cfg_tbl++; |
| } |
| |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_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 = s5k3e2fx_i2c_rxdata(saddr, buf, 2); |
| if (rc < 0) |
| return rc; |
| |
| *rdata = buf[0] << 8 | buf[1]; |
| |
| if (rc < 0) |
| CDBG("s5k3e2fx_i2c_read failed!\n"); |
| |
| return rc; |
| } |
| |
| static int s5k3e2fx_probe_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 s5k3e2fx_probe_init_sensor(const struct msm_camera_sensor_info *data) |
| { |
| int32_t rc; |
| uint16_t chipid = 0; |
| |
| rc = gpio_request(data->sensor_reset, "s5k3e2fx"); |
| if (!rc) |
| gpio_direction_output(data->sensor_reset, 1); |
| else |
| goto init_probe_done; |
| |
| mdelay(20); |
| |
| CDBG("s5k3e2fx_sensor_init(): reseting sensor.\n"); |
| |
| rc = s5k3e2fx_i2c_read_w(s5k3e2fx_client->addr, |
| S5K3E2FX_REG_MODEL_ID, &chipid); |
| if (rc < 0) |
| goto init_probe_fail; |
| |
| if (chipid != S5K3E2FX_MODEL_ID) { |
| CDBG("S5K3E2FX wrong model_id = 0x%x\n", chipid); |
| rc = -ENODEV; |
| goto init_probe_fail; |
| } |
| |
| goto init_probe_done; |
| |
| init_probe_fail: |
| s5k3e2fx_probe_init_done(data); |
| init_probe_done: |
| return rc; |
| } |
| |
| static int s5k3e2fx_init_client(struct i2c_client *client) |
| { |
| /* Initialize the MSM_CAMI2C Chip */ |
| init_waitqueue_head(&s5k3e2fx_wait_queue); |
| return 0; |
| } |
| |
| static const struct i2c_device_id s5k3e2fx_i2c_id[] = { |
| { "s5k3e2fx", 0}, |
| { } |
| }; |
| |
| static int s5k3e2fx_i2c_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int rc = 0; |
| CDBG("s5k3e2fx_probe called!\n"); |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| CDBG("i2c_check_functionality failed\n"); |
| goto probe_failure; |
| } |
| |
| s5k3e2fx_sensorw = kzalloc(sizeof(struct s5k3e2fx_work), GFP_KERNEL); |
| if (!s5k3e2fx_sensorw) { |
| CDBG("kzalloc failed.\n"); |
| rc = -ENOMEM; |
| goto probe_failure; |
| } |
| |
| i2c_set_clientdata(client, s5k3e2fx_sensorw); |
| s5k3e2fx_init_client(client); |
| s5k3e2fx_client = client; |
| |
| mdelay(50); |
| |
| CDBG("s5k3e2fx_probe successed! rc = %d\n", rc); |
| return 0; |
| |
| probe_failure: |
| CDBG("s5k3e2fx_probe failed! rc = %d\n", rc); |
| return rc; |
| } |
| |
| static struct i2c_driver s5k3e2fx_i2c_driver = { |
| .id_table = s5k3e2fx_i2c_id, |
| .probe = s5k3e2fx_i2c_probe, |
| .remove = __exit_p(s5k3e2fx_i2c_remove), |
| .driver = { |
| .name = "s5k3e2fx", |
| }, |
| }; |
| |
| static int32_t s5k3e2fx_test(enum msm_s_test_mode mo) |
| { |
| int32_t rc = 0; |
| |
| if (mo == S_TEST_OFF) |
| rc = 0; |
| else |
| rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, |
| REG_TEST_PATTERN_MODE, (uint16_t)mo); |
| |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_setting(enum msm_s_reg_update rupdate, |
| enum msm_s_setting rt) |
| { |
| int32_t rc = 0; |
| uint16_t num_lperf; |
| |
| switch (rupdate) { |
| case S_UPDATE_PERIODIC: |
| if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { |
| |
| struct s5k3e2fx_i2c_reg_conf tbl_1[] = { |
| {REG_CCP_DATA_FORMAT_MSB, s5k3e2fx_reg_pat[rt].ccp_data_format_msb}, |
| {REG_CCP_DATA_FORMAT_LSB, s5k3e2fx_reg_pat[rt].ccp_data_format_lsb}, |
| {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].x_output_size_msb}, |
| {REG_X_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].x_output_size_lsb}, |
| {REG_Y_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].y_output_size_msb}, |
| {REG_Y_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].y_output_size_lsb}, |
| {REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc}, |
| {REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc}, |
| {REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc}, |
| {REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc}, |
| {REG_BINNING_ENABLE, s5k3e2fx_reg_pat[rt].binning_enable}, |
| }; |
| |
| struct s5k3e2fx_i2c_reg_conf tbl_2[] = { |
| {REG_FRAME_LENGTH_LINES_MSB, 0}, |
| {REG_FRAME_LENGTH_LINES_LSB, 0}, |
| {REG_LINE_LENGTH_PCK_MSB, s5k3e2fx_reg_pat[rt].line_length_pck_msb}, |
| {REG_LINE_LENGTH_PCK_LSB, s5k3e2fx_reg_pat[rt].line_length_pck_lsb}, |
| {REG_SHADE_CLK_ENABLE, s5k3e2fx_reg_pat[rt].shade_clk_enable}, |
| {REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp}, |
| {REG_VPIX, s5k3e2fx_reg_pat[rt].vpix}, |
| {REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on}, |
| {REG_OFFSET, s5k3e2fx_reg_pat[rt].offset}, |
| {REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start}, |
| {REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end}, |
| {REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start}, |
| {REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end}, |
| {REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start}, |
| {REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start}, |
| {REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end}, |
| {REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start}, |
| {REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end}, |
| {REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start}, |
| {REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end}, |
| {REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start}, |
| {REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start}, |
| {REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end}, |
| {REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start}, |
| {REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start}, |
| {REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start}, |
| {REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end}, |
| {REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width}, |
| {REG_3152_RESERVED, s5k3e2fx_reg_pat[rt].reg_3152_reserved}, |
| {REG_315A_RESERVED, s5k3e2fx_reg_pat[rt].reg_315A_reserved}, |
| {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb}, |
| {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb}, |
| {REG_FINE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].fine_integration_time}, |
| {REG_COARSE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].coarse_integration_time}, |
| {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM}, |
| }; |
| |
| rc = s5k3e2fx_i2c_write_table(&tbl_1[0], |
| ARRAY_SIZE(tbl_1)); |
| if (rc < 0) |
| return rc; |
| |
| num_lperf = |
| (uint16_t)((s5k3e2fx_reg_pat[rt].frame_length_lines_msb << 8) & 0xFF00) + |
| s5k3e2fx_reg_pat[rt].frame_length_lines_lsb; |
| |
| num_lperf = num_lperf * s5k3e2fx_ctrl->fps_divider / 0x0400; |
| |
| tbl_2[0] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_MSB, (num_lperf & 0xFF00) >> 8}; |
| tbl_2[1] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_LSB, (num_lperf & 0x00FF)}; |
| |
| rc = s5k3e2fx_i2c_write_table(&tbl_2[0], |
| ARRAY_SIZE(tbl_2)); |
| if (rc < 0) |
| return rc; |
| |
| mdelay(5); |
| |
| rc = s5k3e2fx_test(s5k3e2fx_ctrl->set_test); |
| if (rc < 0) |
| return rc; |
| } |
| break; /* UPDATE_PERIODIC */ |
| |
| case S_REG_INIT: |
| if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { |
| |
| struct s5k3e2fx_i2c_reg_conf tbl_3[] = { |
| {S5K3E2FX_REG_SOFTWARE_RESET, S5K3E2FX_SOFTWARE_RESET}, |
| {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_SW_STANDBY}, |
| /* PLL setting */ |
| {REG_PRE_PLL_CLK_DIV, s5k3e2fx_reg_pat[rt].pre_pll_clk_div}, |
| {REG_PLL_MULTIPLIER_MSB, s5k3e2fx_reg_pat[rt].pll_multiplier_msb}, |
| {REG_PLL_MULTIPLIER_LSB, s5k3e2fx_reg_pat[rt].pll_multiplier_lsb}, |
| {REG_VT_PIX_CLK_DIV, s5k3e2fx_reg_pat[rt].vt_pix_clk_div}, |
| {REG_VT_SYS_CLK_DIV, s5k3e2fx_reg_pat[rt].vt_sys_clk_div}, |
| {REG_OP_PIX_CLK_DIV, s5k3e2fx_reg_pat[rt].op_pix_clk_div}, |
| {REG_OP_SYS_CLK_DIV, s5k3e2fx_reg_pat[rt].op_sys_clk_div}, |
| /*Data Format */ |
| {REG_CCP_DATA_FORMAT_MSB, s5k3e2fx_reg_pat[rt].ccp_data_format_msb}, |
| {REG_CCP_DATA_FORMAT_LSB, s5k3e2fx_reg_pat[rt].ccp_data_format_lsb}, |
| /*Output Size */ |
| {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].x_output_size_msb}, |
| {REG_X_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].x_output_size_lsb}, |
| {REG_Y_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].y_output_size_msb}, |
| {REG_Y_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].y_output_size_lsb}, |
| /* Binning */ |
| {REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc}, |
| {REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc }, |
| {REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc}, |
| {REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc}, |
| {REG_BINNING_ENABLE, s5k3e2fx_reg_pat[rt].binning_enable}, |
| /* Frame format */ |
| {REG_FRAME_LENGTH_LINES_MSB, s5k3e2fx_reg_pat[rt].frame_length_lines_msb}, |
| {REG_FRAME_LENGTH_LINES_LSB, s5k3e2fx_reg_pat[rt].frame_length_lines_lsb}, |
| {REG_LINE_LENGTH_PCK_MSB, s5k3e2fx_reg_pat[rt].line_length_pck_msb}, |
| {REG_LINE_LENGTH_PCK_LSB, s5k3e2fx_reg_pat[rt].line_length_pck_lsb}, |
| /* MSR setting */ |
| {REG_SHADE_CLK_ENABLE, s5k3e2fx_reg_pat[rt].shade_clk_enable}, |
| {REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp}, |
| {REG_VPIX, s5k3e2fx_reg_pat[rt].vpix}, |
| {REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on}, |
| {REG_OFFSET, s5k3e2fx_reg_pat[rt].offset}, |
| /* CDS timing setting */ |
| {REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start}, |
| {REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end}, |
| {REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start}, |
| {REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end}, |
| {REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start}, |
| {REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start}, |
| {REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end}, |
| {REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start}, |
| {REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end}, |
| {REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start}, |
| {REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end}, |
| {REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start}, |
| {REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start}, |
| {REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end}, |
| {REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start}, |
| {REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start}, |
| {REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start}, |
| {REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end}, |
| {REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width}, |
| {REG_3152_RESERVED, s5k3e2fx_reg_pat[rt].reg_3152_reserved}, |
| {REG_315A_RESERVED, s5k3e2fx_reg_pat[rt].reg_315A_reserved}, |
| {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb}, |
| {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb}, |
| {REG_FINE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].fine_integration_time}, |
| {REG_COARSE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].coarse_integration_time}, |
| {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM}, |
| }; |
| |
| /* reset fps_divider */ |
| s5k3e2fx_ctrl->fps_divider = 1 * 0x0400; |
| rc = s5k3e2fx_i2c_write_table(&tbl_3[0], |
| ARRAY_SIZE(tbl_3)); |
| if (rc < 0) |
| return rc; |
| } |
| break; /* case REG_INIT: */ |
| |
| default: |
| rc = -EINVAL; |
| break; |
| } /* switch (rupdate) */ |
| |
| return rc; |
| } |
| |
| static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data) |
| { |
| int32_t rc; |
| |
| s5k3e2fx_ctrl = kzalloc(sizeof(struct s5k3e2fx_ctrl), GFP_KERNEL); |
| if (!s5k3e2fx_ctrl) { |
| CDBG("s5k3e2fx_init failed!\n"); |
| rc = -ENOMEM; |
| goto init_done; |
| } |
| |
| s5k3e2fx_ctrl->fps_divider = 1 * 0x00000400; |
| s5k3e2fx_ctrl->pict_fps_divider = 1 * 0x00000400; |
| s5k3e2fx_ctrl->set_test = S_TEST_OFF; |
| s5k3e2fx_ctrl->prev_res = S_QTR_SIZE; |
| s5k3e2fx_ctrl->pict_res = S_FULL_SIZE; |
| |
| if (data) |
| s5k3e2fx_ctrl->sensordata = data; |
| |
| /* enable mclk first */ |
| msm_camio_clk_rate_set(24000000); |
| mdelay(20); |
| |
| msm_camio_camif_pad_reg_reset(); |
| mdelay(20); |
| |
| rc = s5k3e2fx_probe_init_sensor(data); |
| if (rc < 0) |
| goto init_fail1; |
| |
| if (s5k3e2fx_ctrl->prev_res == S_QTR_SIZE) |
| rc = s5k3e2fx_setting(S_REG_INIT, S_RES_PREVIEW); |
| else |
| rc = s5k3e2fx_setting(S_REG_INIT, S_RES_CAPTURE); |
| |
| if (rc < 0) { |
| CDBG("s5k3e2fx_setting failed. rc = %d\n", rc); |
| goto init_fail1; |
| } |
| |
| /* initialize AF */ |
| rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3146, 0x3A); |
| if (rc < 0) |
| goto init_fail1; |
| |
| rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3130, 0x03); |
| if (rc < 0) |
| goto init_fail1; |
| |
| goto init_done; |
| |
| init_fail1: |
| s5k3e2fx_probe_init_done(data); |
| kfree(s5k3e2fx_ctrl); |
| init_done: |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_power_down(void) |
| { |
| int32_t rc = 0; |
| return rc; |
| } |
| |
| static int s5k3e2fx_sensor_release(void) |
| { |
| int rc = -EBADF; |
| |
| down(&s5k3e2fx_sem); |
| |
| s5k3e2fx_power_down(); |
| |
| gpio_direction_output(s5k3e2fx_ctrl->sensordata->sensor_reset, |
| 0); |
| gpio_free(s5k3e2fx_ctrl->sensordata->sensor_reset); |
| |
| kfree(s5k3e2fx_ctrl); |
| s5k3e2fx_ctrl = NULL; |
| |
| CDBG("s5k3e2fx_release completed\n"); |
| |
| up(&s5k3e2fx_sem); |
| return rc; |
| } |
| |
| static void s5k3e2fx_get_pict_fps(uint16_t fps, uint16_t *pfps) |
| { |
| /* input fps is preview fps in Q8 format */ |
| uint32_t divider; /* Q10 */ |
| |
| divider = (uint32_t) |
| ((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + |
| s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * |
| (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + |
| s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p)) * 0x00000400 / |
| ((s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + |
| s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l) * |
| (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + |
| s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p)); |
| |
| /* Verify PCLK settings and frame sizes. */ |
| *pfps = (uint16_t)(fps * divider / 0x00000400); |
| } |
| |
| static uint16_t s5k3e2fx_get_prev_lines_pf(void) |
| { |
| return (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + |
| s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l); |
| } |
| |
| static uint16_t s5k3e2fx_get_prev_pixels_pl(void) |
| { |
| return s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + |
| s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p; |
| } |
| |
| static uint16_t s5k3e2fx_get_pict_lines_pf(void) |
| { |
| return s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + |
| s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; |
| } |
| |
| static uint16_t s5k3e2fx_get_pict_pixels_pl(void) |
| { |
| return s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + |
| s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; |
| } |
| |
| static uint32_t s5k3e2fx_get_pict_max_exp_lc(void) |
| { |
| uint32_t snapshot_lines_per_frame; |
| |
| if (s5k3e2fx_ctrl->pict_res == S_QTR_SIZE) |
| snapshot_lines_per_frame = |
| s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + |
| s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l; |
| else |
| snapshot_lines_per_frame = 3961 * 3; |
| |
| return snapshot_lines_per_frame; |
| } |
| |
| static int32_t s5k3e2fx_set_fps(struct fps_cfg *fps) |
| { |
| /* input is new fps in Q10 format */ |
| int32_t rc = 0; |
| |
| s5k3e2fx_ctrl->fps_divider = fps->fps_div; |
| |
| rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, |
| REG_FRAME_LENGTH_LINES_MSB, |
| (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + |
| s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * |
| s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00) >> 8); |
| if (rc < 0) |
| goto set_fps_done; |
| |
| rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, |
| REG_FRAME_LENGTH_LINES_LSB, |
| (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + |
| s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * |
| s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00)); |
| |
| set_fps_done: |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) |
| { |
| int32_t rc = 0; |
| |
| uint16_t max_legal_gain = 0x0200; |
| uint32_t ll_ratio; /* Q10 */ |
| uint16_t ll_pck, fl_lines; |
| uint16_t offset = 4; |
| uint8_t gain_msb, gain_lsb; |
| uint8_t intg_t_msb, intg_t_lsb; |
| uint8_t ll_pck_msb, ll_pck_lsb, tmp; |
| |
| struct s5k3e2fx_i2c_reg_conf tbl[2]; |
| |
| CDBG("Line:%d s5k3e2fx_write_exp_gain \n", __LINE__); |
| |
| if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { |
| |
| s5k3e2fx_ctrl->my_reg_gain = gain; |
| s5k3e2fx_ctrl->my_reg_line_count = (uint16_t)line; |
| |
| fl_lines = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + |
| s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; |
| |
| ll_pck = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + |
| s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; |
| |
| } else { |
| |
| fl_lines = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + |
| s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; |
| |
| ll_pck = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + |
| s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; |
| } |
| |
| if (gain > max_legal_gain) |
| gain = max_legal_gain; |
| |
| /* in Q10 */ |
| line = (line * s5k3e2fx_ctrl->fps_divider); |
| |
| if (fl_lines < (line / 0x400)) |
| ll_ratio = (line / (fl_lines - offset)); |
| else |
| ll_ratio = 0x400; |
| |
| /* update gain registers */ |
| gain_msb = (gain & 0xFF00) >> 8; |
| gain_lsb = gain & 0x00FF; |
| tbl[0].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB; |
| tbl[0].bdata = gain_msb; |
| tbl[1].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB; |
| tbl[1].bdata = gain_lsb; |
| rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); |
| if (rc < 0) |
| goto write_gain_done; |
| |
| ll_pck = ll_pck * ll_ratio; |
| ll_pck_msb = ((ll_pck / 0x400) & 0xFF00) >> 8; |
| ll_pck_lsb = (ll_pck / 0x400) & 0x00FF; |
| tbl[0].waddr = REG_LINE_LENGTH_PCK_MSB; |
| tbl[0].bdata = s5k3e2fx_reg_pat[S_RES_PREVIEW].line_length_pck_msb; |
| tbl[1].waddr = REG_LINE_LENGTH_PCK_LSB; |
| tbl[1].bdata = s5k3e2fx_reg_pat[S_RES_PREVIEW].line_length_pck_lsb; |
| rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); |
| if (rc < 0) |
| goto write_gain_done; |
| |
| tmp = (ll_pck * 0x400) / ll_ratio; |
| intg_t_msb = (tmp & 0xFF00) >> 8; |
| intg_t_lsb = (tmp & 0x00FF); |
| tbl[0].waddr = REG_COARSE_INTEGRATION_TIME; |
| tbl[0].bdata = intg_t_msb; |
| tbl[1].waddr = REG_COARSE_INTEGRATION_TIME_LSB; |
| tbl[1].bdata = intg_t_lsb; |
| rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); |
| |
| write_gain_done: |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line) |
| { |
| int32_t rc = 0; |
| |
| CDBG("Line:%d s5k3e2fx_set_pict_exp_gain \n", __LINE__); |
| |
| rc = |
| s5k3e2fx_write_exp_gain(gain, line); |
| |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_video_config(int mode, int res) |
| { |
| int32_t rc; |
| |
| switch (res) { |
| case S_QTR_SIZE: |
| rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_PREVIEW); |
| if (rc < 0) |
| return rc; |
| |
| CDBG("s5k3e2fx sensor configuration done!\n"); |
| break; |
| |
| case S_FULL_SIZE: |
| rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); |
| if (rc < 0) |
| return rc; |
| |
| break; |
| |
| default: |
| return 0; |
| } /* switch */ |
| |
| s5k3e2fx_ctrl->prev_res = res; |
| s5k3e2fx_ctrl->curr_res = res; |
| s5k3e2fx_ctrl->sensormode = mode; |
| |
| rc = |
| s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain, |
| s5k3e2fx_ctrl->my_reg_line_count); |
| |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_snapshot_config(int mode) |
| { |
| int32_t rc = 0; |
| |
| rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); |
| if (rc < 0) |
| return rc; |
| |
| s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res; |
| s5k3e2fx_ctrl->sensormode = mode; |
| |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_raw_snapshot_config(int mode) |
| { |
| int32_t rc = 0; |
| |
| rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); |
| if (rc < 0) |
| return rc; |
| |
| s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res; |
| s5k3e2fx_ctrl->sensormode = mode; |
| |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_set_sensor_mode(int mode, int res) |
| { |
| int32_t rc = 0; |
| |
| switch (mode) { |
| case SENSOR_PREVIEW_MODE: |
| rc = s5k3e2fx_video_config(mode, res); |
| break; |
| |
| case SENSOR_SNAPSHOT_MODE: |
| rc = s5k3e2fx_snapshot_config(mode); |
| break; |
| |
| case SENSOR_RAW_SNAPSHOT_MODE: |
| rc = s5k3e2fx_raw_snapshot_config(mode); |
| break; |
| |
| default: |
| rc = -EINVAL; |
| break; |
| } |
| |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_set_default_focus(void) |
| { |
| int32_t rc = 0; |
| |
| rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, |
| 0x3131, 0); |
| if (rc < 0) |
| return rc; |
| |
| rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, |
| 0x3132, 0); |
| if (rc < 0) |
| return rc; |
| |
| s5k3e2fx_ctrl->curr_lens_pos = 0; |
| |
| return rc; |
| } |
| |
| static int32_t s5k3e2fx_move_focus(int direction, int32_t num_steps) |
| { |
| int32_t rc = 0; |
| int32_t i; |
| int16_t step_direction; |
| int16_t actual_step; |
| int16_t next_pos, pos_offset; |
| int16_t init_code = 50; |
| uint8_t next_pos_msb, next_pos_lsb; |
| int16_t s_move[5]; |
| uint32_t gain; /* Q10 format */ |
| |
| if (direction == MOVE_NEAR) |
| step_direction = 20; |
| else if (direction == MOVE_FAR) |
| step_direction = -20; |
| else { |
| CDBG("s5k3e2fx_move_focus failed at line %d ...\n", __LINE__); |
| return -EINVAL; |
| } |
| |
| actual_step = step_direction * (int16_t)num_steps; |
| pos_offset = init_code + s5k3e2fx_ctrl->curr_lens_pos; |
| gain = ((actual_step << 10) / 5) >> 10; |
| |
| for (i = 0; i <= 4; i++) |
| s_move[i] = gain; |
| |
| /* Ring Damping Code */ |
| for (i = 0; i <= 4; i++) { |
| next_pos = (int16_t)(pos_offset + s_move[i]); |
| |
| if (next_pos > (738 + init_code)) |
| next_pos = 738 + init_code; |
| else if (next_pos < 0) |
| next_pos = 0; |
| |
| CDBG("next_position in damping mode = %d\n", next_pos); |
| /* Writing the Values to the actuator */ |
| if (next_pos == init_code) |
| next_pos = 0x00; |
| |
| next_pos_msb = next_pos >> 8; |
| next_pos_lsb = next_pos & 0x00FF; |
| |
| rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3131, next_pos_msb); |
| if (rc < 0) |
| break; |
| |
| rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3132, next_pos_lsb); |
| if (rc < 0) |
| break; |
| |
| pos_offset = next_pos; |
| s5k3e2fx_ctrl->curr_lens_pos = pos_offset - init_code; |
| if (i < 4) |
| mdelay(3); |
| } |
| |
| return rc; |
| } |
| |
| static int s5k3e2fx_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(&s5k3e2fx_sem); |
| |
| CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype); |
| switch (cdata.cfgtype) { |
| case CFG_GET_PICT_FPS: |
| s5k3e2fx_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 = s5k3e2fx_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 = s5k3e2fx_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 = s5k3e2fx_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 = s5k3e2fx_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 = |
| s5k3e2fx_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 = s5k3e2fx_set_fps(&(cdata.cfg.fps)); |
| break; |
| |
| case CFG_SET_EXP_GAIN: |
| rc = |
| s5k3e2fx_write_exp_gain(cdata.cfg.exp_gain.gain, |
| cdata.cfg.exp_gain.line); |
| break; |
| |
| case CFG_SET_PICT_EXP_GAIN: |
| CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__); |
| rc = |
| s5k3e2fx_set_pict_exp_gain( |
| cdata.cfg.exp_gain.gain, |
| cdata.cfg.exp_gain.line); |
| break; |
| |
| case CFG_SET_MODE: |
| rc = |
| s5k3e2fx_set_sensor_mode( |
| cdata.mode, cdata.rs); |
| break; |
| |
| case CFG_PWR_DOWN: |
| rc = s5k3e2fx_power_down(); |
| break; |
| |
| case CFG_MOVE_FOCUS: |
| rc = |
| s5k3e2fx_move_focus( |
| cdata.cfg.focus.dir, |
| cdata.cfg.focus.steps); |
| break; |
| |
| case CFG_SET_DEFAULT_FOCUS: |
| rc = |
| s5k3e2fx_set_default_focus(); |
| break; |
| |
| case CFG_GET_AF_MAX_STEPS: |
| case CFG_SET_EFFECT: |
| case CFG_SET_LENS_SHADING: |
| default: |
| rc = -EINVAL; |
| break; |
| } |
| |
| up(&s5k3e2fx_sem); |
| return rc; |
| } |
| |
| static int s5k3e2fx_sensor_probe(const struct msm_camera_sensor_info *info, |
| struct msm_sensor_ctrl *s) |
| { |
| int rc = 0; |
| |
| rc = i2c_add_driver(&s5k3e2fx_i2c_driver); |
| if (rc < 0 || s5k3e2fx_client == NULL) { |
| rc = -ENOTSUPP; |
| goto probe_fail; |
| } |
| |
| msm_camio_clk_rate_set(24000000); |
| mdelay(20); |
| |
| rc = s5k3e2fx_probe_init_sensor(info); |
| if (rc < 0) |
| goto probe_fail; |
| |
| s->s_init = s5k3e2fx_sensor_open_init; |
| s->s_release = s5k3e2fx_sensor_release; |
| s->s_config = s5k3e2fx_sensor_config; |
| s5k3e2fx_probe_init_done(info); |
| |
| return rc; |
| |
| probe_fail: |
| CDBG("SENSOR PROBE FAILS!\n"); |
| return rc; |
| } |
| |
| static int __s5k3e2fx_probe(struct platform_device *pdev) |
| { |
| return msm_camera_drv_start(pdev, s5k3e2fx_sensor_probe); |
| } |
| |
| static struct platform_driver msm_camera_driver = { |
| .probe = __s5k3e2fx_probe, |
| .driver = { |
| .name = "msm_camera_s5k3e2fx", |
| .owner = THIS_MODULE, |
| }, |
| }; |
| |
| static int __init s5k3e2fx_init(void) |
| { |
| return platform_driver_register(&msm_camera_driver); |
| } |
| |
| module_init(s5k3e2fx_init); |
| |