/*
* imx132.c - imx132 sensor driver
*
* Copyright (c) 2012 - 2013, NVIDIA CORPORATION. All rights reserved.
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
* This program is distributed in the hope 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.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IMX132_SIZEOF_I2C_BUF 16
struct imx132_reg {
u16 addr;
u16 val;
};
struct imx132_info {
struct miscdevice miscdev_info;
int mode;
struct imx132_power_rail power;
struct nvc_fuseid fuse_id;
struct i2c_client *i2c_client;
struct imx132_platform_data *pdata;
atomic_t in_use;
};
#define IMX132_TABLE_WAIT_MS 0
#define IMX132_TABLE_END 1
#define IMX132_WAIT_MS 5
#define IMX132_FUSE_ID_SIZE 8
static struct imx132_reg mode_1976x1200[] = {
/* Stand by */
{0x0100, 0x00},
{IMX132_TABLE_WAIT_MS, IMX132_WAIT_MS},
/* global settings */
{0x3087, 0x53},
{0x308B, 0x5A},
{0x3094, 0x11},
{0x309D, 0xA4},
{0x30AA, 0x01},
{0x30C6, 0x00},
{0x30C7, 0x00},
{0x3118, 0x2F},
{0x312A, 0x00},
{0x312B, 0x0B},
{0x312C, 0x0B},
{0x312D, 0x13},
/* PLL Setting */
{0x0305, 0x02},
{0x0307, 0x21},
{0x30A4, 0x02},
{0x303C, 0x4B},
/* Mode Setting */
{0x0340, 0x04},
{0x0341, 0xCA},
{0x0342, 0x08},
{0x0343, 0xC8},
{0x0344, 0x00},
{0x0345, 0x00},
{0x0346, 0x00},
{0x0347, 0x00},
{0x0348, 0x07},
{0x0349, 0xB7},
{0x034A, 0x04},
{0x034B, 0xAF},
{0x034C, 0x07},
{0x034D, 0xB8},
{0x034E, 0x04},
{0x034F, 0xB0},
{0x0381, 0x01},
{0x0383, 0x01},
{0x0385, 0x01},
{0x0387, 0x01},
{0x303D, 0x10},
{0x303E, 0x4A},
{0x3040, 0x08},
{0x3041, 0x97},
{0x3048, 0x00},
{0x304C, 0x2F},
{0x304D, 0x02},
{0x3064, 0x92},
{0x306A, 0x10},
{0x309B, 0x00},
{0x309E, 0x41},
{0x30A0, 0x10},
{0x30A1, 0x0B},
{0x30B2, 0x00},
{0x30D5, 0x00},
{0x30D6, 0x00},
{0x30D7, 0x00},
{0x30D8, 0x00},
{0x30D9, 0x00},
{0x30DA, 0x00},
{0x30DB, 0x00},
{0x30DC, 0x00},
{0x30DD, 0x00},
{0x30DE, 0x00},
{0x3102, 0x0C},
{0x3103, 0x33},
{0x3104, 0x30},
{0x3105, 0x00},
{0x3106, 0xCA},
{0x3107, 0x00},
{0x3108, 0x06},
{0x3109, 0x04},
{0x310A, 0x04},
{0x315C, 0x3D},
{0x315D, 0x3C},
{0x316E, 0x3E},
{0x316F, 0x3D},
{0x3301, 0x00},
{0x3304, 0x07},
{0x3305, 0x06},
{0x3306, 0x19},
{0x3307, 0x03},
{0x3308, 0x0F},
{0x3309, 0x07},
{0x330A, 0x0C},
{0x330B, 0x06},
{0x330C, 0x0B},
{0x330D, 0x07},
{0x330E, 0x03},
{0x3318, 0x67},
{0x3322, 0x09},
{0x3342, 0x00},
{0x3348, 0xE0},
/* Shutter gain Settings */
{0x0202, 0x04},
{0x0203, 0x33},
/* Streaming */
{0x0100, 0x01},
{IMX132_TABLE_WAIT_MS, IMX132_WAIT_MS},
{IMX132_TABLE_END, 0x00}
};
enum {
IMX132_MODE_1976X1200,
};
static struct imx132_reg *mode_table[] = {
[IMX132_MODE_1976X1200] = mode_1976x1200,
};
static inline void
msleep_range(unsigned int delay_base)
{
usleep_range(delay_base*1000, delay_base*1000+500);
}
static inline void
imx132_get_frame_length_regs(struct imx132_reg *regs, u32 frame_length)
{
regs->addr = IMX132_FRAME_LEN_LINES_15_8;
regs->val = (frame_length >> 8) & 0xff;
(regs + 1)->addr = IMX132_FRAME_LEN_LINES_7_0;
(regs + 1)->val = (frame_length) & 0xff;
}
static inline void
imx132_get_coarse_time_regs(struct imx132_reg *regs, u32 coarse_time)
{
regs->addr = IMX132_COARSE_INTEGRATION_TIME_15_8;
regs->val = (coarse_time >> 8) & 0xff;
(regs + 1)->addr = IMX132_COARSE_INTEGRATION_TIME_7_0;
(regs + 1)->val = (coarse_time) & 0xff;
}
static inline void
imx132_get_gain_reg(struct imx132_reg *regs, u16 gain)
{
regs->addr = IMX132_ANA_GAIN_GLOBAL;
regs->val = gain;
}
static int
imx132_read_reg(struct i2c_client *client, u16 addr, u8 *val)
{
int err;
struct i2c_msg msg[2];
unsigned char data[3];
if (!client->adapter)
return -ENODEV;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = data;
/* high byte goes out first */
data[0] = (u8) (addr >> 8);
data[1] = (u8) (addr & 0xff);
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 1;
msg[1].buf = data + 2;
err = i2c_transfer(client->adapter, msg, 2);
if (err != 2)
return -EINVAL;
*val = data[2];
return 0;
}
static int
imx132_write_reg(struct i2c_client *client, u16 addr, u8 val)
{
int err;
struct i2c_msg msg;
unsigned char data[3];
if (!client->adapter)
return -ENODEV;
data[0] = (u8) (addr >> 8);
data[1] = (u8) (addr & 0xff);
data[2] = (u8) (val & 0xff);
msg.addr = client->addr;
msg.flags = 0;
msg.len = 3;
msg.buf = data;
err = i2c_transfer(client->adapter, &msg, 1);
if (err == 1)
return 0;
dev_err(&client->dev, "%s:i2c write failed, %x = %x\n",
__func__, addr, val);
return err;
}
static int imx132_i2c_wr_blk(struct i2c_client *client, u8 *buf, int len)
{
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = 0;
msg.len = len;
msg.buf = buf;
if (i2c_transfer(client->adapter, &msg, 1) != 1)
return -EIO;
return 0;
}
static int
imx132_write_table(struct i2c_client *client,
const struct imx132_reg table[],
const struct imx132_reg override_list[],
int num_override_regs)
{
int err;
u8 i2c_transfer_buf[IMX132_SIZEOF_I2C_BUF];
const struct imx132_reg *next;
const struct imx132_reg *n_next;
u8 *b_ptr = i2c_transfer_buf;
u16 buf_count = 0;
for (next = table; next->addr != IMX132_TABLE_END; next++) {
if (next->addr == IMX132_TABLE_WAIT_MS) {
msleep_range(next->val);
continue;
}
if (!buf_count) {
b_ptr = i2c_transfer_buf;
*b_ptr++ = next->addr >> 8;
*b_ptr++ = next->addr & 0xFF;
buf_count = 2;
}
*b_ptr++ = next->val;
buf_count++;
n_next = next + 1;
if ((n_next->addr == next->addr + 1) &&
(n_next->addr != IMX132_TABLE_WAIT_MS) &&
(buf_count < IMX132_SIZEOF_I2C_BUF) &&
(n_next->addr != IMX132_TABLE_END))
continue;
err = imx132_i2c_wr_blk(client, i2c_transfer_buf, buf_count);
if (err) {
pr_err("%s:imx132_write_table:%d", __func__, err);
return err;
}
buf_count = 0;
}
return 0;
}
static int
imx132_set_mode(struct imx132_info *info, struct imx132_mode *mode)
{
struct device *dev = &info->i2c_client->dev;
int sensor_mode;
int err;
struct imx132_reg reg_list[5];
dev_info(dev, "%s: res [%ux%u] framelen %u coarsetime %u gain %u\n",
__func__, mode->xres, mode->yres,
mode->frame_length, mode->coarse_time, mode->gain);
if ((mode->xres == 1976) && (mode->yres == 1200))
sensor_mode = IMX132_MODE_1976X1200;
else {
dev_err(dev, "%s: invalid resolution to set mode %d %d\n",
__func__, mode->xres, mode->yres);
return -EINVAL;
}
/*
* get a list of override regs for the asking frame length,
* coarse integration time, and gain.
*/
imx132_get_frame_length_regs(reg_list, mode->frame_length);
imx132_get_coarse_time_regs(reg_list + 2, mode->coarse_time);
imx132_get_gain_reg(reg_list + 4, mode->gain);
err = imx132_write_table(info->i2c_client, mode_table[sensor_mode],
reg_list, 5);
if (err)
return err;
info->mode = sensor_mode;
dev_info(dev, "[imx132]: stream on.\n");
return 0;
}
static int
imx132_get_status(struct imx132_info *info, u8 *dev_status)
{
/* TBD */
*dev_status = 0;
return 0;
}
static int
imx132_set_frame_length(struct imx132_info *info,
u32 frame_length,
bool group_hold)
{
struct imx132_reg reg_list[2];
int i = 0;
int ret;
imx132_get_frame_length_regs(reg_list, frame_length);
if (group_hold) {
ret = imx132_write_reg(info->i2c_client,
IMX132_GROUP_PARAM_HOLD, 0x01);
if (ret)
return ret;
}
for (i = 0; i < NUM_OF_FRAME_LEN_REG; i++) {
ret = imx132_write_reg(info->i2c_client, reg_list[i].addr,
reg_list[i].val);
if (ret)
return ret;
}
if (group_hold) {
ret = imx132_write_reg(info->i2c_client,
IMX132_GROUP_PARAM_HOLD, 0x0);
if (ret)
return ret;
}
return 0;
}
static int
imx132_set_coarse_time(struct imx132_info *info,
u32 coarse_time,
bool group_hold)
{
int ret;
struct imx132_reg reg_list[2];
int i = 0;
imx132_get_coarse_time_regs(reg_list, coarse_time);
if (group_hold) {
ret = imx132_write_reg(info->i2c_client,
IMX132_GROUP_PARAM_HOLD,
0x01);
if (ret)
return ret;
}
for (i = 0; i < NUM_OF_COARSE_TIME_REG; i++) {
ret = imx132_write_reg(info->i2c_client, reg_list[i].addr,
reg_list[i].val);
if (ret)
return ret;
}
if (group_hold) {
ret = imx132_write_reg(info->i2c_client,
IMX132_GROUP_PARAM_HOLD, 0x0);
if (ret)
return ret;
}
return 0;
}
static int
imx132_set_gain(struct imx132_info *info, u16 gain, bool group_hold)
{
int ret;
struct imx132_reg reg_list;
imx132_get_gain_reg(®_list, gain);
if (group_hold) {
ret = imx132_write_reg(info->i2c_client,
IMX132_GROUP_PARAM_HOLD, 0x1);
if (ret)
return ret;
}
ret = imx132_write_reg(info->i2c_client, reg_list.addr, reg_list.val);
if (ret)
return ret;
if (group_hold) {
ret = imx132_write_reg(info->i2c_client,
IMX132_GROUP_PARAM_HOLD, 0x0);
if (ret)
return ret;
}
return 0;
}
static int
imx132_set_group_hold(struct imx132_info *info, struct imx132_ae *ae)
{
int ret;
int count = 0;
bool groupHoldEnabled = false;
if (ae->gain_enable)
count++;
if (ae->coarse_time_enable)
count++;
if (ae->frame_length_enable)
count++;
if (count >= 2)
groupHoldEnabled = true;
if (groupHoldEnabled) {
ret = imx132_write_reg(info->i2c_client,
IMX132_GROUP_PARAM_HOLD, 0x1);
if (ret)
return ret;
}
if (ae->gain_enable)
imx132_set_gain(info, ae->gain, false);
if (ae->coarse_time_enable)
imx132_set_coarse_time(info, ae->coarse_time, false);
if (ae->frame_length_enable)
imx132_set_frame_length(info, ae->frame_length, false);
if (groupHoldEnabled) {
ret = imx132_write_reg(info->i2c_client,
IMX132_GROUP_PARAM_HOLD, 0x0);
if (ret)
return ret;
}
return 0;
}
static int imx132_get_fuse_id(struct imx132_info *info)
{
int ret = 0;
int i;
u8 bak = 0;
if (info->fuse_id.size)
return 0;
/*
* TBD 1: If the sensor does not have power at this point
* Need to supply the power, e.g. by calling power on function
*/
ret |= imx132_write_reg(info->i2c_client, 0x34C9, 0x10);
for (i = 0; i < IMX132_FUSE_ID_SIZE ; i++) {
ret |= imx132_read_reg(info->i2c_client, 0x3580 + i, &bak);
info->fuse_id.data[i] = bak;
}
if (!ret)
info->fuse_id.size = i;
return ret;
}
static long
imx132_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err;
struct imx132_info *info = file->private_data;
struct device *dev = &info->i2c_client->dev;
switch (cmd) {
case IMX132_IOCTL_SET_MODE:
{
struct imx132_mode mode;
if (copy_from_user(&mode,
(const void __user *)arg,
sizeof(struct imx132_mode))) {
dev_err(dev, "%s:Failed to get mode from user.\n",
__func__);
return -EFAULT;
}
return imx132_set_mode(info, &mode);
}
case IMX132_IOCTL_SET_FRAME_LENGTH:
return imx132_set_frame_length(info, (u32)arg, true);
case IMX132_IOCTL_SET_COARSE_TIME:
return imx132_set_coarse_time(info, (u32)arg, true);
case IMX132_IOCTL_SET_GAIN:
return imx132_set_gain(info, (u16)arg, true);
case IMX132_IOCTL_GET_STATUS:
{
u8 status;
err = imx132_get_status(info, &status);
if (err)
return err;
if (copy_to_user((void __user *)arg, &status, 1)) {
dev_err(dev, "%s:Failed to copy status to user.\n",
__func__);
return -EFAULT;
}
return 0;
}
case IMX132_IOCTL_GET_FUSEID:
{
err = imx132_get_fuse_id(info);
if (err) {
dev_err(dev, "%s:Failed to get fuse id info.\n",
__func__);
return err;
}
if (copy_to_user((void __user *)arg,
&info->fuse_id,
sizeof(struct nvc_fuseid))) {
dev_info(dev, "%s:Fail copy fuse id to user space\n",
__func__);
return -EFAULT;
}
return 0;
}
case IMX132_IOCTL_SET_GROUP_HOLD:
{
struct imx132_ae ae;
if (copy_from_user(&ae, (const void __user *)arg,
sizeof(struct imx132_ae))) {
dev_info(dev, "%s:fail group hold\n", __func__);
return -EFAULT;
}
return imx132_set_group_hold(info, &ae);
}
default:
dev_err(dev, "%s:unknown cmd.\n", __func__);
return -EINVAL;
}
return 0;
}
static int
imx132_open(struct inode *inode, struct file *file)
{
struct miscdevice *miscdev = file->private_data;
struct imx132_info *info;
info = container_of(miscdev, struct imx132_info, miscdev_info);
/* check if the device is in use */
if (atomic_xchg(&info->in_use, 1)) {
dev_info(&info->i2c_client->dev, "%s:BUSY!\n", __func__);
return -EBUSY;
}
file->private_data = info;
if (info->pdata && info->pdata->power_on)
info->pdata->power_on(&info->power);
else {
dev_err(&info->i2c_client->dev,
"%s:no valid power_on function.\n", __func__);
return -EEXIST;
}
return 0;
}
static int
imx132_release(struct inode *inode, struct file *file)
{
struct imx132_info *info = file->private_data;
if (info->pdata && info->pdata->power_off)
info->pdata->power_off(&info->power);
file->private_data = NULL;
/* warn if device is already released */
WARN_ON(!atomic_xchg(&info->in_use, 0));
return 0;
}
static int imx132_power_put(struct imx132_power_rail *pw)
{
if (likely(pw->dvdd))
regulator_put(pw->dvdd);
if (likely(pw->avdd))
regulator_put(pw->avdd);
if (likely(pw->iovdd))
regulator_put(pw->iovdd);
pw->dvdd = NULL;
pw->avdd = NULL;
pw->iovdd = NULL;
return 0;
}
static int imx132_regulator_get(struct imx132_info *info,
struct regulator **vreg, char vreg_name[])
{
struct regulator *reg = NULL;
int err = 0;
reg = regulator_get(&info->i2c_client->dev, vreg_name);
if (unlikely(IS_ERR(reg))) {
dev_err(&info->i2c_client->dev, "%s %s ERR: %d\n",
__func__, vreg_name, (int)reg);
err = PTR_ERR(reg);
reg = NULL;
} else
dev_dbg(&info->i2c_client->dev, "%s: %s\n",
__func__, vreg_name);
*vreg = reg;
return err;
}
static int imx132_power_get(struct imx132_info *info)
{
struct imx132_power_rail *pw = &info->power;
imx132_regulator_get(info, &pw->dvdd, "vdig"); /* digital 1.2v */
imx132_regulator_get(info, &pw->avdd, "vana_imx132"); /* analog 2.7v */
imx132_regulator_get(info, &pw->iovdd, "vif"); /* interface 1.8v */
return 0;
}
static const struct file_operations imx132_fileops = {
.owner = THIS_MODULE,
.open = imx132_open,
.unlocked_ioctl = imx132_ioctl,
.release = imx132_release,
};
static struct miscdevice imx132_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "imx132",
.fops = &imx132_fileops,
};
static int
imx132_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct imx132_info *info;
int err = 0;
pr_info("[imx132]: probing sensor.\n");
info = devm_kzalloc(&client->dev,
sizeof(struct imx132_info), GFP_KERNEL);
if (!info) {
pr_err("[imx132]:%s:Unable to allocate memory!\n", __func__);
return -ENOMEM;
}
info->pdata = client->dev.platform_data;
info->i2c_client = client;
atomic_set(&info->in_use, 0);
info->mode = -1;
i2c_set_clientdata(client, info);
imx132_power_get(info);
memcpy(&info->miscdev_info,
&imx132_device,
sizeof(struct miscdevice));
err = misc_register(&info->miscdev_info);
if (err) {
imx132_power_put(&info->power);
pr_err("[imx132]:%s:Unable to register misc device!\n",
__func__);
}
return err;
}
static int
imx132_remove(struct i2c_client *client)
{
struct imx132_info *info = i2c_get_clientdata(client);
imx132_power_put(&info->power);
misc_deregister(&imx132_device);
return 0;
}
static const struct i2c_device_id imx132_id[] = {
{ "imx132", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, imx132_id);
static struct i2c_driver imx132_i2c_driver = {
.driver = {
.name = "imx132",
.owner = THIS_MODULE,
},
.probe = imx132_probe,
.remove = imx132_remove,
.id_table = imx132_id,
};
static int __init
imx132_init(void)
{
pr_info("[imx132] sensor driver loading\n");
return i2c_add_driver(&imx132_i2c_driver);
}
static void __exit
imx132_exit(void)
{
i2c_del_driver(&imx132_i2c_driver);
}
module_init(imx132_init);
module_exit(imx132_exit);