From 2a6adfdae497ac58cdaaa2b3890f4711d82de84f Mon Sep 17 00:00:00 2001 From: Wang Panzhenzhuan Date: Mon, 7 Apr 2025 20:03:07 +0800 Subject: [PATCH] media: i2c: ov16880: add read otp support Signed-off-by: Wang Panzhenzhuan Change-Id: Ib62e11283c4a1a59609d3cbe2c2fab1ae7ad8b2a --- drivers/media/i2c/ov16880.c | 133 +++++++++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov16880.c b/drivers/media/i2c/ov16880.c index 280405eecacb..23e7a1a28a46 100644 --- a/drivers/media/i2c/ov16880.c +++ b/drivers/media/i2c/ov16880.c @@ -5,6 +5,7 @@ * Copyright (C) 2024 Rockchip Electronics Co., Ltd. * * V0.0X01.0X00 first version. + * V0.0X01.0X01 support rk otp spec. * */ //#define DEBUG @@ -26,8 +27,9 @@ #include #include #include +#include "otp_eeprom.h" -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) #define OV16880_MAJOR_I2C_ADDR 0x36 #define OV16880_MINOR_I2C_ADDR 0x10 @@ -112,6 +114,7 @@ #define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" #define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" #define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" +#define RK_OTP #define OV16880_NAME "ov16880" #define OV16880_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR10_1X10 @@ -177,6 +180,9 @@ struct ov16880 { const char *module_facing; const char *module_name; const char *len_name; +#ifdef RK_OTP + struct otp_info *otp; +#endif }; #define to_ov16880(sd) container_of(sd, struct ov16880, subdev) @@ -1926,14 +1932,103 @@ static int ov16880_g_frame_interval(struct v4l2_subdev *sd, return 0; } +#ifdef RK_OTP +static void ov16880_get_otp(struct otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i, j; + u32 w, h; + + /* awb */ + if (otp->awb_data.flag) { + inf->awb.flag = 1; + inf->awb.r_value = otp->awb_data.r_ratio; + inf->awb.b_value = otp->awb_data.b_ratio; + inf->awb.gr_value = otp->awb_data.g_ratio; + inf->awb.gb_value = 0x0; + + inf->awb.golden_r_value = otp->awb_data.r_golden; + inf->awb.golden_b_value = otp->awb_data.b_golden; + inf->awb.golden_gr_value = otp->awb_data.g_golden; + inf->awb.golden_gb_value = 0x0; + } + + /* lsc */ + if (otp->lsc_data.flag) { + inf->lsc.flag = 1; + inf->lsc.width = otp->basic_data.size.width; + inf->lsc.height = otp->basic_data.size.height; + inf->lsc.table_size = otp->lsc_data.table_size; + + for (i = 0; i < 289; i++) { + inf->lsc.lsc_r[i] = (otp->lsc_data.data[i * 2] << 8) | + otp->lsc_data.data[i * 2 + 1]; + inf->lsc.lsc_gr[i] = (otp->lsc_data.data[i * 2 + 578] << 8) | + otp->lsc_data.data[i * 2 + 579]; + inf->lsc.lsc_gb[i] = (otp->lsc_data.data[i * 2 + 1156] << 8) | + otp->lsc_data.data[i * 2 + 1157]; + inf->lsc.lsc_b[i] = (otp->lsc_data.data[i * 2 + 1734] << 8) | + otp->lsc_data.data[i * 2 + 1735]; + } + } + + /* pdaf */ + if (otp->pdaf_data.flag) { + inf->pdaf.flag = 1; + inf->pdaf.gainmap_width = otp->pdaf_data.gainmap_width; + inf->pdaf.gainmap_height = otp->pdaf_data.gainmap_height; + inf->pdaf.dcc_mode = otp->pdaf_data.dcc_mode; + inf->pdaf.dcc_dir = otp->pdaf_data.dcc_dir; + inf->pdaf.dccmap_width = otp->pdaf_data.dccmap_width; + inf->pdaf.dccmap_height = otp->pdaf_data.dccmap_height; + w = otp->pdaf_data.gainmap_width; + h = otp->pdaf_data.gainmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.gainmap[i * w + j] = + (otp->pdaf_data.gainmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.gainmap[(i * w + j) * 2 + 1]; + } + } + w = otp->pdaf_data.dccmap_width; + h = otp->pdaf_data.dccmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.dccmap[i * w + j] = + (otp->pdaf_data.dccmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.dccmap[(i * w + j) * 2 + 1]; + } + } + } + + /* af */ + if (otp->af_data.flag) { + inf->af.flag = 1; + inf->af.dir_cnt = 1; + inf->af.af_otp[0].vcm_start = otp->af_data.af_inf; + inf->af.af_otp[0].vcm_end = otp->af_data.af_macro; + inf->af.af_otp[0].vcm_dir = 0; + } + +} +#endif + static void ov16880_get_module_inf(struct ov16880 *ov16880, struct rkmodule_inf *inf) { +#ifdef RK_OTP + struct otp_info *otp = ov16880->otp; +#endif + memset(inf, 0, sizeof(*inf)); strscpy(inf->base.sensor, OV16880_NAME, sizeof(inf->base.sensor)); strscpy(inf->base.module, ov16880->module_name, sizeof(inf->base.module)); strscpy(inf->base.lens, ov16880->len_name, sizeof(inf->base.lens)); +#ifdef RK_OTP + if (otp) + ov16880_get_otp(otp, inf); +#endif } static long ov16880_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) @@ -2672,6 +2767,12 @@ static int ov16880_probe(struct i2c_client *client, char facing[2]; int ret; u32 i, hdr_mode = 0; +#ifdef RK_OTP + struct device_node *eeprom_ctrl_node; + struct i2c_client *eeprom_ctrl_client; + struct v4l2_subdev *eeprom_ctrl; + struct otp_info *otp_ptr; +#endif dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16, @@ -2765,6 +2866,36 @@ static int ov16880_probe(struct i2c_client *client, ret = ov16880_check_sensor_id(ov16880, client); if (ret) goto err_power_off; +#ifdef RK_OTP + eeprom_ctrl_node = of_parse_phandle(node, "eeprom-ctrl", 0); + if (eeprom_ctrl_node) { + eeprom_ctrl_client = + of_find_i2c_device_by_node(eeprom_ctrl_node); + of_node_put(eeprom_ctrl_node); + if (IS_ERR_OR_NULL(eeprom_ctrl_client)) { + dev_err(dev, "can not get node\n"); + goto continue_probe; + } + eeprom_ctrl = i2c_get_clientdata(eeprom_ctrl_client); + if (IS_ERR_OR_NULL(eeprom_ctrl)) { + dev_err(dev, "can not get eeprom i2c client\n"); + } else { + otp_ptr = devm_kzalloc(dev, sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + ret = v4l2_subdev_call(eeprom_ctrl, + core, ioctl, 0, otp_ptr); + if (!ret) { + ov16880->otp = otp_ptr; + } else { + ov16880->otp = NULL; + devm_kfree(dev, otp_ptr); + dev_warn(dev, "can not get otp info, skip!\n"); + } + } + } +continue_probe: +#endif #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov16880_internal_ops;