summaryrefslogtreecommitdiff
path: root/drivers/staging/dream/camera/mt9d112.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/dream/camera/mt9d112.c')
-rw-r--r--drivers/staging/dream/camera/mt9d112.c761
1 files changed, 761 insertions, 0 deletions
diff --git a/drivers/staging/dream/camera/mt9d112.c b/drivers/staging/dream/camera/mt9d112.c
new file mode 100644
index 000000000000..4f938f9dfc47
--- /dev/null
+++ b/drivers/staging/dream/camera/mt9d112.c
@@ -0,0 +1,761 @@
+/*
+ * 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);