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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Tomasz,

Thanks for the patch.

On Wed, Sep 02, 2020 at 10:48:13PM +0000, Tomasz Figa wrote:
> 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);

Is this read needed every time when streaming is about to start?

I guess it's not wrong but it seems unnecessary on subsequent times.

> +	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)

-- 
Kind regards,

Sakari Ailus



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux