media: i2c: otp_eeprom: add sensor otp eeprom driver

Signed-off-by: Zhenke Fan <fanzy.fan@rock-chips.com>
Change-Id: If08548c4613289c15947428c8468e7fb3769c60e
This commit is contained in:
Zhenke Fan
2021-10-09 10:38:09 +08:00
committed by Tao Huang
parent 5c9a0d04e7
commit add07bf1a2
4 changed files with 621 additions and 0 deletions

View File

@@ -386,6 +386,13 @@ config VIDEO_NVP6324
To compile this driver as a module, choose M here: the
module will be called jaguar1_drv.
config VIDEO_OTP_EEPROM
tristate "sensor otp from eeprom support"
depends on VIDEO_V4L2 && I2C
select V4L2_FWNODE
help
This driver supports OTP load from eeprom.
config VIDEO_SAA7110
tristate "Philips SAA7110 video decoder"
depends on VIDEO_V4L2 && I2C

View File

@@ -163,3 +163,5 @@ obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o
obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
obj-$(CONFIG_SDR_MAX2175) += max2175.o
obj-$(CONFIG_VIDEO_OTP_EEPROM) += otp_eeprom.o

View File

@@ -0,0 +1,475 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2020 Rockchip Electronics Co., Ltd.
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/proc_fs.h>
#include <linux/rk-camera-module.h>
#include <linux/sem.h>
#include <linux/seq_file.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include "otp_eeprom.h"
#define DEVICE_NAME "otp_eeprom"
static inline struct eeprom_device
*sd_to_eeprom(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct eeprom_device, sd);
}
/* Read registers up to 4 at a time */
static int read_reg_otp(struct i2c_client *client, u16 reg,
unsigned int len, u32 *val)
{
struct i2c_msg msgs[2];
u8 *data_be_p;
__be32 data_be = 0;
__be16 reg_addr_be = cpu_to_be16(reg);
int ret;
if (len > 4 || !len)
return -EINVAL;
data_be_p = (u8 *)&data_be;
/* Write register address */
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 2;
msgs[0].buf = (u8 *)&reg_addr_be;
/* Read data from register */
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = len;
msgs[1].buf = &data_be_p[4 - len];
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret != ARRAY_SIZE(msgs))
return -EIO;
*val = be32_to_cpu(data_be);
return 0;
}
static u8 get_vendor_flag(struct i2c_client *client)
{
u8 vendor_flag = 0;
if (client->addr == SLAVE_ADDRESS)
vendor_flag |= 0x80;
return vendor_flag;
}
static int otp_read_data(struct eeprom_device *eeprom_dev)
{
struct i2c_client *client = eeprom_dev->client;
int i;
struct otp_info *otp_ptr;
struct device *dev = &eeprom_dev->client->dev;
int ret = 0;
u32 temp = 0;
otp_ptr = kzalloc(sizeof(*otp_ptr), GFP_KERNEL);
if (!otp_ptr)
return -ENOMEM;
/* OTP base information*/
ret = read_reg_otp(client, INFO_FLAG_REG,
1, &otp_ptr->basic_data.flag);
if (otp_ptr->basic_data.flag == 0x01) {
ret |= read_reg_otp(client, INFO_ID_REG,
1, &otp_ptr->basic_data.id.supplier_id);
ret |= read_reg_otp(client, INFO_ID_REG + 1,
1, &otp_ptr->basic_data.id.year);
ret |= read_reg_otp(client, INFO_ID_REG + 2,
1, &otp_ptr->basic_data.id.month);
ret |= read_reg_otp(client, INFO_ID_REG + 3,
1, &otp_ptr->basic_data.id.day);
ret |= read_reg_otp(client, INFO_ID_REG + 4,
1, &otp_ptr->basic_data.id.sensor_id);
ret |= read_reg_otp(client, INFO_ID_REG + 5,
1, &otp_ptr->basic_data.id.lens_id);
ret |= read_reg_otp(client, INFO_ID_REG + 6,
1, &otp_ptr->basic_data.id.vcm_id);
ret |= read_reg_otp(client, INFO_ID_REG + 7,
1, &otp_ptr->basic_data.id.driver_ic_id);
ret |= read_reg_otp(client, INFO_ID_REG + 8,
1, &otp_ptr->basic_data.id.color_temperature_id);
for (i = 0; i < SMARTISAN_PN_SIZE; i++) {
ret |= read_reg_otp(client, SMARTISAN_PN_REG + i,
1, &temp);
otp_ptr->basic_data.smartisan_pn[i] = temp;
}
for (i = 0; i < MOUDLE_ID_SIZE; i++) {
ret |= read_reg_otp(client, MOUDLE_ID_REG + i,
1, &temp);
otp_ptr->basic_data.modul_id[i] = temp;
}
ret |= read_reg_otp(client, MIRROR_FLIP_REG,
1, &otp_ptr->basic_data.mirror_flip);
ret |= read_reg_otp(client, FULL_SIZE_WIGHT_REG,
2, &temp);
otp_ptr->basic_data.size.width = temp;
ret |= read_reg_otp(client, FULL_SIZE_HEIGHT_REG,
2, &temp);
otp_ptr->basic_data.size.height = temp;
ret |= read_reg_otp(client, INFO_CHECKSUM_REG,
1, &otp_ptr->basic_data.checksum);
dev_dbg(dev, "fasic info: supplier_id(0x%x) lens(0x%x) time(%d_%d_%d)\n",
otp_ptr->basic_data.id.supplier_id,
otp_ptr->basic_data.id.lens_id,
otp_ptr->basic_data.id.year,
otp_ptr->basic_data.id.month,
otp_ptr->basic_data.id.day);
if (ret)
goto err;
}
/* OTP WB calibration data */
ret = read_reg_otp(client, AWB_FLAG_REG,
1, &otp_ptr->awb_data.flag);
if (otp_ptr->awb_data.flag == 0x01) {
ret |= read_reg_otp(client, AWB_VERSION_REG,
1, &otp_ptr->awb_data.version);
ret |= read_reg_otp(client, CUR_R_REG,
2, &otp_ptr->awb_data.r_ratio);
ret |= read_reg_otp(client, CUR_B_REG,
2, &otp_ptr->awb_data.b_ratio);
ret |= read_reg_otp(client, CUR_G_REG,
2, &otp_ptr->awb_data.g_ratio);
ret |= read_reg_otp(client, GOLDEN_R_REG,
2, &otp_ptr->awb_data.r_golden);
ret |= read_reg_otp(client, GOLDEN_B_REG,
2, &otp_ptr->awb_data.b_golden);
ret |= read_reg_otp(client, GOLDEN_G_REG,
2, &otp_ptr->awb_data.g_golden);
ret |= read_reg_otp(client, AWB_CHECKSUM_REG,
1, &otp_ptr->awb_data.checksum);
dev_dbg(dev, "awb version:0x%x\n",
otp_ptr->awb_data.version);
if (ret)
goto err;
}
/* OTP LSC calibration data */
ret = read_reg_otp(client, LSC_FLAG_REG,
1, &otp_ptr->lsc_data.flag);
if (otp_ptr->lsc_data.flag == 0x01) {
ret |= read_reg_otp(client, LSC_VERSION_REG,
1, &otp_ptr->lsc_data.version);
ret |= read_reg_otp(client, LSC_TABLE_SIZE_REG,
2, &temp);
otp_ptr->lsc_data.table_size = temp;
for (i = 0; i < LSC_DATA_SIZE; i++) {
ret |= read_reg_otp(client, LSC_DATA_START_REG + i,
1, &temp);
otp_ptr->lsc_data.data[i] = temp;
}
ret |= read_reg_otp(client, LSC_CHECKSUM_REG,
1, &otp_ptr->lsc_data.checksum);
dev_dbg(dev, "lsc cur:(version 0x%x, table_size 0x%x checksum 0x%x)\n",
otp_ptr->lsc_data.version,
otp_ptr->lsc_data.table_size,
otp_ptr->lsc_data.checksum);
if (ret)
goto err;
}
/* OTP sfr calibration data */
ret = read_reg_otp(client, LSC_FLAG_REG,
1, &otp_ptr->sfr_otp_data.flag);
if (otp_ptr->sfr_otp_data.flag == 0x01) {
ret |= read_reg_otp(client, SFR_EQUIQ_NUM_REG,
1, &otp_ptr->sfr_otp_data.equip_num);
ret |= read_reg_otp(client, SFR_C_HOR_REG,
2, &otp_ptr->sfr_otp_data.center_horizontal);
ret |= read_reg_otp(client, SFR_C_VER_REG,
2, &otp_ptr->sfr_otp_data.center_vertical);
for (i = 0; i < 3; i++) {
ret |= read_reg_otp(client, SFR_TOP_L_HOR_REG + 16 * i,
2, &otp_ptr->sfr_otp_data.data[i].top_l_horizontal);
ret |= read_reg_otp(client, SFR_TOP_L_VER_REG + 16 * i,
2, &otp_ptr->sfr_otp_data.data[i].top_l_vertical);
ret |= read_reg_otp(client, SFR_TOP_R_HOR_REG + 16 * i,
2, &otp_ptr->sfr_otp_data.data[i].top_r_horizontal);
ret |= read_reg_otp(client, SFR_TOP_R_VER_REG + 16 * i,
2, &otp_ptr->sfr_otp_data.data[i].top_r_vertical);
ret |= read_reg_otp(client, SFR_BOTTOM_L_HOR_REG + 16 * i,
2, &otp_ptr->sfr_otp_data.data[i].bottom_l_horizontal);
ret |= read_reg_otp(client, SFR_BOTTOM_L_VER_REG + 16 * i,
2, &otp_ptr->sfr_otp_data.data[i].bottom_l_vertical);
ret |= read_reg_otp(client, SFR_BOTTOM_R_HOR_REG + 16 * i,
2, &otp_ptr->sfr_otp_data.data[i].bottom_r_horizontal);
ret |= read_reg_otp(client, SFR_BOTTOM_R_VER_REG + 16 * i,
2, &otp_ptr->sfr_otp_data.data[i].bottom_r_vertical);
}
ret |= read_reg_otp(client, SFR_CHECKSUM_REG,
1, &otp_ptr->sfr_otp_data.checksum);
if (ret)
goto err;
}
ret = read_reg_otp(client, TOTAL_CHECKSUM_REG,
1, &otp_ptr->total_checksum);
if (ret)
goto err;
if (otp_ptr->total_checksum) {
eeprom_dev->otp = otp_ptr;
} else {
eeprom_dev->otp = NULL;
kfree(otp_ptr);
}
return 0;
err:
eeprom_dev->otp = NULL;
kfree(otp_ptr);
return -EINVAL;
}
static int otp_read(struct eeprom_device *eeprom_dev)
{
u8 vendor_flag = 0;
struct i2c_client *client = eeprom_dev->client;
vendor_flag = get_vendor_flag(client);
if (vendor_flag == 0x80)
otp_read_data(eeprom_dev);
return 0;
}
static long eeprom_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
struct eeprom_device *eeprom_dev =
sd_to_eeprom(sd);
if (!eeprom_dev->otp)
otp_read(eeprom_dev);
if (arg && eeprom_dev->otp)
memcpy(arg, eeprom_dev->otp,
sizeof(struct otp_info));
return 0;
}
#ifdef CONFIG_PROC_FS
static int otp_eeprom_show(struct seq_file *p, void *v)
{
struct eeprom_device *dev = p->private;
int i = 0;
int j = 0;
if (dev) {
seq_puts(p, "[Header]\n");
seq_puts(p, "version=1.0;\n\n");
seq_puts(p, "[RKAWBOTPParam]\n");
seq_printf(p, "flag=%d;\n", dev->otp->awb_data.flag);
seq_printf(p, "r_value=%d;\n", dev->otp->awb_data.r_ratio);
seq_printf(p, "b_value=%d;\n", dev->otp->awb_data.b_ratio);
seq_printf(p, "gr_value=%d;\n", dev->otp->awb_data.g_ratio);
seq_puts(p, "gb_value=-1;\n");
seq_printf(p, "golden_r_value=%d;\n", dev->otp->awb_data.r_golden);
seq_printf(p, "golden_b_value=%d;\n", dev->otp->awb_data.b_golden);
seq_printf(p, "golden_gr_value=%d;\n", dev->otp->awb_data.g_golden);
seq_puts(p, "golden_gb_value=-1;\n\n");
seq_puts(p, "[RKLSCOTPParam]\n");
seq_printf(p, "flag=%d;\n", dev->otp->lsc_data.flag);
seq_printf(p, "width=%d;\n", dev->otp->basic_data.size.width);
seq_printf(p, "height=%d;\n", dev->otp->basic_data.size.height);
seq_printf(p, "tablesize=%d;\n\n", dev->otp->lsc_data.table_size);
seq_puts(p, "lsc_r_table=\n");
for (i = 0; i < 17; i++) {
for (j = 0; j < 17; j++) {
seq_printf(p, "%d", (dev->otp->lsc_data.data[(i * 17 + j) * 2] << 8)
| dev->otp->lsc_data.data[(i * 17 + j) * 2 + 1]);
if (j < 16)
seq_puts(p, " ");
}
if (i < 16)
seq_puts(p, "\n");
}
seq_puts(p, "\n\n");
seq_puts(p, "lsc_b_table=\n");
for (i = 0; i < 17; i++) {
for (j = 0; j < 17; j++) {
seq_printf(p, "%d", (dev->otp->lsc_data.data[(i * 17 + j) * 2 +
1734] << 8) | dev->otp->lsc_data.data[(i * 17 + j) *
2 + 1735]);
if (j < 16)
seq_puts(p, " ");
}
if (i < 16)
seq_puts(p, "\n");
}
seq_puts(p, "\n\n");
seq_puts(p, "lsc_gr_table=\n");
for (i = 0; i < 17; i++) {
for (j = 0; j < 17; j++) {
seq_printf(p, "%d", (dev->otp->lsc_data.data[(i * 17 + j) * 2 +
578] << 8) | dev->otp->lsc_data.data[(i * 17 + j) *
2 + 579]);
if (j < 16)
seq_puts(p, " ");
}
if (i < 16)
seq_puts(p, "\n");
}
seq_puts(p, "\n\n");
seq_puts(p, "lsc_gb_table=\n");
for (i = 0; i < 17; i++) {
for (j = 0; j < 17; j++) {
seq_printf(p, "%d", (dev->otp->lsc_data.data[(i * 17 + j) * 2 +
1156] << 8) | dev->otp->lsc_data.data[(i * 17 + j) *
2 + 1157]);
if (j < 16)
seq_puts(p, " ");
}
if (i < 16)
seq_puts(p, "\n");
}
seq_puts(p, "\n\n");
}
return 0;
}
static int eeprom_open(struct inode *inode, struct file *file)
{
struct eeprom_device *data = PDE_DATA(inode);
return single_open(file, otp_eeprom_show, data);
}
static const struct proc_ops ops = {
.proc_open = eeprom_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static int eeprom_proc_init(struct eeprom_device *dev)
{
dev->procfs = proc_create_data(dev->name, 0, NULL, &ops, dev);
if (!dev->procfs)
return -EINVAL;
return 0;
}
static void eeprom_proc_cleanup(struct eeprom_device *dev)
{
if (dev->procfs)
remove_proc_entry(dev->name, NULL);
dev->procfs = NULL;
}
#endif
static const struct v4l2_subdev_core_ops eeprom_core_ops = {
.ioctl = eeprom_ioctl,
};
static const struct v4l2_subdev_ops eeprom_ops = {
.core = &eeprom_core_ops,
};
static void eeprom_subdev_cleanup(struct eeprom_device *dev)
{
v4l2_device_unregister_subdev(&dev->sd);
media_entity_cleanup(&dev->sd.entity);
}
static int eeprom_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct eeprom_device *eeprom_dev;
dev_info(&client->dev, "probing...\n");
eeprom_dev = devm_kzalloc(&client->dev,
sizeof(*eeprom_dev),
GFP_KERNEL);
if (eeprom_dev == NULL) {
dev_err(&client->dev, "Probe failed\n");
return -ENOMEM;
}
v4l2_i2c_subdev_init(&eeprom_dev->sd,
client, &eeprom_ops);
eeprom_dev->client = client;
sprintf(eeprom_dev->name, "%s", DEVICE_NAME);
eeprom_proc_init(eeprom_dev);
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
pm_runtime_idle(&client->dev);
dev_info(&client->dev, "probing successful\n");
return 0;
}
static int eeprom_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct eeprom_device *eeprom_dev =
sd_to_eeprom(sd);
kfree(eeprom_dev->otp);
pm_runtime_disable(&client->dev);
eeprom_subdev_cleanup(eeprom_dev);
eeprom_proc_cleanup(eeprom_dev);
return 0;
}
static int __maybe_unused eeprom_suspend(struct device *dev)
{
return 0;
}
static int __maybe_unused eeprom_resume(struct device *dev)
{
return 0;
}
static const struct i2c_device_id eeprom_id_table[] = {
{ DEVICE_NAME, 0 },
{ { 0 } }
};
MODULE_DEVICE_TABLE(i2c, eeprom_id_table);
static const struct of_device_id eeprom_of_table[] = {
{ .compatible = "rk,otp_eeprom" },
{ { 0 } }
};
MODULE_DEVICE_TABLE(of, eeprom_of_table);
static const struct dev_pm_ops eeprom_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(eeprom_suspend, eeprom_resume)
SET_RUNTIME_PM_OPS(eeprom_suspend, eeprom_resume, NULL)
};
static struct i2c_driver eeprom_i2c_driver = {
.driver = {
.name = DEVICE_NAME,
.pm = &eeprom_pm_ops,
.of_match_table = eeprom_of_table,
},
.probe = &eeprom_probe,
.remove = &eeprom_remove,
.id_table = eeprom_id_table,
};
module_i2c_driver(eeprom_i2c_driver);
MODULE_DESCRIPTION("OTP driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,137 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2020 Rockchip Electronics Co., Ltd. */
#ifndef OTP_EEPROM_H
#define OTP_EEPROM_H
#define SLAVE_ADDRESS 0x50
#define INFO_FLAG_REG 0X0000
#define INFO_ID_REG 0X0001
#define SMARTISAN_PN_REG 0X000A
#define SMARTISAN_PN_SIZE 0x000C //12
#define MOUDLE_ID_REG 0X0016
#define MOUDLE_ID_SIZE 0X0010 //16
#define MIRROR_FLIP_REG 0X0026
#define FULL_SIZE_WIGHT_REG 0X0027
#define FULL_SIZE_HEIGHT_REG 0X0029
#define INFO_CHECKSUM_REG 0X0033
#define AWB_FLAG_REG 0x0034
#define AWB_VERSION_REG 0x0035
#define CUR_R_REG 0x0036
#define CUR_B_REG 0x0038
#define CUR_G_REG 0x003A
#define GOLDEN_R_REG 0x003C
#define GOLDEN_B_REG 0x003E
#define GOLDEN_G_REG 0x0040
#define AWB_CHECKSUM_REG 0x0062
#define LSC_FLAG_REG 0X0063
#define LSC_VERSION_REG 0x0064
#define LSC_TABLE_SIZE_REG 0x0065
#define LSC_DATA_START_REG 0x0067
#define LSC_DATA_SIZE 0x0908 //2312
#define LSC_CHECKSUM_REG 0x097B
#define SFR_FLAG_REG 0X097C
#define SFR_EQUIQ_NUM_REG 0X097D
#define SFR_C_HOR_REG 0X097E
#define SFR_C_VER_REG 0X0980
#define SFR_TOP_L_HOR_REG 0X0982
#define SFR_TOP_L_VER_REG 0X0984
#define SFR_TOP_R_HOR_REG 0X0986
#define SFR_TOP_R_VER_REG 0X0988
#define SFR_BOTTOM_L_HOR_REG 0X098A
#define SFR_BOTTOM_L_VER_REG 0X098C
#define SFR_BOTTOM_R_HOR_REG 0X098E
#define SFR_BOTTOM_R_VER_REG 0X0990
#define SFR_CHECKSUM_REG 0x09BE
#define TOTAL_CHECKSUM_REG 0x09BF
struct id_defination {
u32 supplier_id;
u32 year;
u32 month;
u32 day;
u32 sensor_id;
u32 lens_id;
u32 vcm_id;
u32 driver_ic_id;
u32 color_temperature_id;
};
struct full_size {
u16 width;
u16 height;
};
struct basic_info {
u32 flag;
struct id_defination id;
u32 smartisan_pn[SMARTISAN_PN_SIZE];
u32 modul_id[MOUDLE_ID_SIZE];
u32 mirror_flip;
struct full_size size;
u32 checksum;
};
struct awb_otp_info {
u32 flag;
u32 version;
u32 r_ratio;
u32 b_ratio;
u32 g_ratio;
u32 r_golden;
u32 b_golden;
u32 g_golden;
u32 checksum;
};
struct lsc_otp_info {
u32 flag;
u32 version;
u16 table_size;
u8 data[LSC_DATA_SIZE];
u32 checksum;
};
struct sfr_data {
u32 top_l_horizontal;
u32 top_l_vertical;
u32 top_r_horizontal;
u32 top_r_vertical;
u32 bottom_l_horizontal;
u32 bottom_l_vertical;
u32 bottom_r_horizontal;
u32 bottom_r_vertical;
};
struct sfr_otp_info {
u32 flag;
u32 equip_num;
u32 center_horizontal;
u32 center_vertical;
struct sfr_data data[3];
u32 checksum;
};
struct otp_info {
struct basic_info basic_data;
struct awb_otp_info awb_data;
struct lsc_otp_info lsc_data;
struct sfr_otp_info sfr_otp_data;
u32 total_checksum;
};
/* eeprom device structure */
struct eeprom_device {
struct v4l2_subdev sd;
struct i2c_client *client;
struct otp_info *otp;
struct proc_dir_entry *procfs;
char name[128];
};
#endif /* OTP_EEPROM_H */