M7350/kernel/drivers/media/platform/msm/camera_v1/imx074.c
2024-09-09 08:52:07 +00:00

1415 lines
36 KiB
C

/* 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 <linux/delay.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <media/msm_camera.h>
#include <mach/gpio.h>
#include <mach/camera.h>
#include <asm/mach-types.h>
#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");