// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2020 Rockchip Electronics Co., Ltd. /* * otp_eeprom driver * * V0.0X01.0X01 * 1. fix table_size. * 2. fix ioctl return value. * 3. add version control. * V0.0X01.0X02 * 1. fix otp info null issue. * V0.0X01.0X03 * 1. add buf read optimize otp read speed. * 2. add mutex for otp read. */ //#define DEBUG #include #include #include #include #include #include #include #include #include #include #include #include "otp_eeprom.h" #define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x03) #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 *)®_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; } /* Read registers buffers at a time */ static int read_reg_otp_buf(struct i2c_client *client, u16 reg, unsigned int len, u8 *buf) { struct i2c_msg msgs[2]; __be16 reg_addr_be = cpu_to_be16(reg); int ret; /* Write register address */ msgs[0].addr = client->addr; msgs[0].flags = 0; msgs[0].len = 2; msgs[0].buf = (u8 *)®_addr_be; /* Read data from register */ msgs[1].addr = client->addr; msgs[1].flags = I2C_M_RD; msgs[1].len = len; msgs[1].buf = buf; ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret != ARRAY_SIZE(msgs)) return -EIO; return 0; } static u8 get_vendor_flag(struct i2c_client *client) { u8 vendor_flag = 0; u8 vendor[9]; int i = 0; u32 temp = 0; for (i = 0; i < 8; i++) { read_reg_otp(client, INFO_FLAG_REG + i, 1, &temp); vendor[i] = (u8)temp; } vendor[8] = 0; if (strcmp(vendor, "ROCKCHIP") == 0) vendor_flag |= 0x40; else 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; dev_info(dev, "get otp successful\n"); } else { eeprom_dev->otp = NULL; kfree(otp_ptr); dev_warn(&client->dev, "otp is NULL!\n"); } return 0; err: eeprom_dev->otp = NULL; kfree(otp_ptr); dev_warn(&client->dev, "@%s read otp err!\n", __func__); return -EINVAL; } static void rkotp_read_module_info(struct eeprom_device *eeprom_dev, struct otp_info *otp_ptr, u32 base_addr) { struct i2c_client *client = eeprom_dev->client; struct device *dev = &eeprom_dev->client->dev; int i = 0; u32 temp = 0; u32 checksum = 0; int ret = 0; ret |= read_reg_otp(client, base_addr, 4, &otp_ptr->basic_data.module_size); checksum += otp_ptr->basic_data.module_size; base_addr += 4; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->basic_data.version); checksum += otp_ptr->basic_data.version; base_addr += 2; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->basic_data.id.supplier_id); checksum += otp_ptr->basic_data.id.supplier_id; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->basic_data.id.year); checksum += otp_ptr->basic_data.id.year; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->basic_data.id.month); checksum += otp_ptr->basic_data.id.month; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->basic_data.id.day); checksum += otp_ptr->basic_data.id.day; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->basic_data.id.sensor_id); checksum += otp_ptr->basic_data.id.sensor_id; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->basic_data.id.lens_id); checksum += otp_ptr->basic_data.id.lens_id; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->basic_data.id.vcm_id); checksum += otp_ptr->basic_data.id.vcm_id; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->basic_data.id.driver_ic_id); checksum += otp_ptr->basic_data.id.driver_ic_id; base_addr += 1; for (i = 0; i < RKMOUDLE_ID_SIZE; i++) { ret |= read_reg_otp(client, base_addr, 1, &temp); otp_ptr->basic_data.modul_id[i] = temp; checksum += temp; base_addr += 1; } ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->basic_data.mirror_flip); checksum += otp_ptr->basic_data.mirror_flip; base_addr += 1; ret |= read_reg_otp(client, base_addr, 2, &temp); checksum += temp; otp_ptr->basic_data.size.width = temp; base_addr += 2; ret |= read_reg_otp(client, base_addr, 2, &temp); checksum += temp; otp_ptr->basic_data.size.height = temp; base_addr += 2; for (i = 0; i < RK_INFO_RESERVED_SIZE; i++) { ret |= read_reg_otp(client, base_addr, 1, &temp); checksum += temp; base_addr += 1; } ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->basic_data.checksum); if ((checksum % 255 + 1) == otp_ptr->basic_data.checksum && (!ret)) { otp_ptr->basic_data.flag = 0x01; otp_ptr->flag++; dev_info(dev, "fasic info: supplier_id(0x%x) lens(0x%x) time(%d_%d_%d) module id %x\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, (u32)(*otp_ptr->basic_data.modul_id)); } else { otp_ptr->basic_data.flag = 0; dev_info(dev, "fasic info: checksum err, checksum %d, reg_checksum %d\n", (int)(checksum % 255 + 1), (int)otp_ptr->basic_data.checksum); dev_info(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); dev_info(dev, "fasic info: full size, width(%d) height(%d) flip(0x%x)\n", otp_ptr->basic_data.size.width, otp_ptr->basic_data.size.height, otp_ptr->basic_data.mirror_flip); } } static void rkotp_read_awb(struct eeprom_device *eeprom_dev, struct otp_info *otp_ptr, u32 base_addr) { struct i2c_client *client = eeprom_dev->client; struct device *dev = &eeprom_dev->client->dev; u32 checksum = 0; u32 temp = 0; int i = 0; int ret = 0; ret = read_reg_otp(client, base_addr, 4, &otp_ptr->awb_data.size); checksum += otp_ptr->awb_data.size; base_addr += 4; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->awb_data.version); checksum += otp_ptr->awb_data.version; base_addr += 2; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->awb_data.r_ratio); checksum += otp_ptr->awb_data.r_ratio; base_addr += 2; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->awb_data.b_ratio); checksum += otp_ptr->awb_data.b_ratio; base_addr += 2; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->awb_data.g_ratio); checksum += otp_ptr->awb_data.g_ratio; base_addr += 2; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->awb_data.r_golden); checksum += otp_ptr->awb_data.r_golden; base_addr += 2; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->awb_data.b_golden); checksum += otp_ptr->awb_data.b_golden; base_addr += 2; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->awb_data.g_golden); checksum += otp_ptr->awb_data.g_golden; base_addr += 2; for (i = 0; i < RK_AWB_RESERVED_SIZE; i++) { ret |= read_reg_otp(client, base_addr, 1, &temp); checksum += temp; base_addr += 1; } ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->awb_data.checksum); if ((checksum % 255 + 1) == otp_ptr->awb_data.checksum && (!ret)) { otp_ptr->awb_data.flag = 0x01; otp_ptr->flag++; dev_info(dev, "awb version:0x%x\n", otp_ptr->awb_data.version); dev_info(dev, "awb cur:(r 0x%x, b 0x%x, g 0x%x)\n", otp_ptr->awb_data.r_ratio, otp_ptr->awb_data.b_ratio, otp_ptr->awb_data.g_ratio); dev_info(dev, "awb gol:(r 0x%x, b 0x%x, g 0x%x),\n", otp_ptr->awb_data.r_golden, otp_ptr->awb_data.b_golden, otp_ptr->awb_data.g_golden); } else { otp_ptr->awb_data.flag = 0; dev_info(dev, "awb info: checksum err, checksum %d, reg_checksum %d\n", (int) (checksum % 255 + 1), (int) otp_ptr->awb_data.checksum); } } static void rkotp_read_lsc(struct eeprom_device *eeprom_dev, struct otp_info *otp_ptr, u32 base_addr) { struct i2c_client *client = eeprom_dev->client; struct device *dev = &eeprom_dev->client->dev; u32 checksum = 0; u8 *lsc_buf; int i = 0; int ret = 0; #ifdef DEBUG int w, h, j; #endif lsc_buf = kzalloc(LSC_DATA_SIZE, GFP_KERNEL); if (!lsc_buf) { dev_err(dev, "%s ENOMEM!\n", __func__); return; } ret = read_reg_otp(client, base_addr, 4, &otp_ptr->lsc_data.size); checksum += otp_ptr->lsc_data.size; base_addr += 4; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->lsc_data.version); checksum += otp_ptr->lsc_data.version; base_addr += 2; ret |= read_reg_otp_buf(client, base_addr, LSC_DATA_SIZE, lsc_buf); base_addr += LSC_DATA_SIZE; for (i = 0; i < LSC_DATA_SIZE; i++) { otp_ptr->lsc_data.data[i] = lsc_buf[i]; checksum += lsc_buf[i]; } otp_ptr->lsc_data.table_size = LSC_DATA_SIZE; #ifdef DEBUG w = 17 * 2; h = 17 * 4; dev_info(dev, "show lsc table\n"); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) dev_info(dev, "%d ", otp_ptr->lsc_data.data[i * w + j]); if (i < h) dev_info(dev, "\n"); } #endif memset(lsc_buf, 0, LSC_DATA_SIZE); ret |= read_reg_otp_buf(client, base_addr, RK_LSC_RESERVED_SIZE, lsc_buf); for (i = 0; i < RK_LSC_RESERVED_SIZE; i++) { checksum += lsc_buf[i]; } base_addr += RK_LSC_RESERVED_SIZE; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->lsc_data.checksum); if ((checksum % 255 + 1) == otp_ptr->lsc_data.checksum && (!ret)) { otp_ptr->lsc_data.flag = 0x01; otp_ptr->flag++; dev_info(dev, "lsc info:(version 0x%x, checksum 0x%x)\n", otp_ptr->lsc_data.version, (int)otp_ptr->lsc_data.checksum); } else { otp_ptr->lsc_data.flag = 0x00; dev_info(dev, "lsc info: checksum err, checksum %d, reg_checksum %d\n", (int)(checksum % 255 + 1), (int)otp_ptr->lsc_data.checksum); } kfree(lsc_buf); } static void rkotp_read_pdaf(struct eeprom_device *eeprom_dev, struct otp_info *otp_ptr, u32 base_addr) { struct i2c_client *client = eeprom_dev->client; struct device *dev = &eeprom_dev->client->dev; u32 checksum = 0; u8 *pdaf_buf; int i = 0; int ret = 0; #ifdef DEBUG int w, h, j; #endif pdaf_buf = kzalloc(RK_GAINMAP_SIZE, GFP_KERNEL); if (!pdaf_buf) { dev_err(dev, "%s ENOMEM!\n", __func__); return; } ret = read_reg_otp(client, base_addr, 4, &otp_ptr->pdaf_data.size); checksum += otp_ptr->pdaf_data.size; base_addr += 4; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->pdaf_data.version); checksum += otp_ptr->pdaf_data.version; base_addr += 2; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->pdaf_data.gainmap_width); checksum += otp_ptr->pdaf_data.gainmap_width; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->pdaf_data.gainmap_height); checksum += otp_ptr->pdaf_data.gainmap_height; base_addr += 1; ret |= read_reg_otp_buf(client, base_addr, RK_GAINMAP_SIZE, pdaf_buf); base_addr += RK_GAINMAP_SIZE; for (i = 0; i < RK_GAINMAP_SIZE; i++) { otp_ptr->pdaf_data.gainmap[i] = pdaf_buf[i]; checksum += otp_ptr->pdaf_data.gainmap[i]; } #ifdef DEBUG w = 64; h = 32; dev_info(dev, "show pdaf gainmap table\n"); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) dev_info(dev, "%d ", otp_ptr->pdaf_data.gainmap[i * w + j]); if (i < h) dev_info(dev, "\n"); } #endif ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->pdaf_data.gainmap_checksum); checksum += otp_ptr->pdaf_data.gainmap_checksum; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->pdaf_data.dcc_mode); checksum += otp_ptr->pdaf_data.dcc_mode; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->pdaf_data.dcc_dir); checksum += otp_ptr->pdaf_data.dcc_dir; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->pdaf_data.dccmap_width); checksum += otp_ptr->pdaf_data.dccmap_width; base_addr += 1; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->pdaf_data.dccmap_height); checksum += otp_ptr->pdaf_data.dccmap_height; base_addr += 1; memset(pdaf_buf, 0, RK_DCCMAP_SIZE); ret |= read_reg_otp_buf(client, base_addr, RK_DCCMAP_SIZE, pdaf_buf); for (i = 0; i < RK_DCCMAP_SIZE; i++) { otp_ptr->pdaf_data.dccmap[i] = pdaf_buf[i]; checksum += otp_ptr->pdaf_data.dccmap[i]; } base_addr += RK_DCCMAP_SIZE; #ifdef DEBUG w = 32; h = 16; dev_info(dev, "show pdaf dccmap table\n"); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) dev_info(dev, "%d ", otp_ptr->pdaf_data.dccmap[i * w + j]); if (i < h) dev_info(dev, "\n"); } #endif ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->pdaf_data.dccmap_checksum); checksum += otp_ptr->pdaf_data.dccmap_checksum; base_addr += 1; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->pdaf_data.pd_offset); checksum += otp_ptr->pdaf_data.pd_offset; base_addr += 2; memset(pdaf_buf, 0, RK_PDAF_RESERVED_SIZE); ret |= read_reg_otp_buf(client, base_addr, RK_PDAF_RESERVED_SIZE, pdaf_buf); for (i = 0; i < RK_PDAF_RESERVED_SIZE; i++) { checksum += pdaf_buf[i]; } base_addr += RK_PDAF_RESERVED_SIZE; ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->pdaf_data.checksum); if ((checksum % 255 + 1) == otp_ptr->pdaf_data.checksum && (!ret)) { otp_ptr->pdaf_data.flag = 0x01; otp_ptr->flag++; dev_info(dev, "pdaf info:(version 0x%x, checksum 0x%x)\n", otp_ptr->pdaf_data.version, (int)otp_ptr->pdaf_data.checksum); } else { otp_ptr->pdaf_data.flag = 0x00; dev_info(dev, "pdaf info: checksum err, checksum %d, reg_checksum %d\n", (int)(checksum % 255 + 1), (int)otp_ptr->pdaf_data.checksum); } kfree(pdaf_buf); } static void rkotp_read_af(struct eeprom_device *eeprom_dev, struct otp_info *otp_ptr, u32 base_addr) { struct i2c_client *client = eeprom_dev->client; struct device *dev = &eeprom_dev->client->dev; u32 checksum = 0; u32 temp = 0; int i = 0; int ret = 0; ret = read_reg_otp(client, base_addr, 4, &otp_ptr->af_data.size); checksum += otp_ptr->af_data.size; base_addr += 4; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->af_data.version); checksum += otp_ptr->af_data.version; base_addr += 2; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->af_data.af_inf); checksum += otp_ptr->af_data.af_inf; base_addr += 2; ret |= read_reg_otp(client, base_addr, 2, &otp_ptr->af_data.af_macro); checksum += otp_ptr->af_data.af_macro; base_addr += 2; for (i = 0; i < RK_AF_RESERVED_SIZE; i++) { ret |= read_reg_otp(client, base_addr, 1, &temp); checksum += temp; base_addr += 1; } ret |= read_reg_otp(client, base_addr, 1, &otp_ptr->af_data.checksum); if ((checksum % 255 + 1) == otp_ptr->af_data.checksum && (!ret)) { otp_ptr->af_data.flag = 0x01; otp_ptr->flag++; dev_info(dev, "af info:(version 0x%x, checksum 0x%x)\n", otp_ptr->af_data.version, (int)otp_ptr->af_data.checksum); } else { otp_ptr->af_data.flag = 0x00; dev_info(dev, "af info: checksum err, checksum %d, reg_checksum %d\n", (int)(checksum % 255 + 1), (int)otp_ptr->af_data.checksum); } } static int rkotp_read_data(struct eeprom_device *eeprom_dev) { struct i2c_client *client = eeprom_dev->client; struct otp_info *otp_ptr; struct device *dev = &eeprom_dev->client->dev; u32 id = 0; u32 base_addr = 0; int i = 0; int ret = 0; otp_ptr = kzalloc(sizeof(*otp_ptr), GFP_KERNEL); if (!otp_ptr) return -ENOMEM; base_addr = RKOTP_REG_START; otp_ptr->flag = 0; for (i = 0; i < RKOTP_MAX_MODULE; i++) { read_reg_otp(client, base_addr, 1, &id); dev_info(dev, "show block id %d, addr 0x%x\n", id, base_addr); switch (id) { case RKOTP_INFO_ID: rkotp_read_module_info(eeprom_dev, otp_ptr, base_addr + 1); base_addr += 0x28;//v1 0x30 v2 0x28; break; case RKOTP_AWB_ID: rkotp_read_awb(eeprom_dev, otp_ptr, base_addr + 1); base_addr += 0x30; break; case RKOTP_LSC_ID: rkotp_read_lsc(eeprom_dev, otp_ptr, base_addr + 1); base_addr += 0x930; break; case RKOTP_PDAF_ID: rkotp_read_pdaf(eeprom_dev, otp_ptr, base_addr + 1); base_addr += 0xA30; break; case RKOTP_AF_ID: rkotp_read_af(eeprom_dev, otp_ptr, base_addr + 1); base_addr += 0x20; break; default: id = -1; break; } if (id == -1) break; } if (otp_ptr->flag) { eeprom_dev->otp = otp_ptr; dev_info(dev, "rkotp read successful!\n"); } else { eeprom_dev->otp = NULL; kfree(otp_ptr); dev_warn(&client->dev, "otp is NULL!\n"); ret = -1; } return ret; } static int otp_read(struct eeprom_device *eeprom_dev) { u8 vendor_flag = 0; struct i2c_client *client = eeprom_dev->client; mutex_lock(&eeprom_dev->mutex); vendor_flag = get_vendor_flag(client); if (vendor_flag == 0x80) otp_read_data(eeprom_dev); else if (vendor_flag == 0x40) rkotp_read_data(eeprom_dev); else { dev_warn(&client->dev, "no vendor flag infos!\n"); mutex_unlock(&eeprom_dev->mutex); return -1; } mutex_unlock(&eeprom_dev->mutex); return 0; } static long eeprom_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct eeprom_device *eeprom_dev = sd_to_eeprom(sd); long ret = 0; if (!eeprom_dev->otp) { if (otp_read(eeprom_dev)) ret = -EFAULT; } if (arg && eeprom_dev->otp) memcpy(arg, eeprom_dev->otp, sizeof(struct otp_info)); else ret = -EFAULT; return ret; } #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; u32 gainmap_w, gainmap_h; u32 dccmap_w, dccmap_h; if (dev && dev->otp) { seq_puts(p, "[Header]\n"); seq_puts(p, "version=1.0;\n\n"); if (dev->otp->awb_data.flag) { 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"); } if (dev->otp->lsc_data.flag) { 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"); } if (dev->otp->pdaf_data.flag) { gainmap_w = dev->otp->pdaf_data.gainmap_width; gainmap_h = dev->otp->pdaf_data.gainmap_height; dccmap_w = dev->otp->pdaf_data.dccmap_width; dccmap_h = dev->otp->pdaf_data.dccmap_height; seq_printf(p, "[RKPDAFOTPParam]\n"); seq_printf(p, "flag=%d;\n", dev->otp->pdaf_data.flag); seq_printf(p, "gainmap_width=%d;\n", gainmap_w); seq_printf(p, "gainmap_height=%d;\n", gainmap_h); seq_printf(p, "pd_offset=%d\n", dev->otp->pdaf_data.pd_offset); seq_printf(p, "gainmap_table=\n"); for (i = 0; i < gainmap_h; i++) { for (j = 0; j < gainmap_w; j++) { seq_printf(p, "%d", (dev->otp->pdaf_data.gainmap[(i * gainmap_w + j) * 2] << 8) | dev->otp->pdaf_data.gainmap[(i * gainmap_w + j) * 2 + 1]); if (j < gainmap_w) seq_printf(p, " "); } if (i < gainmap_h) seq_printf(p, "\n"); } seq_printf(p, "\n"); seq_printf(p, "dcc_mode=%d\n", dev->otp->pdaf_data.dcc_mode); seq_printf(p, "dcc_dir=%d\n", dev->otp->pdaf_data.dcc_dir); seq_printf(p, "dccmap_width=%d\n", dev->otp->pdaf_data.dccmap_width); seq_printf(p, "dccmap_height=%d\n", dev->otp->pdaf_data.dccmap_height); for (i = 0; i < dccmap_h; i++) { for (j = 0; j < dccmap_w; j++) { seq_printf(p, "%d", (dev->otp->pdaf_data.dccmap[(i * dccmap_w + j) * 2] << 8) | dev->otp->pdaf_data.dccmap[(i * dccmap_w + j) * 2 + 1]); if (j < dccmap_w) seq_printf(p, " "); } if (i < dccmap_h) seq_printf(p, "\n"); } seq_printf(p, "\n"); } if (dev->otp->af_data.flag) { seq_printf(p, "[RKAFOTPParam]\n"); seq_printf(p, "flag=%d;\n", dev->otp->af_data.flag); seq_printf(p, "af_inf=%d;\n", dev->otp->af_data.af_inf); seq_printf(p, "af_macro=%d;\n", dev->otp->af_data.af_macro); } } else { seq_puts(p, "otp is null!\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, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16, (DRIVER_VERSION & 0xff00) >> 8, DRIVER_VERSION & 0x00ff); eeprom_dev = devm_kzalloc(&client->dev, sizeof(*eeprom_dev), GFP_KERNEL); if (eeprom_dev == NULL) { dev_err(&client->dev, "Probe failed\n"); return -ENOMEM; } mutex_init(&eeprom_dev->mutex); v4l2_i2c_subdev_init(&eeprom_dev->sd, client, &eeprom_ops); eeprom_dev->client = client; snprintf(eeprom_dev->name, sizeof(eeprom_dev->name), "%s-%d-%02x", DEVICE_NAME, i2c_adapter_id(client->adapter), client->addr); 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); mutex_destroy(&eeprom_dev->mutex); 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");