![Brian Swetland](/assets/img/avatar_default.png)
This patch adds driver for HTC Dream camera. I guess driver is slightly higher quality than usual for staging/ , but it is fairly big and I don't feel like doing all the cleanups myself. Also some parts can probably be removed, as they did not end up in shipping hardware.. Signed-off-by: Pavel Machek <pavel@ucw.cz> Cc: Brian Swetland <swetland@google.com> Cc: Iliyan Malchev <ibm@android.com> Cc: San Mehat <san@android.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
1497 lines
32 KiB
C
1497 lines
32 KiB
C
/*
|
|
* Copyright (C) 2008-2009 QUALCOMM Incorporated.
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/types.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);
|