/* * Copyright (C) 2008-2009 QUALCOMM Incorporated. */ #include #include #include #include #include #include #include #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);