![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>
762 lines
14 KiB
C
762 lines
14 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 <media/msm_camera.h>
|
|
#include <mach/gpio.h>
|
|
#include "mt9d112.h"
|
|
|
|
/* Micron MT9D112 Registers and their values */
|
|
/* Sensor Core Registers */
|
|
#define REG_MT9D112_MODEL_ID 0x3000
|
|
#define MT9D112_MODEL_ID 0x1580
|
|
|
|
/* SOC Registers Page 1 */
|
|
#define REG_MT9D112_SENSOR_RESET 0x301A
|
|
#define REG_MT9D112_STANDBY_CONTROL 0x3202
|
|
#define REG_MT9D112_MCU_BOOT 0x3386
|
|
|
|
struct mt9d112_work {
|
|
struct work_struct work;
|
|
};
|
|
|
|
static struct mt9d112_work *mt9d112_sensorw;
|
|
static struct i2c_client *mt9d112_client;
|
|
|
|
struct mt9d112_ctrl {
|
|
const struct msm_camera_sensor_info *sensordata;
|
|
};
|
|
|
|
|
|
static struct mt9d112_ctrl *mt9d112_ctrl;
|
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(mt9d112_wait_queue);
|
|
DECLARE_MUTEX(mt9d112_sem);
|
|
|
|
|
|
/*=============================================================
|
|
EXTERNAL DECLARATIONS
|
|
==============================================================*/
|
|
extern struct mt9d112_reg mt9d112_regs;
|
|
|
|
|
|
/*=============================================================*/
|
|
|
|
static int mt9d112_reset(const struct msm_camera_sensor_info *dev)
|
|
{
|
|
int rc = 0;
|
|
|
|
rc = gpio_request(dev->sensor_reset, "mt9d112");
|
|
|
|
if (!rc) {
|
|
rc = gpio_direction_output(dev->sensor_reset, 0);
|
|
mdelay(20);
|
|
rc = gpio_direction_output(dev->sensor_reset, 1);
|
|
}
|
|
|
|
gpio_free(dev->sensor_reset);
|
|
return rc;
|
|
}
|
|
|
|
static int32_t mt9d112_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(mt9d112_client->adapter, msg, 1) < 0) {
|
|
CDBG("mt9d112_i2c_txdata failed\n");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int32_t mt9d112_i2c_write(unsigned short saddr,
|
|
unsigned short waddr, unsigned short wdata, enum mt9d112_width width)
|
|
{
|
|
int32_t rc = -EIO;
|
|
unsigned char buf[4];
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
switch (width) {
|
|
case WORD_LEN: {
|
|
buf[0] = (waddr & 0xFF00)>>8;
|
|
buf[1] = (waddr & 0x00FF);
|
|
buf[2] = (wdata & 0xFF00)>>8;
|
|
buf[3] = (wdata & 0x00FF);
|
|
|
|
rc = mt9d112_i2c_txdata(saddr, buf, 4);
|
|
}
|
|
break;
|
|
|
|
case BYTE_LEN: {
|
|
buf[0] = waddr;
|
|
buf[1] = wdata;
|
|
rc = mt9d112_i2c_txdata(saddr, buf, 2);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (rc < 0)
|
|
CDBG(
|
|
"i2c_write failed, addr = 0x%x, val = 0x%x!\n",
|
|
waddr, wdata);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int32_t mt9d112_i2c_write_table(
|
|
struct mt9d112_i2c_reg_conf const *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 = mt9d112_i2c_write(mt9d112_client->addr,
|
|
reg_conf_tbl->waddr, reg_conf_tbl->wdata,
|
|
reg_conf_tbl->width);
|
|
if (rc < 0)
|
|
break;
|
|
if (reg_conf_tbl->mdelay_time != 0)
|
|
mdelay(reg_conf_tbl->mdelay_time);
|
|
reg_conf_tbl++;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int mt9d112_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(mt9d112_client->adapter, msgs, 2) < 0) {
|
|
CDBG("mt9d112_i2c_rxdata failed!\n");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int32_t mt9d112_i2c_read(unsigned short saddr,
|
|
unsigned short raddr, unsigned short *rdata, enum mt9d112_width width)
|
|
{
|
|
int32_t rc = 0;
|
|
unsigned char buf[4];
|
|
|
|
if (!rdata)
|
|
return -EIO;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
switch (width) {
|
|
case WORD_LEN: {
|
|
buf[0] = (raddr & 0xFF00)>>8;
|
|
buf[1] = (raddr & 0x00FF);
|
|
|
|
rc = mt9d112_i2c_rxdata(saddr, buf, 2);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
*rdata = buf[0] << 8 | buf[1];
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (rc < 0)
|
|
CDBG("mt9d112_i2c_read failed!\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int32_t mt9d112_set_lens_roll_off(void)
|
|
{
|
|
int32_t rc = 0;
|
|
rc = mt9d112_i2c_write_table(&mt9d112_regs.rftbl[0],
|
|
mt9d112_regs.rftbl_size);
|
|
return rc;
|
|
}
|
|
|
|
static long mt9d112_reg_init(void)
|
|
{
|
|
int32_t array_length;
|
|
int32_t i;
|
|
long rc;
|
|
|
|
/* PLL Setup Start */
|
|
rc = mt9d112_i2c_write_table(&mt9d112_regs.plltbl[0],
|
|
mt9d112_regs.plltbl_size);
|
|
|
|
if (rc < 0)
|
|
return rc;
|
|
/* PLL Setup End */
|
|
|
|
array_length = mt9d112_regs.prev_snap_reg_settings_size;
|
|
|
|
/* Configure sensor for Preview mode and Snapshot mode */
|
|
for (i = 0; i < array_length; i++) {
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
mt9d112_regs.prev_snap_reg_settings[i].register_address,
|
|
mt9d112_regs.prev_snap_reg_settings[i].register_value,
|
|
WORD_LEN);
|
|
|
|
if (rc < 0)
|
|
return rc;
|
|
}
|
|
|
|
/* Configure for Noise Reduction, Saturation and Aperture Correction */
|
|
array_length = mt9d112_regs.noise_reduction_reg_settings_size;
|
|
|
|
for (i = 0; i < array_length; i++) {
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
mt9d112_regs.noise_reduction_reg_settings[i].register_address,
|
|
mt9d112_regs.noise_reduction_reg_settings[i].register_value,
|
|
WORD_LEN);
|
|
|
|
if (rc < 0)
|
|
return rc;
|
|
}
|
|
|
|
/* Set Color Kill Saturation point to optimum value */
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x35A4,
|
|
0x0593,
|
|
WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc = mt9d112_i2c_write_table(&mt9d112_regs.stbl[0],
|
|
mt9d112_regs.stbl_size);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc = mt9d112_set_lens_roll_off();
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static long mt9d112_set_sensor_mode(int mode)
|
|
{
|
|
uint16_t clock;
|
|
long rc = 0;
|
|
|
|
switch (mode) {
|
|
case SENSOR_PREVIEW_MODE:
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, 0xA20C, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, 0x0004, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, 0xA215, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, 0x0004, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, 0xA20B, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, 0x0000, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
clock = 0x0250;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x341C, clock, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, 0xA103, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, 0x0001, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
mdelay(5);
|
|
break;
|
|
|
|
case SENSOR_SNAPSHOT_MODE:
|
|
/* Switch to lower fps for Snapshot */
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x341C, 0x0120, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, 0xA120, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, 0x0002, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
mdelay(5);
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, 0xA103, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc =
|
|
mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, 0x0002, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static long mt9d112_set_effect(int mode, int effect)
|
|
{
|
|
uint16_t reg_addr;
|
|
uint16_t reg_val;
|
|
long rc = 0;
|
|
|
|
switch (mode) {
|
|
case SENSOR_PREVIEW_MODE:
|
|
/* Context A Special Effects */
|
|
reg_addr = 0x2799;
|
|
break;
|
|
|
|
case SENSOR_SNAPSHOT_MODE:
|
|
/* Context B Special Effects */
|
|
reg_addr = 0x279B;
|
|
break;
|
|
|
|
default:
|
|
reg_addr = 0x2799;
|
|
break;
|
|
}
|
|
|
|
switch (effect) {
|
|
case CAMERA_EFFECT_OFF: {
|
|
reg_val = 0x6440;
|
|
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, reg_addr, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, reg_val, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
}
|
|
break;
|
|
|
|
case CAMERA_EFFECT_MONO: {
|
|
reg_val = 0x6441;
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, reg_addr, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, reg_val, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
}
|
|
break;
|
|
|
|
case CAMERA_EFFECT_NEGATIVE: {
|
|
reg_val = 0x6443;
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, reg_addr, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, reg_val, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
}
|
|
break;
|
|
|
|
case CAMERA_EFFECT_SOLARIZE: {
|
|
reg_val = 0x6445;
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, reg_addr, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, reg_val, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
}
|
|
break;
|
|
|
|
case CAMERA_EFFECT_SEPIA: {
|
|
reg_val = 0x6442;
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, reg_addr, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, reg_val, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
}
|
|
break;
|
|
|
|
case CAMERA_EFFECT_PASTEL:
|
|
case CAMERA_EFFECT_MOSAIC:
|
|
case CAMERA_EFFECT_RESIZE:
|
|
return -EINVAL;
|
|
|
|
default: {
|
|
reg_val = 0x6440;
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, reg_addr, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, reg_val, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* Refresh Sequencer */
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x338C, 0xA103, WORD_LEN);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x3390, 0x0005, WORD_LEN);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int mt9d112_sensor_init_probe(const struct msm_camera_sensor_info *data)
|
|
{
|
|
uint16_t model_id = 0;
|
|
int rc = 0;
|
|
|
|
CDBG("init entry \n");
|
|
rc = mt9d112_reset(data);
|
|
if (rc < 0) {
|
|
CDBG("reset failed!\n");
|
|
goto init_probe_fail;
|
|
}
|
|
|
|
mdelay(5);
|
|
|
|
/* Micron suggested Power up block Start:
|
|
* Put MCU into Reset - Stop MCU */
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
REG_MT9D112_MCU_BOOT, 0x0501, WORD_LEN);
|
|
if (rc < 0)
|
|
goto init_probe_fail;
|
|
|
|
/* Pull MCU from Reset - Start MCU */
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
REG_MT9D112_MCU_BOOT, 0x0500, WORD_LEN);
|
|
if (rc < 0)
|
|
goto init_probe_fail;
|
|
|
|
mdelay(5);
|
|
|
|
/* Micron Suggested - Power up block */
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
REG_MT9D112_SENSOR_RESET, 0x0ACC, WORD_LEN);
|
|
if (rc < 0)
|
|
goto init_probe_fail;
|
|
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
REG_MT9D112_STANDBY_CONTROL, 0x0008, WORD_LEN);
|
|
if (rc < 0)
|
|
goto init_probe_fail;
|
|
|
|
/* FUSED_DEFECT_CORRECTION */
|
|
rc = mt9d112_i2c_write(mt9d112_client->addr,
|
|
0x33F4, 0x031D, WORD_LEN);
|
|
if (rc < 0)
|
|
goto init_probe_fail;
|
|
|
|
mdelay(5);
|
|
|
|
/* Micron suggested Power up block End */
|
|
/* Read the Model ID of the sensor */
|
|
rc = mt9d112_i2c_read(mt9d112_client->addr,
|
|
REG_MT9D112_MODEL_ID, &model_id, WORD_LEN);
|
|
if (rc < 0)
|
|
goto init_probe_fail;
|
|
|
|
CDBG("mt9d112 model_id = 0x%x\n", model_id);
|
|
|
|
/* Check if it matches it with the value in Datasheet */
|
|
if (model_id != MT9D112_MODEL_ID) {
|
|
rc = -EINVAL;
|
|
goto init_probe_fail;
|
|
}
|
|
|
|
rc = mt9d112_reg_init();
|
|
if (rc < 0)
|
|
goto init_probe_fail;
|
|
|
|
return rc;
|
|
|
|
init_probe_fail:
|
|
return rc;
|
|
}
|
|
|
|
int mt9d112_sensor_init(const struct msm_camera_sensor_info *data)
|
|
{
|
|
int rc = 0;
|
|
|
|
mt9d112_ctrl = kzalloc(sizeof(struct mt9d112_ctrl), GFP_KERNEL);
|
|
if (!mt9d112_ctrl) {
|
|
CDBG("mt9d112_init failed!\n");
|
|
rc = -ENOMEM;
|
|
goto init_done;
|
|
}
|
|
|
|
if (data)
|
|
mt9d112_ctrl->sensordata = data;
|
|
|
|
/* Input MCLK = 24MHz */
|
|
msm_camio_clk_rate_set(24000000);
|
|
mdelay(5);
|
|
|
|
msm_camio_camif_pad_reg_reset();
|
|
|
|
rc = mt9d112_sensor_init_probe(data);
|
|
if (rc < 0) {
|
|
CDBG("mt9d112_sensor_init failed!\n");
|
|
goto init_fail;
|
|
}
|
|
|
|
init_done:
|
|
return rc;
|
|
|
|
init_fail:
|
|
kfree(mt9d112_ctrl);
|
|
return rc;
|
|
}
|
|
|
|
static int mt9d112_init_client(struct i2c_client *client)
|
|
{
|
|
/* Initialize the MSM_CAMI2C Chip */
|
|
init_waitqueue_head(&mt9d112_wait_queue);
|
|
return 0;
|
|
}
|
|
|
|
int mt9d112_sensor_config(void __user *argp)
|
|
{
|
|
struct sensor_cfg_data cfg_data;
|
|
long rc = 0;
|
|
|
|
if (copy_from_user(&cfg_data,
|
|
(void *)argp,
|
|
sizeof(struct sensor_cfg_data)))
|
|
return -EFAULT;
|
|
|
|
/* down(&mt9d112_sem); */
|
|
|
|
CDBG("mt9d112_ioctl, cfgtype = %d, mode = %d\n",
|
|
cfg_data.cfgtype, cfg_data.mode);
|
|
|
|
switch (cfg_data.cfgtype) {
|
|
case CFG_SET_MODE:
|
|
rc = mt9d112_set_sensor_mode(
|
|
cfg_data.mode);
|
|
break;
|
|
|
|
case CFG_SET_EFFECT:
|
|
rc = mt9d112_set_effect(cfg_data.mode,
|
|
cfg_data.cfg.effect);
|
|
break;
|
|
|
|
case CFG_GET_AF_MAX_STEPS:
|
|
default:
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* up(&mt9d112_sem); */
|
|
|
|
return rc;
|
|
}
|
|
|
|
int mt9d112_sensor_release(void)
|
|
{
|
|
int rc = 0;
|
|
|
|
/* down(&mt9d112_sem); */
|
|
|
|
kfree(mt9d112_ctrl);
|
|
/* up(&mt9d112_sem); */
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int mt9d112_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;
|
|
}
|
|
|
|
mt9d112_sensorw =
|
|
kzalloc(sizeof(struct mt9d112_work), GFP_KERNEL);
|
|
|
|
if (!mt9d112_sensorw) {
|
|
rc = -ENOMEM;
|
|
goto probe_failure;
|
|
}
|
|
|
|
i2c_set_clientdata(client, mt9d112_sensorw);
|
|
mt9d112_init_client(client);
|
|
mt9d112_client = client;
|
|
|
|
CDBG("mt9d112_probe succeeded!\n");
|
|
|
|
return 0;
|
|
|
|
probe_failure:
|
|
kfree(mt9d112_sensorw);
|
|
mt9d112_sensorw = NULL;
|
|
CDBG("mt9d112_probe failed!\n");
|
|
return rc;
|
|
}
|
|
|
|
static const struct i2c_device_id mt9d112_i2c_id[] = {
|
|
{ "mt9d112", 0},
|
|
{ },
|
|
};
|
|
|
|
static struct i2c_driver mt9d112_i2c_driver = {
|
|
.id_table = mt9d112_i2c_id,
|
|
.probe = mt9d112_i2c_probe,
|
|
.remove = __exit_p(mt9d112_i2c_remove),
|
|
.driver = {
|
|
.name = "mt9d112",
|
|
},
|
|
};
|
|
|
|
static int mt9d112_sensor_probe(const struct msm_camera_sensor_info *info,
|
|
struct msm_sensor_ctrl *s)
|
|
{
|
|
int rc = i2c_add_driver(&mt9d112_i2c_driver);
|
|
if (rc < 0 || mt9d112_client == NULL) {
|
|
rc = -ENOTSUPP;
|
|
goto probe_done;
|
|
}
|
|
|
|
/* Input MCLK = 24MHz */
|
|
msm_camio_clk_rate_set(24000000);
|
|
mdelay(5);
|
|
|
|
rc = mt9d112_sensor_init_probe(info);
|
|
if (rc < 0)
|
|
goto probe_done;
|
|
|
|
s->s_init = mt9d112_sensor_init;
|
|
s->s_release = mt9d112_sensor_release;
|
|
s->s_config = mt9d112_sensor_config;
|
|
|
|
probe_done:
|
|
CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__);
|
|
return rc;
|
|
}
|
|
|
|
static int __mt9d112_probe(struct platform_device *pdev)
|
|
{
|
|
return msm_camera_drv_start(pdev, mt9d112_sensor_probe);
|
|
}
|
|
|
|
static struct platform_driver msm_camera_driver = {
|
|
.probe = __mt9d112_probe,
|
|
.driver = {
|
|
.name = "msm_camera_mt9d112",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
static int __init mt9d112_init(void)
|
|
{
|
|
return platform_driver_register(&msm_camera_driver);
|
|
}
|
|
|
|
module_init(mt9d112_init);
|