[PATCH v4 4/4] media: i2c: gc5035: Add OTP configuration handling

From: Tomasz Figa
Date: Wed Sep 02 2020 - 18:48:40 EST


From: Hao He <hao.he@xxxxxxxxxxxxxx>

The sensor OTP holds values for various configuration registers
deteremined at manufacturing time and dead pixel correction tables. Add
code to load both from the OTP and initialize the sensor appropriately.

Signed-off-by: Hao He <hao.he@xxxxxxxxxxxxxx>
Signed-off-by: Xingyu Wu <wuxy@xxxxxxxxxxxxxx>
Signed-off-by: Tomasz Figa <tfiga@xxxxxxxxxxxx>
---
drivers/media/i2c/gc5035.c | 478 +++++++++++++++++++++++++++++++++++++
1 file changed, 478 insertions(+)

diff --git a/drivers/media/i2c/gc5035.c b/drivers/media/i2c/gc5035.c
index 12e1b3a430b5..61645cec6948 100644
--- a/drivers/media/i2c/gc5035.c
+++ b/drivers/media/i2c/gc5035.c
@@ -81,6 +81,57 @@
#define GC5035_TEST_PATTERN_ENABLE 0x11
#define GC5035_TEST_PATTERN_DISABLE 0x10

+/* Page 2 registers */
+
+/* OTP access registers */
+#define GC5035_REG_OTP_MODE 0xf3
+#define GC5035_OTP_PRE_READ 0x20
+#define GC5035_OTP_READ_MODE 0x12
+#define GC5035_OTP_READ_DONE 0x00
+#define GC5035_REG_OTP_DATA 0x6c
+#define GC5035_REG_OTP_ACCESS_ADDR_H 0x69
+#define GC5035_REG_OTP_ACCESS_ADDR_L 0x6a
+#define GC5035_OTP_ACCESS_ADDR_H_MASK 0x1f
+#define GC5035_OTP_ADDR_MASK 0x1fff
+#define GC5035_OTP_ADDR_SHIFT 3
+#define GC5035_REG_DD_TOTALNUM_H 0x01
+#define GC5035_REG_DD_TOTALNUM_L 0x02
+#define GC5035_DD_TOTALNUM_H_MASK 0x07
+#define GC5035_REG_DD_LOAD_STATUS 0x06
+#define GC5035_OTP_BIT_LOAD BIT(0)
+
+/* OTP-related definitions */
+
+#define GC5035_OTP_ID_SIZE 9
+#define GC5035_OTP_ID_DATA_OFFSET 0x0020
+#define GC5035_OTP_DATA_LENGTH 1024
+
+/* OTP DPC parameters */
+#define GC5035_OTP_DPC_FLAG_OFFSET 0x0068
+#define GC5035_OTP_DPC_FLAG_MASK 0x03
+#define GC5035_OTP_FLAG_EMPTY 0x00
+#define GC5035_OTP_FLAG_VALID 0x01
+#define GC5035_OTP_DPC_TOTAL_NUMBER_OFFSET 0x0070
+#define GC5035_OTP_DPC_ERROR_NUMBER_OFFSET 0x0078
+
+/* OTP register parameters */
+#define GC5035_OTP_REG_FLAG_OFFSET 0x0880
+#define GC5035_OTP_REG_DATA_OFFSET 0x0888
+#define GC5035_OTP_REG_ADDR_OFFSET 1
+#define GC5035_OTP_REG_VAL_OFFSET 2
+#define GC5035_OTP_PAGE_FLAG_OFFSET 3
+#define GC5035_OTP_PER_PAGE_SIZE 4
+#define GC5035_OTP_REG_PAGE_MASK 0x07
+#define GC5035_OTP_REG_MAX_GROUP 5
+#define GC5035_OTP_REG_BYTE_PER_GROUP 5
+#define GC5035_OTP_REG_PER_GROUP 2
+#define GC5035_OTP_REG_BYTE_PER_REG 2
+#define GC5035_OTP_REG_DATA_SIZE 25
+#define GC5035_OTP_REG_SIZE 10
+
+#define GC5035_DD_DELAY_US (10 * 1000)
+#define GC5035_DD_TIMEOUT_US (100 * 1000)
+
static const char * const gc5035_supplies[] = {
/*
* Requested separately due to power sequencing needs:
@@ -95,6 +146,21 @@ struct gc5035_regval {
u8 val;
};

+struct gc5035_reg {
+ u8 page;
+ struct gc5035_regval regval;
+};
+
+struct gc5035_otp_regs {
+ unsigned int num_regs;
+ struct gc5035_reg regs[GC5035_OTP_REG_SIZE];
+};
+
+struct gc5035_dpc {
+ bool valid;
+ unsigned int total_num;
+};
+
struct gc5035_mode {
u32 width;
u32 height;
@@ -122,6 +188,11 @@ struct gc5035 {
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;

+ bool otp_read;
+ u8 otp_id[GC5035_OTP_ID_SIZE];
+ struct gc5035_dpc dpc;
+ struct gc5035_otp_regs otp_regs;
+
/*
* Serialize control access, get/set format, get selection
* and start streaming.
@@ -136,6 +207,69 @@ static inline struct gc5035 *to_gc5035(struct v4l2_subdev *sd)
return container_of(sd, struct gc5035, subdev);
}

+static const struct gc5035_regval gc5035_otp_init_regs[] = {
+ {0xfc, 0x01},
+ {0xf4, 0x40},
+ {0xf5, 0xe9},
+ {0xf6, 0x14},
+ {0xf8, 0x49},
+ {0xf9, 0x82},
+ {0xfa, 0x00},
+ {0xfc, 0x81},
+ {0xfe, 0x00},
+ {0x36, 0x01},
+ {0xd3, 0x87},
+ {0x36, 0x00},
+ {0x33, 0x00},
+ {0xf7, 0x01},
+ {0xfc, 0x8e},
+ {0xfe, 0x00},
+ {0xee, 0x30},
+ {0xfa, 0x10},
+ {0xf5, 0xe9},
+ {0xfe, 0x02},
+ {0x67, 0xc0},
+ {0x59, 0x3f},
+ {0x55, 0x84},
+ {0x65, 0x80},
+ {0x66, 0x03},
+ {0xfe, 0x00},
+};
+
+static const struct gc5035_regval gc5035_otp_exit_regs[] = {
+ {0xfe, 0x02},
+ {0x67, 0x00},
+ {0xfe, 0x00},
+ {0xfa, 0x00},
+};
+
+static const struct gc5035_regval gc5035_dd_auto_load_regs[] = {
+ {0xfe, 0x02},
+ {0xbe, 0x00},
+ {0xa9, 0x01},
+ {0x09, 0x33},
+};
+
+static const struct gc5035_regval gc5035_otp_dd_regs[] = {
+ {0x03, 0x00},
+ {0x04, 0x80},
+ {0x95, 0x0a},
+ {0x96, 0x30},
+ {0x97, 0x0a},
+ {0x98, 0x32},
+ {0x99, 0x07},
+ {0x9a, 0xa9},
+ {0xf3, 0x80},
+};
+
+static const struct gc5035_regval gc5035_otp_dd_enable_regs[] = {
+ {0xbe, 0x01},
+ {0x09, 0x00},
+ {0xfe, 0x01},
+ {0x80, 0x02},
+ {0xfe, 0x00},
+};
+
/*
* Xclk 24Mhz
* Pclk 87.6Mhz
@@ -763,6 +897,346 @@ static int gc5035_read_reg(struct gc5035 *gc5035, u8 reg, u8 *val)
return 0;
}

+static int gc5035_otp_read_data(struct gc5035 *gc5035, u16 bit_addr, u8 *data,
+ size_t length)
+{
+ size_t i;
+ int ret;
+
+ if (WARN_ON(bit_addr % 8))
+ return -EINVAL;
+
+ if (WARN_ON(bit_addr / 8 + length > GC5035_OTP_DATA_LENGTH))
+ return -EINVAL;
+
+ ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 2);
+ if (ret)
+ return ret;
+
+ ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_ACCESS_ADDR_H,
+ (bit_addr >> 8) &
+ GC5035_OTP_ACCESS_ADDR_H_MASK);
+ if (ret)
+ return ret;
+
+ ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_ACCESS_ADDR_L,
+ bit_addr & 0xff);
+ if (ret)
+ return ret;
+
+ ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE,
+ GC5035_OTP_PRE_READ);
+ if (ret)
+ goto out_read_done;
+
+ ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE,
+ GC5035_OTP_READ_MODE);
+ if (ret)
+ goto out_read_done;
+
+ for (i = 0; i < length; i++) {
+ ret = gc5035_read_reg(gc5035, GC5035_REG_OTP_DATA, &data[i]);
+ if (ret)
+ goto out_read_done;
+ }
+
+out_read_done:
+ gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE, GC5035_OTP_READ_DONE);
+
+ return ret;
+}
+
+static int gc5035_read_otp_regs(struct gc5035 *gc5035)
+{
+ struct device *dev = &gc5035->client->dev;
+ struct gc5035_otp_regs *otp_regs = &gc5035->otp_regs;
+ u8 regs[GC5035_OTP_REG_DATA_SIZE] = {0};
+ unsigned int i, j;
+ u8 flag;
+ int ret;
+
+ ret = gc5035_otp_read_data(gc5035, GC5035_OTP_REG_FLAG_OFFSET,
+ &flag, 1);
+ if (ret) {
+ dev_err(dev, "failed to read otp reg flag\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "register update flag = 0x%x\n", flag);
+
+ gc5035->otp_regs.num_regs = 0;
+ if (flag != GC5035_OTP_FLAG_VALID)
+ return 0;
+
+ ret = gc5035_otp_read_data(gc5035, GC5035_OTP_REG_DATA_OFFSET,
+ regs, sizeof(regs));
+ if (ret) {
+ dev_err(dev, "failed to read otp reg data\n");
+ return ret;
+ }
+
+ for (i = 0; i < GC5035_OTP_REG_MAX_GROUP; i++) {
+ unsigned int base_group = i * GC5035_OTP_REG_BYTE_PER_GROUP;
+
+ for (j = 0; j < GC5035_OTP_REG_PER_GROUP; j++) {
+ struct gc5035_reg *reg;
+
+ if (!(regs[base_group] &
+ BIT((GC5035_OTP_PER_PAGE_SIZE * j +
+ GC5035_OTP_PAGE_FLAG_OFFSET))))
+ continue;
+
+ reg = &otp_regs->regs[otp_regs->num_regs++];
+ reg->page = (regs[base_group] >>
+ (GC5035_OTP_PER_PAGE_SIZE * j)) &
+ GC5035_OTP_REG_PAGE_MASK;
+ reg->regval.addr = regs[base_group + j *
+ GC5035_OTP_REG_BYTE_PER_REG +
+ GC5035_OTP_REG_ADDR_OFFSET];
+ reg->regval.val = regs[base_group + j *
+ GC5035_OTP_REG_BYTE_PER_REG +
+ GC5035_OTP_REG_VAL_OFFSET];
+ }
+ }
+
+ return 0;
+}
+
+static int gc5035_read_dpc(struct gc5035 *gc5035)
+{
+ struct device *dev = &gc5035->client->dev;
+ struct gc5035_dpc *dpc = &gc5035->dpc;
+ u8 dpc_flag = 0;
+ u8 error_number = 0;
+ u8 total_number = 0;
+ int ret;
+
+ ret = gc5035_otp_read_data(gc5035, GC5035_OTP_DPC_FLAG_OFFSET,
+ &dpc_flag, 1);
+ if (ret) {
+ dev_err(dev, "failed to read dpc flag\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "dpc flag = 0x%x\n", dpc_flag);
+
+ dpc->valid = false;
+
+ switch (dpc_flag & GC5035_OTP_DPC_FLAG_MASK) {
+ case GC5035_OTP_FLAG_EMPTY:
+ dev_dbg(dev, "dpc info is empty!!\n");
+ break;
+
+ case GC5035_OTP_FLAG_VALID:
+ dev_dbg(dev, "dpc info is valid!\n");
+ ret = gc5035_otp_read_data(gc5035,
+ GC5035_OTP_DPC_TOTAL_NUMBER_OFFSET,
+ &total_number, 1);
+ if (ret) {
+ dev_err(dev, "failed to read dpc total number\n");
+ return ret;
+ }
+
+ ret = gc5035_otp_read_data(gc5035,
+ GC5035_OTP_DPC_ERROR_NUMBER_OFFSET,
+ &error_number, 1);
+ if (ret) {
+ dev_err(dev, "failed to read dpc error number\n");
+ return ret;
+ }
+
+ dpc->total_num = total_number + error_number;
+ dpc->valid = true;
+ dev_dbg(dev, "total_num = %d\n", dpc->total_num);
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int gc5035_otp_read_sensor_info(struct gc5035 *gc5035)
+{
+ int ret;
+
+ ret = gc5035_read_dpc(gc5035);
+ if (ret)
+ return ret;
+
+ return gc5035_read_otp_regs(gc5035);
+}
+
+static int gc5035_check_dd_load_status(struct gc5035 *gc5035)
+{
+ u8 status;
+ int ret;
+
+ ret = gc5035_read_reg(gc5035, GC5035_REG_DD_LOAD_STATUS, &status);
+ if (ret)
+ return ret;
+
+ if (status & GC5035_OTP_BIT_LOAD)
+ return status;
+ else
+ return 0;
+}
+
+static int gc5035_otp_update_dd(struct gc5035 *gc5035)
+{
+ struct device *dev = &gc5035->client->dev;
+ struct gc5035_dpc *dpc = &gc5035->dpc;
+ int val, ret;
+
+ if (!dpc->valid) {
+ dev_dbg(dev, "DPC table invalid, not updating DD.\n");
+ return 0;
+ }
+
+ dev_dbg(dev, "DD auto load start\n");
+
+ ret = gc5035_write_array(gc5035, gc5035_dd_auto_load_regs,
+ ARRAY_SIZE(gc5035_dd_auto_load_regs));
+ if (ret) {
+ dev_err(dev, "failed to write dd auto load reg\n");
+ return ret;
+ }
+
+ ret = gc5035_write_reg(gc5035, GC5035_REG_DD_TOTALNUM_H,
+ (dpc->total_num >> 8) &
+ GC5035_DD_TOTALNUM_H_MASK);
+ if (ret)
+ return ret;
+
+ ret = gc5035_write_reg(gc5035, GC5035_REG_DD_TOTALNUM_L,
+ dpc->total_num & 0xff);
+ if (ret)
+ return ret;
+
+ ret = gc5035_write_array(gc5035, gc5035_otp_dd_regs,
+ ARRAY_SIZE(gc5035_otp_dd_regs));
+ if (ret)
+ return ret;
+
+ /* Wait for DD to finish loading automatically */
+ ret = readx_poll_timeout(gc5035_check_dd_load_status, gc5035,
+ val, val <= 0, GC5035_DD_DELAY_US,
+ GC5035_DD_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(dev, "DD load timeout\n");
+ return -EFAULT;
+ }
+ if (val < 0) {
+ dev_err(dev, "DD load failure\n");
+ return val;
+ }
+
+ ret = gc5035_write_array(gc5035, gc5035_otp_dd_enable_regs,
+ ARRAY_SIZE(gc5035_otp_dd_enable_regs));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int gc5035_otp_update_regs(struct gc5035 *gc5035)
+{
+ struct device *dev = &gc5035->client->dev;
+ struct gc5035_otp_regs *otp_regs = &gc5035->otp_regs;
+ unsigned int i;
+ int ret;
+
+ dev_dbg(dev, "reg count = %d\n", otp_regs->num_regs);
+
+ for (i = 0; i < otp_regs->num_regs; i++) {
+ ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG,
+ otp_regs->regs[i].page);
+ if (ret)
+ return ret;
+
+ ret = gc5035_write_reg(gc5035,
+ otp_regs->regs[i].regval.addr,
+ otp_regs->regs[i].regval.val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gc5035_otp_update(struct gc5035 *gc5035)
+{
+ struct device *dev = &gc5035->client->dev;
+ int ret;
+
+ ret = gc5035_otp_update_dd(gc5035);
+ if (ret) {
+ dev_err(dev, "failed to update otp dd\n");
+ return ret;
+ }
+
+ ret = gc5035_otp_update_regs(gc5035);
+ if (ret) {
+ dev_err(dev, "failed to update otp regs\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int gc5035_set_otp_config(struct gc5035 *gc5035)
+{
+ struct device *dev = &gc5035->client->dev;
+ u8 otp_id[GC5035_OTP_ID_SIZE];
+ int ret;
+
+ ret = gc5035_write_array(gc5035, gc5035_otp_init_regs,
+ ARRAY_SIZE(gc5035_otp_init_regs));
+ if (ret) {
+ dev_err(dev, "failed to write otp init reg\n");
+ return ret;
+ }
+
+ ret = gc5035_otp_read_data(gc5035, GC5035_OTP_ID_DATA_OFFSET,
+ &otp_id[0], GC5035_OTP_ID_SIZE);
+ if (ret) {
+ dev_err(dev, "failed to read otp id\n");
+ goto out_otp_exit;
+ }
+
+ if (!gc5035->otp_read || memcmp(gc5035->otp_id, otp_id, sizeof(otp_id))) {
+ dev_dbg(dev, "reading OTP configuration\n");
+
+ memset(&gc5035->otp_regs, 0, sizeof(gc5035->otp_regs));
+ memset(&gc5035->dpc, 0, sizeof(gc5035->dpc));
+
+ memcpy(gc5035->otp_id, otp_id, sizeof(gc5035->otp_id));
+
+ ret = gc5035_otp_read_sensor_info(gc5035);
+ if (ret < 0) {
+ dev_err(dev, "failed to read otp info\n");
+ goto out_otp_exit;
+ }
+
+ gc5035->otp_read = true;
+ }
+
+ ret = gc5035_otp_update(gc5035);
+ if (ret < 0)
+ return ret;
+
+out_otp_exit:
+ ret = gc5035_write_array(gc5035, gc5035_otp_exit_regs,
+ ARRAY_SIZE(gc5035_otp_exit_regs));
+ if (ret) {
+ dev_err(dev, "failed to write otp exit reg\n");
+ return ret;
+ }
+
+ return ret;
+}
+
static int gc5035_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
@@ -859,6 +1333,10 @@ static int __gc5035_start_stream(struct gc5035 *gc5035)
if (ret)
return ret;

+ ret = gc5035_set_otp_config(gc5035);
+ if (ret)
+ return ret;
+
ret = gc5035_write_array(gc5035, gc5035->cur_mode->reg_list,
gc5035->cur_mode->num_regs);
if (ret)
--
2.28.0.402.g5ffc5be6b7-goog