/*
* ov4682.c - ov4682 sensor driver
*
* Copyright (c) 2015, 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
#include
#include
#include
#include "nvc_utilities.h"
#include "ov4689_tables.h"
#define MAX_BUFFER_SIZE 32
/* Frame length: R0x380e~R0x380f */
#define OV4689_NUM_BYTES_FL (2)
/* Coarse time: R0x3500~R0x3502 */
#define OV4689_NUM_BYTES_CT (3)
/* Gain: R0x3508~R0x3509 */
#define OV4689_NUM_BYTES_GAIN (2)
/* Num of regs in override list */
#define OV4689_NUM_BYTES_OVERRIDES (OV4689_NUM_BYTES_FL + \
OV4689_NUM_BYTES_CT + \
OV4689_NUM_BYTES_GAIN)
#define OV4689_SUPPORT_SENSORID (1)
#define OV4689_SUPPORT_EEPROM (0)
#define OV4689_SUPPORT_DEBUGFS (1)
struct ov4689_info {
struct miscdevice miscdev_info;
int mode;
struct ov4689_power_rail power;
struct ov4689_sensordata sensor_data;
struct i2c_client *i2c_client;
struct ov4689_platform_data *pdata;
struct clk *mclk;
struct regmap *regmap;
#if OV4689_SUPPORT_DEBUGFS
struct dentry *debugdir;
#endif
atomic_t in_use;
#if OV4689_SUPPORT_EEPROM
struct ov4689_eeprom_data eeprom[ov4689_EEPROM_NUM_BLOCKS];
u8 eeprom_buf[ov4689_EEPROM_SIZE];
#endif
};
static const struct regmap_config sensor_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
};
static inline void
msleep_range(unsigned int delay_base)
{
usleep_range(delay_base*1000, delay_base*1000+500);
}
static inline void ov4689_get_frame_length_regs(struct ov4689_reg *regs,
u32 frame_length)
{
/* 2 registers for FL, i.e., 2-byte FL */
regs->addr = 0x380e;
regs->val = (frame_length >> 8) & 0xff;
(regs + 1)->addr = 0x380f;
(regs + 1)->val = (frame_length) & 0xff;
}
static inline void ov4689_get_coarse_time_regs(struct ov4689_reg *regs,
u32 coarse_time)
{
/* 3 registers for CT, i.e., 3-byte CT */
regs->addr = 0x3500;
regs->val = (coarse_time >> 12) & 0xff;
(regs + 1)->addr = 0x3501;
(regs + 1)->val = (coarse_time >> 4) & 0xff;
(regs + 2)->addr = 0x3502;
(regs + 2)->val = (coarse_time & 0xf) << 4;
}
static inline void ov4689_get_gain_reg(struct ov4689_reg *regs,
u16 gain)
{
/* 2 register for gain, i.e., 2-byte gain */
regs->addr = 0x3508;
regs->val = (gain >> 8) & 0xff;
(regs + 1)->addr = 0x3509;
(regs + 1)->val = gain & 0xff;
}
static inline int ov4689_read_reg(struct ov4689_info *info,
u16 addr, u8 *val)
{
return regmap_read(info->regmap, addr, (unsigned int *) val);
}
static int ov4689_write_reg(struct ov4689_info *info, u16 addr, u8 val)
{
int err;
err = regmap_write(info->regmap, addr, val);
if (err)
pr_err("%s:i2c write failed, %x = %x\n",
__func__, addr, val);
return err;
}
static int ov4689_write_table(struct ov4689_info *info,
const struct ov4689_reg table[],
const struct ov4689_reg override_list[],
int num_override_regs)
{
int err;
const struct ov4689_reg *next;
int i;
u16 val;
for (next = table; next->addr != OV4689_TABLE_END; next++) {
if (next->addr == OV4689_TABLE_WAIT_MS) {
msleep_range(next->val);
continue;
}
val = next->val;
/* When an override list is passed in, replace the reg */
/* value to write if the reg is in the list */
if (override_list) {
for (i = 0; i < num_override_regs; i++) {
if (next->addr == override_list[i].addr) {
val = override_list[i].val;
break;
}
}
}
err = ov4689_write_reg(info, next->addr, val);
if (err) {
pr_err("%s:ov4689_write_table:%d", __func__, err);
return err;
}
}
return 0;
}
static int ov4689_set_mode(struct ov4689_info *info, struct ov4689_mode *mode)
{
int sensor_mode;
int err;
struct ov4689_reg reg_list[OV4689_NUM_BYTES_OVERRIDES];
if ((mode->xres == 2688 && mode->yres == 1520) ||
(mode->xres == 2688 && mode->yres == 1504)) {
sensor_mode = OV4689_MODE_2688x1520;
pr_info("ov4689_set_mode 2688x1520\n");
} else if (mode->xres == 1920 && mode->yres == 1080) {
sensor_mode = OV4689_MODE_1920x1080;
pr_info("ov4689_set_mode 1920x1080\n");
} else {
pr_err("There is no this resolution no support %dX%d!!",
mode->xres, mode->yres);
return 1;
}
/* get a list of override regs for the asking frame length, */
/* coarse integration time, and gain. */
ov4689_get_frame_length_regs(reg_list, mode->frame_length);
ov4689_get_coarse_time_regs(reg_list + OV4689_NUM_BYTES_FL,
mode->coarse_time);
ov4689_get_gain_reg(reg_list + OV4689_NUM_BYTES_FL +
OV4689_NUM_BYTES_CT, mode->gain);
err = ov4689_write_table(info, mode_table[sensor_mode],
reg_list, OV4689_NUM_BYTES_OVERRIDES);
info->mode = sensor_mode;
pr_info("[ov4689]: stream on.\n");
return 0;
}
static int ov4689_get_status(struct ov4689_info *info, u8 *dev_status)
{
*dev_status = 0;
return 0;
}
static int ov4689_set_frame_length(struct ov4689_info *info, u32 frame_length,
bool group_hold)
{
struct ov4689_reg reg_list[OV4689_NUM_BYTES_FL];
int i = 0;
int ret;
ov4689_get_frame_length_regs(reg_list, frame_length);
if (group_hold) {
ret = ov4689_write_reg(info, 0x3208, 0x00);
if (ret)
return ret;
}
for (i = 0; i < OV4689_NUM_BYTES_FL; i++) {
ret = ov4689_write_reg(info, reg_list[i].addr,
reg_list[i].val);
if (ret)
return ret;
}
if (group_hold) {
ret = ov4689_write_reg(info, 0x3208, 0x10);
if (ret)
return ret;
ret = ov4689_write_reg(info, 0x3208, 0xA0);
if (ret)
return ret;
}
return 0;
}
static int ov4689_set_coarse_time(struct ov4689_info *info, u32 coarse_time,
bool group_hold)
{
int ret;
struct ov4689_reg reg_list[OV4689_NUM_BYTES_CT];
int i = 0;
ov4689_get_coarse_time_regs(reg_list, coarse_time);
if (group_hold) {
ret = ov4689_write_reg(info, 0x3208, 0x00);
if (ret)
return ret;
}
for (i = 0; i < OV4689_NUM_BYTES_CT; i++) {
ret = ov4689_write_reg(info, reg_list[i].addr,
reg_list[i].val);
if (ret)
return ret;
}
if (group_hold) {
ret = ov4689_write_reg(info, 0x3208, 0x10);
if (ret)
return ret;
ret = ov4689_write_reg(info, 0x3208, 0xA0);
if (ret)
return ret;
}
return 0;
}
static int ov4689_set_gain(struct ov4689_info *info, u16 gain,
bool group_hold)
{
int ret, i;
struct ov4689_reg reg_list[OV4689_NUM_BYTES_GAIN];
ov4689_get_gain_reg(reg_list, gain);
if (group_hold) {
ret = ov4689_write_reg(info, 0x3208, 0x00);
if (ret)
return ret;
}
/* writing 1-byte gain */
for (i = 0; i < OV4689_NUM_BYTES_GAIN; i++) {
ret = ov4689_write_reg(info, reg_list[i].addr,
reg_list[i].val);
if (ret)
return ret;
}
if (group_hold) {
ret = ov4689_write_reg(info, 0x3208, 0x10);
if (ret)
return ret;
ret = ov4689_write_reg(info, 0x3208, 0xA0);
if (ret)
return ret;
}
return 0;
}
static int ov4689_set_group_hold(struct ov4689_info *info,
struct ov4689_grouphold *ae)
{
int ret;
int count = 0;
bool group_hold_enabled = false;
if (ae->gain_enable)
count++;
if (ae->coarse_time_enable)
count++;
if (ae->frame_length_enable)
count++;
if (count >= 2)
group_hold_enabled = true;
if (group_hold_enabled) {
ret = ov4689_write_reg(info, 0x3208, 0x00);
if (ret)
return ret;
}
if (ae->gain_enable)
ov4689_set_gain(info, ae->gain, false);
if (ae->coarse_time_enable)
ov4689_set_coarse_time(info, ae->coarse_time, false);
if (ae->frame_length_enable)
ov4689_set_frame_length(info, ae->frame_length, false);
if (group_hold_enabled) {
ret = ov4689_write_reg(info, 0x3208, 0x10);
if (ret)
return ret;
ret = ov4689_write_reg(info, 0x3208, 0xA0);
if (ret)
return ret;
}
return 0;
}
#if OV4689_SUPPORT_SENSORID
static int ov4689_get_sensor_id(struct ov4689_info *info)
{
int ret = 0;
int i;
u8 bak = 0;
pr_info("%s\n", __func__);
if (info->sensor_data.fuse_id_size)
return 0;
/* select bank 31 */
ov4689_write_reg(info, 0x3d84, 31);
for (i = 0; i < 8; i++) {
ret |= ov4689_read_reg(info, 0x300A + i, &bak);
info->sensor_data.fuse_id[i] = bak;
}
if (!ret)
info->sensor_data.fuse_id_size = 2;
return ret;
}
#endif
static void ov4689_mclk_disable(struct ov4689_info *info)
{
dev_dbg(&info->i2c_client->dev, "%s: disable MCLK\n", __func__);
clk_disable_unprepare(info->mclk);
}
static int ov4689_mclk_enable(struct ov4689_info *info)
{
int err;
unsigned long mclk_init_rate = 24000000;
dev_info(&info->i2c_client->dev, "%s: enable MCLK with %lu Hz\n",
__func__, mclk_init_rate);
err = clk_set_rate(info->mclk, mclk_init_rate);
if (!err)
err = clk_prepare_enable(info->mclk);
return err;
}
#if OV4689_SUPPORT_EEPROM
static int ov4689_eeprom_device_release(struct ov4689_info *info)
{
int i;
for (i = 0; i < ov4689_EEPROM_NUM_BLOCKS; i++) {
if (info->eeprom[i].i2c_client != NULL) {
i2c_unregister_device(info->eeprom[i].i2c_client);
info->eeprom[i].i2c_client = NULL;
}
}
return 0;
}
static int ov4689_eeprom_device_init(struct ov4689_info *info)
{
char *dev_name = "eeprom_ov4689";
static struct regmap_config eeprom_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
int i;
int err;
for (i = 0; i < ov4689_EEPROM_NUM_BLOCKS; i++) {
info->eeprom[i].adap = i2c_get_adapter(
info->i2c_client->adapter->nr);
memset(&info->eeprom[i].brd, 0, sizeof(info->eeprom[i].brd));
strncpy(info->eeprom[i].brd.type, dev_name,
sizeof(info->eeprom[i].brd.type));
info->eeprom[i].brd.addr = ov4689_EEPROM_ADDRESS + i;
info->eeprom[i].i2c_client = i2c_new_device(
info->eeprom[i].adap, &info->eeprom[i].brd);
info->eeprom[i].regmap = devm_regmap_init_i2c(
info->eeprom[i].i2c_client, &eeprom_regmap_config);
if (IS_ERR(info->eeprom[i].regmap)) {
err = PTR_ERR(info->eeprom[i].regmap);
ov4689_eeprom_device_release(info);
return err;
}
}
return 0;
}
static int ov4689_read_eeprom(struct ov4689_info *info,
u8 reg, u16 length, u8 *buf)
{
return regmap_raw_read(info->eeprom[0].regmap, reg, &buf[reg], length);
}
static int ov4689_write_eeprom(struct ov4689_info *info,
u16 addr, u8 val)
{
return regmap_write(info->eeprom[addr >> 8].regmap, addr & 0xFF, val);
}
#endif
static long OV4689_IOCTL(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
struct ov4689_info *info = file->private_data;
switch (_IOC_NR(cmd)) {
case _IOC_NR(OV4689_IOCTL_SET_POWER):
if (!info->pdata)
break;
if (arg && info->pdata->power_on) {
err = ov4689_mclk_enable(info);
if (!err)
err = info->pdata->power_on(&info->power);
if (err < 0)
ov4689_mclk_disable(info);
}
if (!arg && info->pdata->power_off) {
info->pdata->power_off(&info->power);
ov4689_mclk_disable(info);
}
break;
case _IOC_NR(OV4689_IOCTL_SET_MODE):
{
struct ov4689_mode mode;
if (copy_from_user(&mode, (const void __user *)arg,
sizeof(struct ov4689_mode))) {
pr_err("%s:Failed to get mode from user.\n", __func__);
return -EFAULT;
}
return ov4689_set_mode(info, &mode);
}
case _IOC_NR(OV4689_IOCTL_SET_FRAME_LENGTH):
return ov4689_set_frame_length(info, (u32)arg, true);
case _IOC_NR(OV4689_IOCTL_SET_COARSE_TIME):
return ov4689_set_coarse_time(info, (u32)arg, true);
case _IOC_NR(OV4689_IOCTL_SET_GAIN):
return ov4689_set_gain(info, (u16)arg, true);
case _IOC_NR(OV4689_IOCTL_GET_STATUS):
{
u8 status;
err = ov4689_get_status(info, &status);
if (err)
return err;
if (copy_to_user((void __user *)arg, &status, 1)) {
pr_err("%s:Failed to copy status to user\n", __func__);
return -EFAULT;
}
return 0;
}
#if OV4689_SUPPORT_SENSORID
case _IOC_NR(OV4689_IOCTL_GET_SENSORDATA):
{
err = ov4689_get_sensor_id(info);
if (err) {
pr_err("%s:Failed to get fuse id info.\n", __func__);
return err;
}
if (copy_to_user((void __user *)arg, &info->sensor_data,
sizeof(struct ov4689_sensordata))) {
pr_info("%s:Failed to copy fuse id to user space\n",
__func__);
return -EFAULT;
}
return 0;
}
#endif
case _IOC_NR(OV4689_IOCTL_SET_GROUP_HOLD):
{
struct ov4689_grouphold grouphold;
if (copy_from_user(&grouphold, (const void __user *)arg,
sizeof(struct ov4689_grouphold))) {
pr_info("%s:fail group hold\n", __func__);
return -EFAULT;
}
return ov4689_set_group_hold(info, &grouphold);
}
#if OV4689_SUPPORT_EEPROM
/* there is actually one of them really in use
NVC_IOCTL_GET_EEPROM_DATA
or
OV4689_IOCTL_GET_SENSORDATA (legacy?)
*/
case _IOC_NR(NVC_IOCTL_GET_EEPROM_DATA):
{
ov4689_read_eeprom(info,
0,
ov4689_EEPROM_SIZE,
info->eeprom_buf);
if (copy_to_user((void __user *)arg,
info->eeprom_buf, ov4689_EEPROM_SIZE)) {
dev_err(&info->i2c_client->dev,
"%s:Failed to copy status to user\n",
__func__);
return -EFAULT;
}
return 0;
}
case _IOC_NR(NVC_IOCTL_SET_EEPROM_DATA):
{
int i;
if (copy_from_user(info->eeprom_buf,
(const void __user *)arg, ov4689_EEPROM_SIZE)) {
dev_err(&info->i2c_client->dev,
"%s:Failed to read from user buffer\n",
__func__);
return -EFAULT;
}
for (i = 0; i < ov4689_EEPROM_SIZE; i++) {
ov4689_write_eeprom(info,
i,
info->eeprom_buf[i]);
msleep(20);
}
return 0;
}
#endif
default:
pr_err("%s:unknown cmd.\n", __func__);
err = -EINVAL;
}
return err;
}
#if OV4689_SUPPORT_DEBUGFS
static int ov4689_debugfs_show(struct seq_file *s, void *unused)
{
struct ov4689_info *dev = s->private;
dev_dbg(&dev->i2c_client->dev, "%s: ++\n", __func__);
return 0;
}
static ssize_t ov4689_debugfs_write(struct file *file, char const __user *buf,
size_t count, loff_t *offset)
{
struct ov4689_info *dev =
((struct seq_file *)file->private_data)->private;
struct i2c_client *i2c_client = dev->i2c_client;
int ret = 0;
char buffer[MAX_BUFFER_SIZE];
u32 address;
u32 data;
u8 readback;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
if (copy_from_user(&buffer, buf, sizeof(buffer)))
goto debugfs_write_fail;
if (sscanf(buf, "0x%x 0x%x", &address, &data) == 2)
goto set_attr;
if (sscanf(buf, "0X%x 0X%x", &address, &data) == 2)
goto set_attr;
if (sscanf(buf, "%d %d", &address, &data) == 2)
goto set_attr;
if (sscanf(buf, "0x%x 0x%x", &address, &data) == 1)
goto read;
if (sscanf(buf, "0X%x 0X%x", &address, &data) == 1)
goto read;
if (sscanf(buf, "%d %d", &address, &data) == 1)
goto read;
dev_err(&i2c_client->dev, "SYNTAX ERROR: %s\n", buf);
return -EFAULT;
set_attr:
dev_info(&i2c_client->dev,
"new address = %x, data = %x\n", address, data);
ret |= ov4689_write_reg(dev, address, data);
read:
ret |= ov4689_read_reg(dev, address, &readback);
dev_dbg(&i2c_client->dev,
"wrote to address 0x%x with value 0x%x\n",
address, readback);
if (ret)
goto debugfs_write_fail;
return count;
debugfs_write_fail:
dev_err(&i2c_client->dev,
"%s: test pattern write failed\n", __func__);
return -EFAULT;
}
static int ov4689_debugfs_open(struct inode *inode, struct file *file)
{
struct ov4689_info *dev = inode->i_private;
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
return single_open(file, ov4689_debugfs_show, inode->i_private);
}
static const struct file_operations ov4689_debugfs_fops = {
.open = ov4689_debugfs_open,
.read = seq_read,
.write = ov4689_debugfs_write,
.llseek = seq_lseek,
.release = single_release,
};
static void ov4689_remove_debugfs(struct ov4689_info *dev)
{
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s: ++\n", __func__);
debugfs_remove_recursive(dev->debugdir);
dev->debugdir = NULL;
}
static void ov4689_create_debugfs(struct ov4689_info *dev)
{
struct dentry *ret;
struct i2c_client *i2c_client = dev->i2c_client;
dev_dbg(&i2c_client->dev, "%s\n", __func__);
dev->debugdir =
debugfs_create_dir(dev->miscdev_info.this_device->kobj.name,
NULL);
if (!dev->debugdir)
goto remove_debugfs;
ret = debugfs_create_file("d",
S_IWUSR | S_IRUGO,
dev->debugdir, dev,
&ov4689_debugfs_fops);
if (!ret)
goto remove_debugfs;
return;
remove_debugfs:
dev_err(&i2c_client->dev, "couldn't create debugfs\n");
ov4689_remove_debugfs(dev);
}
#endif
static int ov4689_power_on(struct ov4689_power_rail *pw)
{
struct ov4689_info *info = container_of(pw, struct ov4689_info, power);
gpio_set_value(info->pdata->reset_gpio, 0);
usleep_range(10, 20);
gpio_set_value(info->pdata->reset_gpio, 1);
usleep_range(1000, 1100);
return 0;
}
static int ov4689_power_off(struct ov4689_power_rail *pw)
{
struct ov4689_info *info = container_of(pw, struct ov4689_info, power);
gpio_set_value(info->pdata->reset_gpio, 0);
return 0;
}
static int ov4689_open(struct inode *inode, struct file *file)
{
struct miscdevice *miscdev = file->private_data;
struct ov4689_info *info;
info = container_of(miscdev, struct ov4689_info, miscdev_info);
/* check if the device is in use */
if (atomic_xchg(&info->in_use, 1)) {
pr_info("%s:BUSY!\n", __func__);
return -EBUSY;
}
file->private_data = info;
return 0;
}
static int ov4689_release(struct inode *inode, struct file *file)
{
struct ov4689_info *info = file->private_data;
file->private_data = NULL;
/* warn if device is already released */
WARN_ON(!atomic_xchg(&info->in_use, 0));
return 0;
}
static int ov4689_power_put(struct ov4689_power_rail *pw)
{
if (unlikely(!pw))
return -EFAULT;
if (likely(pw->avdd))
regulator_put(pw->avdd);
if (likely(pw->iovdd))
regulator_put(pw->iovdd);
if (likely(pw->dvdd))
regulator_put(pw->dvdd);
pw->avdd = NULL;
pw->iovdd = NULL;
pw->dvdd = NULL;
return 0;
}
static int ov4689_regulator_get(struct ov4689_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 ov4689_power_get(struct ov4689_info *info)
{
struct ov4689_power_rail *pw = &info->power;
int err = 0;
err |= ov4689_regulator_get(info, &pw->avdd, "vana"); /* ananlog 2.7v */
err |= ov4689_regulator_get(info, &pw->dvdd, "vdig"); /* digital 1.2v */
err |= ov4689_regulator_get(info, &pw->iovdd, "vif"); /* IO 1.8v */
return err;
}
static const struct file_operations ov4689_fileops = {
.owner = THIS_MODULE,
.open = ov4689_open,
.unlocked_ioctl = OV4689_IOCTL,
#ifdef CONFIG_COMPAT
.compat_ioctl = OV4689_IOCTL,
#endif
.release = ov4689_release,
};
static struct miscdevice ov4689_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "ov4689",
.fops = &ov4689_fileops,
};
static struct ov4689_platform_data *ov4689_parse_dt(struct i2c_client *client)
{
struct device_node *np = client->dev.of_node;
struct ov4689_platform_data *board_info_pdata;
board_info_pdata = devm_kzalloc(&client->dev, sizeof(*board_info_pdata),
GFP_KERNEL);
if (!board_info_pdata) {
dev_err(&client->dev, "Failed to allocate pdata\n");
return NULL;
}
of_property_read_string(np, "clocks", &board_info_pdata->mclk_name);
board_info_pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
board_info_pdata->power_on = ov4689_power_on;
board_info_pdata->power_off = ov4689_power_off;
return board_info_pdata;
}
static int ov4689_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ov4689_info *info;
int err;
const char *mclk_name;
pr_info("[ov4689]: probing sensor.\n");
info = devm_kzalloc(&client->dev,
sizeof(struct ov4689_info), GFP_KERNEL);
if (!info) {
pr_err("%s:Unable to allocate memory!\n", __func__);
return -ENOMEM;
}
info->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config);
if (IS_ERR(info->regmap)) {
dev_err(&client->dev,
"regmap init failed: %ld\n", PTR_ERR(info->regmap));
return -ENODEV;
}
if (client->dev.of_node)
info->pdata = ov4689_parse_dt(client);
else
info->pdata = client->dev.platform_data;
if (!info->pdata) {
pr_err("[ov4689]:%s:Unable to get platform data\n", __func__);
return -EFAULT;
}
gpio_request_one(info->pdata->reset_gpio, GPIOF_OUT_INIT_LOW,
"reset_gpio");
info->i2c_client = client;
atomic_set(&info->in_use, 0);
info->mode = -1;
mclk_name = info->pdata->mclk_name ?
info->pdata->mclk_name : "default_mclk";
info->mclk = devm_clk_get(&client->dev, mclk_name);
if (IS_ERR(info->mclk)) {
dev_err(&client->dev, "%s: unable to get clock %s\n",
__func__, mclk_name);
return PTR_ERR(info->mclk);
}
ov4689_power_get(info);
memcpy(&info->miscdev_info,
&ov4689_device,
sizeof(struct miscdevice));
err = misc_register(&info->miscdev_info);
if (err) {
pr_err("%s:Unable to register misc device!\n", __func__);
goto ov4689_probe_fail;
}
i2c_set_clientdata(client, info);
#if OV4689_SUPPORT_EEPROM
/* eeprom interface */
err = ov4689_eeprom_device_init(info);
if (err) {
dev_err(&client->dev,
"Failed to allocate eeprom register map: %d\n", err);
return err;
}
#endif
#if OV4689_SUPPORT_DEBUGFS
/* create debugfs interface */
ov4689_create_debugfs(info);
#endif
return 0;
ov4689_probe_fail:
ov4689_power_put(&info->power);
return err;
}
static int ov4689_remove(struct i2c_client *client)
{
struct ov4689_info *info;
info = i2c_get_clientdata(client);
misc_deregister(&ov4689_device);
ov4689_power_put(&info->power);
#if OV4689_SUPPORT_DEBUGFS
ov4689_remove_debugfs(info);
#endif
#if OV4689_SUPPORT_EEPROM
ov4689_eeprom_device_release(info);
#endif
return 0;
}
static const struct i2c_device_id ov4689_id[] = {
{ "ov4689", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, ov4689_id);
static struct i2c_driver ov4689_i2c_driver = {
.driver = {
.name = "ov4689",
.owner = THIS_MODULE,
},
.probe = ov4689_probe,
.remove = ov4689_remove,
.id_table = ov4689_id,
};
static int __init
ov4689_init(void)
{
pr_info("[ov4689] sensor driver loading\n");
return i2c_add_driver(&ov4689_i2c_driver);
}
static void __exit
ov4689_exit(void)
{
i2c_del_driver(&ov4689_i2c_driver);
}
module_init(ov4689_init);
module_exit(ov4689_exit);