Cant get eeprom data from MIPI CSI Camera using V4L2 ctl or Argus API

Hi all,

We’re using a custom GMSL2 - MIPI CSI 2 Camera, we’ve implemented the driver to get the stream, we are able to get the stream using LibArgus in NV12, and also we’re able to get the eeprom data (driver for at24 also implemented) using the following command :

sudo hexdump -C /sys/bus/i2c/devices/30-0050/eeprom 

But when i use

v4l2-ctl --all -d /dev/video0

I get the output below with eeprom data at 0000…

Driver Info (not using libv4l2):
	Driver name   : tegra-video
	Card type     : vi-output, ar0820 30-0010
	Bus info      : platform:15c10000.vi:0
	Driver version: 4.9.140
	Capabilities  : 0x84200001
		Video Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps   : 0x04200001
		Video Capture
		Streaming
		Extended Pix Format
Priority: 2
Video input : 0 (Camera 0: ok)
Format Video Capture:
	Width/Height      : 3848/2168
	Pixel Format      : 'BA12'
	Field             : None
	Bytes per Line    : 7696
	Size Image        : 16684928
	Colorspace        : sRGB
	Transfer Function : Default (maps to sRGB)
	YCbCr/HSV Encoding: Default (maps to ITU-R 601)
	Quantization      : Default (maps to Full Range)
	Flags             : 

Camera Controls

                     group_hold 0x009a2003 (bool)   : default=0 value=0 flags=execute-on-write
                     hdr_enable 0x009a2004 (intmenu): min=0 max=1 default=0 value=0
                    eeprom_data 0x009a2005 (str)    : min=0 max=2048 step=2 value='00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' flags=read-only, has-payload
                    sensor_mode 0x009a2008 (int64)  : min=0 max=0 step=0 default=0 value=0 flags=slider
                           gain 0x009a2009 (int64)  : min=0 max=0 step=0 default=0 value=1 flags=slider
                       exposure 0x009a200a (int64)  : min=0 max=0 step=0 default=0 value=4388 flags=slider
                     frame_rate 0x009a200b (int64)  : min=0 max=0 step=0 default=0 value=30000000 flags=slider
                 exposure_short 0x009a200c (int64)  : min=0 max=0 step=0 default=0 value=15 flags=slider
           sensor_configuration 0x009a2032 (u32)    : min=0 max=0 step=0 default=0 flags=read-only, volatile, has-payload
         sensor_mode_i2c_packet 0x009a2033 (u32)    : min=0 max=0 step=0 default=0 flags=read-only, volatile, has-payload
      sensor_control_i2c_packet 0x009a2034 (u32)    : min=0 max=0 step=0 default=0 flags=read-only, volatile, has-payload
                    bypass_mode 0x009a2064 (intmenu): min=0 max=1 default=0 value=1
                override_enable 0x009a2065 (intmenu): min=0 max=1 default=0 value=1
                   height_align 0x009a2066 (int)    : min=1 max=16 step=1 default=1 value=1
                     size_align 0x009a2067 (intmenu): min=0 max=2 default=0 value=0
               write_isp_format 0x009a2068 (int)    : min=1 max=1 step=1 default=1 value=1
       sensor_signal_properties 0x009a2069 (u32)    : min=0 max=0 step=0 default=0 flags=read-only, has-payload
        sensor_image_properties 0x009a206a (u32)    : min=0 max=0 step=0 default=0 flags=read-only, has-payload
      sensor_control_properties 0x009a206b (u32)    : min=0 max=0 step=0 default=0 flags=read-only, has-payload
              sensor_dv_timings 0x009a206c (u32)    : min=0 max=0 step=0 default=0 flags=read-only, has-payload
               low_latency_mode 0x009a206d (bool)   : default=0 value=0
               preferred_stride 0x009a206e (int)    : min=0 max=65535 step=1 default=0 value=0
                   sensor_modes 0x009a2082 (int)    : min=0 max=30 step=1 default=30 value=2 flags=read-only


Please note the EEPROM CID was implemented in the sensor driver

Can you please suggest a solution for this issue ?

Thanks

hello chakibdace,

please see-also camera_common.h, did you use this struct?

struct camera_common_eeprom_data {
  	struct i2c_client *i2c_client;
  	struct i2c_adapter *adap;
  	struct i2c_board_info brd;
  	struct regmap *regmap;
};

BTW,
may I also know which JetPack release version you’re working with,
thanks

Hi @JerryChang

I am currently using this struct in fact it’s used in the ov5693 sensor driver and i got inspied by its implementation.

I am using JetPack 4.3 (L4T 32.4.3) in fact, the custom driver to get the stream was developed for this version of JetPack.

Thanks

hello chakibdace,

please dig into kernel logs, is there suspicious failures related to TEGRA_CAMERA_CID_EEPROM_DATA?

@JerryChang there is suspicious logs when the jetson setup

There is a tegra camera subbdev registration failed

Please dont take in consideration logs relating to max9295 and max9296

I guess there is not failures related to the EEPROM, please note that the at24.c driver is installed and work correctly using hexdump

Thanks

hello chakibdace,

do you have this has-eeprom property setting in device tree to enable eeprom support?
for exmaple,

 	i2c@3180000 {
 		tca9548@77 {
 			i2c@0 {
 				ov5693_a@36 {
 					...
 					/* Enable EEPROM support */
 					has-eeprom = "1";

besides, it’s generic camera controls to handle this CID control,
please dig into tegracam_ctrls.c to check you’ve sending this to low-level camera driver.

Hi @JerryChang

I’ve added the has-eeprom = “1” parameter in the device tree, and it seems that it couldn’t retreive the eeprom data from the sensor so the kernel init is failing with following error :

**Failed to register i2c client eeprom_ar0820 at 0x50 (-16)**

You can have the logs in attached image

Do you have any idea about why it is has this behavior ?

Because i’m able to hexdump eeprom from 30-0050 having the right data output like below

jetson@xavier:~$ sudo hexdump /sys/bus/i2c/devices/30-0050/eeprom 
0000000 4152 5050 0003 200d 0001 f010 ff80 ffff
0000010 ffff ffff ffff ffff ffff ffff ffff ffff
0000020 200e 0100 3241 3743 3737 3831 3038 3032
0000030 3030 3230 3132 3142 3037 3130 3332 d5d1
0000040 2f01 0100 554d 3149 3736 3335 0037 0000
0000050 0000 0000 0000 0000 3200 3230 2d31 3231
0000060 312d 0036 3531 303a 3a33 3135 8700 ff08
0000070 ffff ffff ffff ffff ffff ffff ffff ffff
0000080 5a1a 0100 0f08 0000 0878 0000 0001 0000
0000090 ad1f 4547 a7bc 4547 0000 0000 7924 44f1
00000a0 d8ca 4485 0005 0000 0000 3f80 9ab2 bd47
00000b0 144d bd17 94da 3d68 90d5 3d62 0000 0000
00000c0 0000 0000 0000 0000 0001 0000 1b92 3931
00000d0 72d5 b99c 0003 0000 4e20 ffff ffff ffff
00000e0 2011 0100 3135 3130 3731 3336 0000 0000
00000f0 0000 0000 0000 0000 0000 0000 0000 781e
0000100 2010 0100 0000 0000 0000 0000 0000 0000
0000110 0000 0000 0000 0000 0000 0000 0000 6ba9
0000120 0c09 0100 000c 0000 0000 239c ffff ffff
0000130 ffff ffff ffff ffff ffff ffff ffff ffff
0000140 1b06 0100 5346 3243 3233 5056 3231 425f
0000150 0031 0000 0000 0000 ef00 ff3b ffff ffff
0000160 ffff ffff ffff ffff ffff ffff ffff ffff
*
000100

Even using i2cget to read registers values from 0x50, i’ve the output data

jetson@xavier:~$ i2cget -f -y 30 0x50
0x50
jetson@xavier:~$ i2cget -f -y 30 0x50
0x03
jetson@xavier:~$ i2cget -f -y 30 0x50
0x00
jetson@xavier:~$ i2cget -f -y 30 0x50
0x0d
jetson@xavier:~$ i2cget -f -y 30 0x50
0x20
jetson@xavier:~$ i2cget -f -y 30 0x50
0x01
jetson@xavier:~$ i2cget -f -y 30 0x50
0x00
jetson@xavier:~$ i2cget -f -y 30 0x50
0x10
jetson@xavier:~$ i2cget -f -y 30 0x50
0xf0

There is the ar0820 sensor device tree :

	i2c@3180000 {
		tca9546@70 {
			i2c@0 {
			ar0820_a@10 {
				compatible = "nvidia,ar0820";
				/* I2C device address */
				reg = <0x10>;

				/* V4L2 device node location */
				devnode = "video0";

				/* Physical dimensions of sensor */
				physical_w = "3.674";
				physical_h = "2.738";

				/* Enable EEPROM support */
				has-eeprom = "1";

				sensor_model = "ar0820";

				post_crop_frame_drop = "0";

				use_sensor_mode_id = "true";
 
				mode0 {/*mode AR0820_MODE_3848X2168_30FPS*/
					mclk_khz = "24000";
					num_lanes = "4";
					tegra_sinterface = "serial_a";
					discontinuous_clk = "no";
					dpcm_enable = "false";
					cil_settletime = "0";

					dynamic_pixel_bit_depth = "12";
					csi_pixel_bit_depth = "12";
					mode_type = "bayer";
					pixel_phase = "grbg";

					active_w = "3848";
					active_h = "2168";
					readout_orientation = "0";
					line_length = "4440";
					inherent_gain = "1";
					mclk_multiplier = "14.58";
					pix_clk_hz = "296236800";
					serdes_pix_clk_hz = "833333333";

					gain_factor = "1";
					min_gain_val = "1";
					max_gain_val = "15";
					step_gain_val = "1";
					default_gain = "1";
					min_hdr_ratio = "1";
					max_hdr_ratio = "1";
					framerate_factor = "1000000";
					min_framerate = "30000000";
					max_framerate = "30000000";
					step_framerate = "1";
					default_framerate ="30000000";
					exposure_factor ="1000000";
					min_exp_time = "15";
					max_exp_time = "33333";
					step_exp_time = "1";
					default_exp_time = "33333";

					embedded_metadata_height = "0";
				};

				ports {
					#address-cells = <1>;
					#size-cells = <0>;
					port@0 {
						reg = <0>;
						liar0820_ar0820_out0: endpoint {
							port-index = <0>;
							bus-width = <4>;
							remote-endpoint = <&liar0820_csi_in0>;
							};
						};
					};
				gmsl-link {
					src-csi-port = "a";
					dst-csi-port = "a";
					serdes-csi-link = "a";
					csi-mode = "1x4";
					num-lanes = <4>;
					streams = "ued-u1", "raw12";
					};
				};
				eeprom_a@50 {
					compatible = "atmel,24c32";
					reg = <0x50>;
					pagesize = <32>;

This is the sensor driver, the EEPROM adrees is 0x50

#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/module.h>

#include <linux/seq_file.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>

#include <media/max9295.h>
#include <media/max9296.h>

#include <media/tegracam_core.h>
#include "ar0820_mode_tbls.h"
#include <media/ar0820.h>

#include <linux/debugfs.h>
#include <media/tegra-v4l2-camera.h>


#define AR0820_MIN_GAIN         (1)
#define AR0820_MAX_GAIN         (63)
#define AR0820_DEFAULT_FRAME_LENGTH		(2224)
#define AR0820_MAX_FRAME_LENGTH			(48928)
#define AR0820_FRAME_LENGTH_ADDR		0x300A
#define AR0820_COARSE_TIME_ADDR			0x3012
#define AR0820_GAIN_ADDR				0x3060
#define AR0820_COARSE_GAIN_ADDR			0x3366	/* GAIN ADDR */
#define AR0820_FINE_GAIN_ADDR			0x336A	/* GAIN ADDR */
#define AR0820_GROUP_HOLD_ADDR			0x3022	/* REG HOLD */
#define AR0820_SW_RESET_ADDR			0x3021	/* SW RESET */

const struct of_device_id ar0820_of_match[] = {
	{.compatible = "nvidia,ar0820", },
	{ },
};

MODULE_DEVICE_TABLE(of, ar0820_of_match);

static const u32 ctrl_cid_list[] = {
	TEGRA_CAMERA_CID_GAIN,
	TEGRA_CAMERA_CID_EXPOSURE,
	TEGRA_CAMERA_CID_EXPOSURE_SHORT,
	TEGRA_CAMERA_CID_FRAME_RATE,
	TEGRA_CAMERA_CID_HDR_EN,
	TEGRA_CAMERA_CID_SENSOR_MODE_ID,
	TEGRA_CAMERA_CID_EEPROM_DATA,
	//TEGRA_CAMERA_CID_OTP_DATA,
	//TEGRA_CAMERA_CID_FUSE_ID,
};

struct ar0820 {
	struct i2c_client *i2c_client;
	const struct i2c_device_id *id;
	struct v4l2_subdev *subdev;
	struct device *ser_dev;
	struct device *dser_dev;
	struct gmsl_link_ctx g_ctx;
	u32 frame_length;
	struct camera_common_data *s_data;
	struct tegracam_device *tc_dev;
	struct camera_common_eeprom_data eeprom[AR0820_EEPROM_BLOCK_SIZE];
	u8	   eeprom_buf[AR0820_EEPROM_SIZE];
	//u8	   otp_buf[AR0820_OTP_SIZE];
	//u8	   fuse_id[AR0820_FUSE_ID_SIZE];
	struct mutex			streaming_lock;
	bool				streaming;
	

};



static const struct regmap_config sensor_regmap_config = {
	.reg_bits = 16,
	.val_bits = 16,
	.cache_type = REGCACHE_RBTREE,
	.use_single_rw = true,
};

static inline void ar0820_get_frame_length_regs(ar0820_reg * regs,
						u32 frame_length)
{
	regs->addr = AR0820_FRAME_LENGTH_ADDR;
	regs->val = frame_length & 0xffff;
}

static inline void ar0820_get_coarse_time_regs_shs1(ar0820_reg * regs,
						    u32 coarse_time)
{
	regs->addr = AR0820_COARSE_TIME_ADDR;
	regs->val = coarse_time & 0xffff;
}

static inline void ar0820_get_gain_reg(ar0820_reg * regs, u16 gain)
{
	regs->addr = AR0820_GAIN_ADDR;
	regs->val = (gain) & 0xffff;
}

static inline void ar0820_get_coarse_gain_reg(ar0820_reg * regs, u16 gain)
{
	regs->addr = AR0820_COARSE_GAIN_ADDR;
	regs->val = (gain) & 0xffff;
}

static inline void ar0820_get_fine_gain_reg(ar0820_reg * regs, u16 gain)
{
	regs->addr = AR0820_FINE_GAIN_ADDR;
	regs->val = (gain) & 0xffff;
}

static int test_mode;
module_param(test_mode, int, 0644);

static inline int ar0820_read_reg(struct camera_common_data *s_data,
				  u16 addr, u16 * val)
{
	int err = 0;
	u32 reg_val = 0;

	err = regmap_read(s_data->regmap, addr, &reg_val);
	*val = reg_val & 0xFFFF;

	return err;
}

static int ar0820_write_reg(struct camera_common_data *s_data,
			    u16 addr, u16 val)
{
	int err;
	struct device *dev = s_data->dev;
	err = regmap_write(s_data->regmap, addr, val);

	if (err) {
		dev_err(dev, "%s:i2c write failed, 0x%x = %x\n",
			__func__, addr, val);
	}
	usleep_range(1000, 1010);
	return err;
}

static int ar0820_write_table(struct ar0820 *priv, const ar0820_reg table[])
{
	int ret = 0;
	int i = 0;
	int retry;
	struct camera_common_data *s_data = priv->s_data;
	struct i2c_client *client = priv->i2c_client;

	while (table[i].addr != AR0820_TABLE_END) {

		for (retry = 0; retry < 3; retry++) {
			if (AR0820_TABLE_WAIT_MS == table[i].addr) {
				msleep(table[i].val);
				break;
			}
			ret =
			    ar0820_write_reg(s_data, table[i].addr,
					     table[i].val);
			if (!ret) {
				dev_dbg(&client->dev,
					"Success writting register %x\n",
					table[i].addr);
				break;
			}
			usleep_range(1000, 1010);
		}
		i++;
	}
	return ret;
}

static struct mutex serdes_lock__;

static int ar0820_fill_string_ctrl(struct tegracam_device *tc_dev,
				struct v4l2_ctrl *ctrl)
{
	struct ar0820 *priv = tc_dev->priv;
	int i;

	switch (ctrl->id) {
	case TEGRA_CAMERA_CID_EEPROM_DATA:
		for (i = 0; i < AR0820_EEPROM_SIZE; i++)
			sprintf(&ctrl->p_new.p_char[i*2], "%02x",
				priv->eeprom_buf[i]);
		break;
    /*
	case TEGRA_CAMERA_CID_OTP_DATA:
		for (i = 0; i < AR0820_OTP_SIZE; i++)
			sprintf(&ctrl->p_new.p_char[i*2], "%02x",
				priv->otp_buf[i]);
		break;
	case TEGRA_CAMERA_CID_FUSE_ID:
		for (i = 0; i < AR0820_FUSE_ID_SIZE; i++)
			sprintf(&ctrl->p_new.p_char[i*2], "%02x",
				priv->fuse_id[i]);
		break;
    */
	default:
		return -EINVAL;
	}
	ctrl->p_cur.p_char = ctrl->p_new.p_char;
	return 0;
}

static int ar0820_eeprom_device_release(struct ar0820 *priv)
{
	int i;

	for (i = 0; i < AR0820_EEPROM_NUM_BLOCKS; i++) {
		if (priv->eeprom[i].i2c_client != NULL) {
			i2c_unregister_device(priv->eeprom[i].i2c_client);
			priv->eeprom[i].i2c_client = NULL;
		}
	}

	return 0;
}

static int ar0820_eeprom_device_init(struct ar0820 *priv)
{
	struct camera_common_pdata *pdata =  priv->s_data->pdata;
	char *dev_name = "eeprom_ar0820";
	static struct regmap_config eeprom_regmap_config = {
		.reg_bits = 8,
		.val_bits = 8,
	};
	int i;
	int err;

	if (!pdata->has_eeprom)
		return -EINVAL;

	for (i = 0; i < AR0820_EEPROM_NUM_BLOCKS; i++) {
		priv->eeprom[i].adap = i2c_get_adapter(
				priv->i2c_client->adapter->nr);
		memset(&priv->eeprom[i].brd, 0, sizeof(priv->eeprom[i].brd));
		strncpy(priv->eeprom[i].brd.type, dev_name,
				sizeof(priv->eeprom[i].brd.type));
		priv->eeprom[i].brd.addr = AR0820_EEPROM_ADDRESS + i;
		priv->eeprom[i].i2c_client = i2c_new_device(
				priv->eeprom[i].adap, &priv->eeprom[i].brd);

		priv->eeprom[i].regmap = devm_regmap_init_i2c(
			priv->eeprom[i].i2c_client, &eeprom_regmap_config);
		if (IS_ERR(priv->eeprom[i].regmap)) {
			err = PTR_ERR(priv->eeprom[i].regmap);
			ar0820_eeprom_device_release(priv);
			return err;
		}
	}

	return 0;
}

static int ar0820_read_eeprom(struct ar0820 *priv)
{
	int err, i;

	for (i = 0; i < AR0820_EEPROM_NUM_BLOCKS; i++) {
		err = regmap_bulk_read(priv->eeprom[i].regmap, 0,
			&priv->eeprom_buf[i * AR0820_EEPROM_BLOCK_SIZE],
			AR0820_EEPROM_BLOCK_SIZE);
		if (err)
			return err;
	}

	return 0;
}

static int ar0820_gmsl_serdes_setup(struct ar0820 *priv)
{
	int err = 0;
	struct device *dev;

	if (!priv || !priv->ser_dev || !priv->dser_dev || !priv->i2c_client) {
		return -EINVAL;
	}

	dev = &priv->i2c_client->dev;

	mutex_lock(&serdes_lock__);

	/* For now no separate power on required for serializer device */
	max9296_power_on(priv->dser_dev);

	/* setup serdes addressing and control pipeline */
	err = max9296_setup_link(priv->dser_dev, &priv->i2c_client->dev);
	if (err) {
		dev_err(dev, "gmsl deserializer link config failed\n");
		goto ret;
	}

	err = max9295_setup_control(priv->ser_dev);
	if (err) {
		dev_err(dev, "gmsl serializer setup failed\n");
		goto ret;
	}

	err = max9296_setup_control(priv->dser_dev);

	if (err) {
		dev_err(dev, "gmsl deserializer setup failed\n");
		goto ret;
	}

ret:
	mutex_unlock(&serdes_lock__);
	return err;
}

static void ar0820_gmsl_serdes_reset(struct ar0820 *priv)
{
	mutex_lock(&serdes_lock__);

	/* reset serdes addressing and control pipeline */
	max9295_reset_control(priv->ser_dev);
	max9296_reset_control(priv->dser_dev, &priv->i2c_client->dev);

	max9296_power_off(priv->dser_dev);

	mutex_unlock(&serdes_lock__);
}

static int ar0820_power_on(struct camera_common_data *s_data)
{
	int err = 0;
	struct camera_common_power_rail *pw = s_data->power;
	struct camera_common_pdata *pdata = s_data->pdata;
	struct device *dev = s_data->dev;

	dev_dbg(dev, "%s: power on\n", __func__);
	if (pdata && pdata->power_on) {
		err = pdata->power_on(pw);

		if (err) {
			dev_err(dev, "%s failed.\n", __func__);
		} else {
			pw->state = SWITCH_ON;
		}

		return err;
	}

	pw->state = SWITCH_ON;

	return 0;
}

static int ar0820_power_off(struct camera_common_data *s_data)
{
	int err = 0;
	struct camera_common_power_rail *pw = s_data->power;
	struct camera_common_pdata *pdata = s_data->pdata;
	struct device *dev = s_data->dev;

	dev_dbg(dev, "%s:\n", __func__);

	if (pdata && pdata->power_off) {
		err = pdata->power_off(pw);
		if (!err) {
			goto power_off_done;
		} else {
			dev_err(dev, "%s failed.\n", __func__);
		}
		return err;
	}

power_off_done:
	pw->state = SWITCH_OFF;

	return 0;
}

static int ar0820_power_get(struct tegracam_device *tc_dev)
{
	struct device *dev = tc_dev->dev;
	struct camera_common_data *s_data = tc_dev->s_data;
	struct camera_common_power_rail *pw = s_data->power;
	struct camera_common_pdata *pdata = s_data->pdata;
	const char *mclk_name;
	const char *parentclk_name;
	struct clk *parent;
	int err = 0;

	mclk_name = pdata->mclk_name ? pdata->mclk_name : "cam_mclk1";
	pw->mclk = devm_clk_get(dev, mclk_name);
	if (IS_ERR(pw->mclk)) {
		dev_err(dev, "unable to get clock %s\n", mclk_name);
		return PTR_ERR(pw->mclk);
	}

	parentclk_name = pdata->parentclk_name;
	if (parentclk_name) {
		parent = devm_clk_get(dev, parentclk_name);
		if (IS_ERR(parent)) {
			dev_err(dev, "unable to get parent clcok %s",
				parentclk_name);
		} else
			clk_set_parent(pw->mclk, parent);
	}

	pw->state = SWITCH_OFF;

	return err;
}

static int ar0820_power_put(struct tegracam_device *tc_dev)
{
	struct camera_common_data *s_data = tc_dev->s_data;
	struct camera_common_power_rail *pw = s_data->power;

	if (unlikely(!pw)) {
		return -EFAULT;
	}

	return 0;
}

static int ar0820_set_group_hold(struct tegracam_device *tc_dev, bool val)
{
	struct camera_common_data *s_data = tc_dev->s_data;
	struct device *dev = tc_dev->dev;
	int err;

	return 0;
	err = ar0820_write_reg(s_data, AR0820_GROUP_HOLD_ADDR, val);
	if (err) {
		dev_dbg(dev, "%s: Group hold control error\n", __func__);
		return err;
	}

	return 0;
}

static int ar0820_set_gain(struct tegracam_device *tc_dev, s64 val)
{
	struct camera_common_data *s_data = tc_dev->s_data;
	struct device *dev = tc_dev->dev;
	ar0820_reg reg_list[1];
	int err, i;
	u16 coarse_gain, fine_gain;

	if (val < AR0820_MIN_GAIN) {
		val = AR0820_MIN_GAIN;
	}

	if (val > AR0820_MAX_GAIN) {
		val = AR0820_MAX_GAIN;
	}

	coarse_gain = (u16) (val / 16);
	fine_gain = (u16) (val % 16);

	coarse_gain = (u16) (coarse_gain * 0x1111);
	fine_gain = (u16) (fine_gain * 0x1111);

	ar0820_get_coarse_gain_reg(reg_list, coarse_gain);
	for (i = 0; i < 1; i++) {
		err = ar0820_write_reg(s_data, reg_list[i].addr,
				       reg_list[i].val);
		if (err) {
			goto fail;
		}
	}
	ar0820_get_fine_gain_reg(reg_list, fine_gain);
	for (i = 0; i < 1; i++) {
		err = ar0820_write_reg(s_data, reg_list[i].addr,
				       reg_list[i].val);
		if (err) {
			goto fail;
		}
	}

	return 0;

fail:
	dev_info(dev, "%s: GAIN control error\n", __func__);
	return err;
}

static int ar0820_set_frame_rate(struct tegracam_device *tc_dev, s64 val)
{
	return 0;
}

static int ar0820_set_exposure(struct tegracam_device *tc_dev, s64 val)
{
	struct ar0820 *priv = (struct ar0820 *)tegracam_get_privdata(tc_dev);
	struct camera_common_data *s_data = tc_dev->s_data;
	const struct sensor_mode_properties *mode =
	    &s_data->sensor_props.sensor_modes[s_data->mode];
	ar0820_reg reg_list[1];
	int err;
	u32 coarse_time;
	int i = 0;

	if (priv->frame_length == 0) {
		priv->frame_length = AR0820_DEFAULT_FRAME_LENGTH / 8;
	}

	coarse_time =
	    mode->signal_properties.pixel_clock.val * val /
	    mode->control_properties.framerate_factor /
	    mode->image_properties.line_length;

	if (coarse_time > priv->frame_length) {
		coarse_time = priv->frame_length;
	}

	if (coarse_time < 4) {
		coarse_time = 4;
	}

	ar0820_get_coarse_time_regs_shs1(reg_list, coarse_time);
	for (i = 0; i < 1; i++) {
		err = ar0820_write_reg(priv->s_data, reg_list[i].addr,
				       reg_list[i].val);
		if (err) {
			goto fail;
		}
	}

	return 0;

fail:
	dev_dbg(&priv->i2c_client->dev,
		"%s: set coarse time error\n", __func__);
	return err;
}

static struct tegracam_ctrl_ops ar0820_ctrl_ops = {
	.numctrls = ARRAY_SIZE(ctrl_cid_list),
	.ctrl_cid_list = ctrl_cid_list,
	.string_ctrl_size = {AR0820_EEPROM_STR_SIZE},
	.set_gain = ar0820_set_gain,
	.set_exposure = ar0820_set_exposure,
	.set_exposure_short = ar0820_set_exposure,
	.set_frame_rate = ar0820_set_frame_rate,
	.set_group_hold = ar0820_set_group_hold,
	.fill_string_ctrl = ar0820_fill_string_ctrl,
};

static struct camera_common_pdata *ar0820_parse_dt(struct tegracam_device
						   *tc_dev)
{
	struct device *dev = tc_dev->dev;
	struct device_node *node = dev->of_node;
	struct camera_common_pdata *board_priv_pdata;
	const struct of_device_id *match;
	int err;

	if (!node) {
		return NULL;
	}

	match = of_match_device(ar0820_of_match, dev);
	if (!match) {
		dev_err(dev, "Failed to find matching dt id\n");
		return NULL;
	}

	board_priv_pdata =
	    devm_kzalloc(dev, sizeof(*board_priv_pdata), GFP_KERNEL);

	err = of_property_read_string(node, "mclk",
				      &board_priv_pdata->mclk_name);
	if (err) {
		dev_err(dev, "mclk not in DT\n");
	}

	board_priv_pdata->has_eeprom =
		of_property_read_bool(node, "has-eeprom");
	board_priv_pdata->v_flip = of_property_read_bool(node, "vertical-flip");
	board_priv_pdata->h_mirror = of_property_read_bool(node,
							 "horizontal-mirror");

	return board_priv_pdata;
}

static int ar0820_set_mode(struct tegracam_device *tc_dev)
{
	struct ar0820 *priv = (struct ar0820 *)tegracam_get_privdata(tc_dev);
	struct camera_common_data *s_data = tc_dev->s_data;
	struct device *dev = tc_dev->dev;
	const struct of_device_id *match;
	int err;

	match = of_match_device(ar0820_of_match, dev);
	if (!match) {
		dev_err(dev, "Failed to find matching dt id\n");
		return -EINVAL;
	}

	err = ar0820_write_table(priv, mode_table[s_data->mode_prop_idx]);
	if (err) {
		return err;
	}

	return 0;
}

static int ar0820_start_streaming(struct tegracam_device *tc_dev)
{
	struct ar0820 *priv = (struct ar0820 *)tegracam_get_privdata(tc_dev);
	struct device *dev = tc_dev->dev;
	int err;

	err = max9296_start_streaming(priv->dser_dev, dev);
	if (err) {
		goto exit;
	}

	err = ar0820_write_table(priv, mode_table[AR0820_MODE_START_STREAM]);
	if (err) {
		return err;
	}

	msleep(100);

	return 0;

exit:
	dev_err(dev, "%s: error setting stream\n", __func__);

	return err;
}

static int ar0820_stop_streaming(struct tegracam_device *tc_dev)
{
	struct device *dev = tc_dev->dev;
	struct ar0820 *priv = (struct ar0820 *)tegracam_get_privdata(tc_dev);
	int err;

	max9296_stop_streaming(priv->dser_dev, dev);

	err = ar0820_write_table(priv, mode_table[AR0820_MODE_STOP_STREAM]);
	if (err) {
		return err;
	}

	return 0;
}

static struct camera_common_sensor_ops ar0820_common_ops = {
	.numfrmfmts = ARRAY_SIZE(ar0820_frmfmt),
	.frmfmt_table = ar0820_frmfmt,
	.power_on = ar0820_power_on,
	.power_off = ar0820_power_off,
	.parse_dt = ar0820_parse_dt,
	.power_get = ar0820_power_get,
	.power_put = ar0820_power_put,
	.set_mode = ar0820_set_mode,
	.start_streaming = ar0820_start_streaming,
	.stop_streaming = ar0820_stop_streaming,
};

static int ar0820_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);

	dev_dbg(&client->dev, "%s:\n", __func__);

	return 0;
}

static const struct v4l2_subdev_internal_ops ar0820_subdev_internal_ops = {
	.open = ar0820_open,
};

static int ar0820_board_setup(struct ar0820 *priv)
{
	struct tegracam_device *tc_dev = priv->tc_dev;
	struct device *dev = tc_dev->dev;
	struct device_node *node = dev->of_node;
	struct device_node *ser_node;
	struct i2c_client *ser_i2c = NULL;
	struct device_node *dser_node;
	struct i2c_client *dser_i2c = NULL;
	struct device_node *gmsl;
	int value = 0xFFFF;
	const char *str_value;
	const char *str_value1[2];
	int i;
	int err = 0;
	bool eeprom_ctrl = 0;
	struct camera_common_data *s_data = priv->s_data;

	/* eeprom interface */
	err = ar0820_eeprom_device_init(priv);
	if (err && s_data->pdata->has_eeprom)
		dev_err(dev,
			"Failed to allocate eeprom reg map: %d\n", err);
	eeprom_ctrl = !err;

	err = camera_common_mclk_enable(s_data);
	if (err) {
		dev_err(dev,
			"Error %d turning on mclk\n", err);
		return err;
	}

	err = ar0820_power_on(s_data);
	if (err) {
		dev_err(dev,
			"Error %d during power on sensor\n", err);
		return err;
	}

	if (eeprom_ctrl) {
		err = ar0820_read_eeprom(priv);
		if (err) {
			dev_err(dev,
				"Error %d reading eeprom\n", err);
			goto error;
		}
	}
    /*
	err = ar0820_otp_setup(priv);
	if (err) {
		dev_err(dev,
			"Error %d reading otp data\n", err);
		goto error;
	}

	err = ar0820_fuse_id_setup(priv);
	if (err) {
		dev_err(dev,
			"Error %d reading fuse id data\n", err);
		goto error;
	}
    */

	err = of_property_read_u32(node, "reg", &priv->g_ctx.sdev_reg);
	if (err < 0) {
		dev_err(dev, "reg not found\n");
		goto error;
	}

	err = of_property_read_u32(node, "def-addr", &priv->g_ctx.sdev_def);
	if (err < 0) {
		dev_err(dev, "def-addr not found\n");
		goto error;
	}

	ser_node = of_parse_phandle(node, "nvidia,gmsl-ser-device", 0);
	if (ser_node == NULL) {
		dev_err(dev, "missing %s handle\n", "nvidia,gmsl-ser-device");
		goto error;
	}

	err = of_property_read_u32(ser_node, "reg", &priv->g_ctx.ser_reg);
	if (err < 0) {
		dev_err(dev, "serializer reg not found\n");
		goto error;
	}

	ser_i2c = of_find_i2c_device_by_node(ser_node);
	of_node_put(ser_node);

	if (ser_i2c == NULL) {
		dev_err(dev, "missing serializer dev handle\n");
		goto error;
	}
	if (ser_i2c->dev.driver == NULL) {
		dev_err(dev, "missing serializer driver\n");
		goto error;
	}

	priv->ser_dev = &ser_i2c->dev;

	dser_node = of_parse_phandle(node, "nvidia,gmsl-dser-device", 0);
	if (dser_node == NULL) {
		dev_err(dev, "missing %s handle\n", "nvidia,gmsl-dser-device");
		goto error;
	}

	dser_i2c = of_find_i2c_device_by_node(dser_node);
	of_node_put(dser_node);

	if (dser_i2c == NULL) {
		dev_err(dev, "missing deserializer dev handle\n");
		goto error;
	}
	if (dser_i2c->dev.driver == NULL) {
		dev_err(dev, "missing deserializer driver\n");
		goto error;
	}

	priv->dser_dev = &dser_i2c->dev;

	/* populate g_ctx from DT */
	gmsl = of_get_child_by_name(node, "gmsl-link");
	if (gmsl == NULL) {
		dev_err(dev, "missing gmsl-link device node\n");
		err = -EINVAL;
		goto error;
	}

	err = of_property_read_string(gmsl, "dst-csi-port", &str_value);
	if (err < 0) {
		dev_err(dev, "No dst-csi-port found\n");
		goto error;
	}
	priv->g_ctx.dst_csi_port =
	    (!strcmp(str_value, "a")) ? GMSL_CSI_PORT_A : GMSL_CSI_PORT_B;

	err = of_property_read_string(gmsl, "src-csi-port", &str_value);
	if (err < 0) {
		dev_err(dev, "No src-csi-port found\n");
		goto error;
	}
	priv->g_ctx.src_csi_port =
	    (!strcmp(str_value, "a")) ? GMSL_CSI_PORT_A : GMSL_CSI_PORT_B;

	err = of_property_read_string(gmsl, "csi-mode", &str_value);
	if (err < 0) {
		dev_err(dev, "No csi-mode found\n");
		goto error;
	}

	if (!strcmp(str_value, "1x4")) {
		priv->g_ctx.csi_mode = GMSL_CSI_1X4_MODE;
	} else if (!strcmp(str_value, "2x4")) {
		priv->g_ctx.csi_mode = GMSL_CSI_2X4_MODE;
	} else if (!strcmp(str_value, "4x2")) {
		priv->g_ctx.csi_mode = GMSL_CSI_4X2_MODE;
	} else if (!strcmp(str_value, "2x2")) {
		priv->g_ctx.csi_mode = GMSL_CSI_2X2_MODE;
	} else {
		dev_err(dev, "invalid csi mode\n");
		goto error;
	}

	err = of_property_read_string(gmsl, "serdes-csi-link", &str_value);
	if (err < 0) {
		dev_err(dev, "No serdes-csi-link found\n");
		goto error;
	}
	priv->g_ctx.serdes_csi_link =
	    (!strcmp(str_value, "a")) ?
	    GMSL_SERDES_CSI_LINK_A : GMSL_SERDES_CSI_LINK_B;

	err = of_property_read_u32(gmsl, "num-lanes", &value);
	if (err < 0) {
		dev_err(dev, "No num-lanes info\n");
		goto error;
	}
	priv->g_ctx.num_csi_lanes = value;

	priv->g_ctx.num_streams = of_property_count_strings(gmsl, "streams");
	if (priv->g_ctx.num_streams <= 0) {
		dev_err(dev, "No streams found\n");
		err = -EINVAL;
		goto error;
	}

	for (i = 0; i < priv->g_ctx.num_streams; i++) {
		of_property_read_string_index(gmsl, "streams", i,
					      &str_value1[i]);
		if (!str_value1[i]) {
			dev_err(dev, "invalid stream info\n");
			goto error;
		}
		if (!strcmp(str_value1[i], "raw12")) {
			priv->g_ctx.streams[i].st_data_type =
			    GMSL_CSI_DT_RAW_12;
		} else if (!strcmp(str_value1[i], "embed")) {
			priv->g_ctx.streams[i].st_data_type = GMSL_CSI_DT_EMBED;
		} else if (!strcmp(str_value1[i], "ued-u1")) {
			priv->g_ctx.streams[i].st_data_type =
			    GMSL_CSI_DT_UED_U1;
		} else {
			dev_err(dev, "invalid stream data type\n");
			goto error;
		}
	}

	priv->g_ctx.s_dev = dev;

	

	return 0;

error:
	dev_err(dev, "board setup failed\n");
	return err;
}

static int ar0820_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct device *dev = &client->dev;
	struct device_node *node = dev->of_node;
	struct tegracam_device *tc_dev;
	struct ar0820 *priv;
	int err;

	dev_info(dev, "probing v4l2 sensor.\n");

	if (!IS_ENABLED(CONFIG_OF) || !node) {
		return -EINVAL;
	}

	priv = devm_kzalloc(dev, sizeof(struct ar0820), GFP_KERNEL);
	if (!priv) {
		dev_err(dev, "unable to allocate memory!\n");
		return -ENOMEM;
	}
	tc_dev = devm_kzalloc(dev, sizeof(struct tegracam_device), GFP_KERNEL);
	if (!tc_dev)
		return -ENOMEM;

	priv->i2c_client = tc_dev->client = client;
	tc_dev->dev = dev;
	strncpy(tc_dev->name, "ar0820", sizeof(tc_dev->name));
	tc_dev->dev_regmap_config = &sensor_regmap_config;
	tc_dev->sensor_ops = &ar0820_common_ops;
	tc_dev->v4l2sd_internal_ops = &ar0820_subdev_internal_ops;
	tc_dev->tcctrl_ops = &ar0820_ctrl_ops;

	err = tegracam_device_register(tc_dev);
	if (err) {
		dev_err(dev, "tegra camera driver registration failed\n");
		return err;
	}
	priv->tc_dev = tc_dev;
	priv->s_data = tc_dev->s_data;
	priv->subdev = &tc_dev->s_data->subdev;
	tegracam_set_privdata(tc_dev, (void *)priv);

	err = ar0820_board_setup(priv);
	if (err) {
		dev_err(dev, "board setup failed\n");
		return err;
	}

	/* Pair sensor to serializer dev */
	err = max9295_sdev_pair(priv->ser_dev, &priv->g_ctx);
	if (err) {
		dev_err(&client->dev, "gmsl ser pairing failed\n");
		return err;
	}

	/* Register sensor to deserializer dev */
	err = max9296_sdev_register(priv->dser_dev, &priv->g_ctx);
	if (err) {
		dev_err(&client->dev, "gmsl deserializer register failed\n");
		return err;
	}

	/*
	 * gmsl serdes setup
	 *
	 * Sensor power on/off should be the right place for serdes
	 * setup/reset. But the problem is, the total required delay
	 * in serdes setup/reset exceeds the frame wait timeout, looks to
	 * be related to multiple channel open and close sequence
	 * issue (#BUG 200477330).
	 * Once this bug is fixed, these may be moved to power on/off.
	 * The delays in serdes is as per guidelines and can't be reduced,
	 * so it is placed in probe/remove, though for that, deserializer
	 * would be powered on always post boot, until 1.2v is supplied
	 * to deserializer from CVB.
	 */
	err = ar0820_gmsl_serdes_setup(priv);
	if (err) {
		dev_err(&client->dev,
			"%s gmsl serdes setup failed\n", __func__);
		return err;
	}

	err = tegracam_v4l2subdev_register(tc_dev, true);
	if (err) {
		dev_err(dev, "tegra camera subdev registration failed\n");
		return err;
	}

	dev_info(&client->dev, "Detected AR0820 sensor\n");

	err = max9295_setup_streaming(priv->ser_dev);
	if (err) {
		dev_info(dev, "Error setting up max9295");
	}
	err = max9296_setup_streaming(priv->dser_dev, dev);
	if (err) {
		dev_info(dev, "Error setting up max9296");
	}

	return 0;
}

static int ar0820_remove(struct i2c_client *client)
{
	struct camera_common_data *s_data = to_camera_common_data(&client->dev);
	struct ar0820 *priv = (struct ar0820 *)s_data->priv;

	ar0820_gmsl_serdes_reset(priv);

	tegracam_v4l2subdev_unregister(priv->tc_dev);
	tegracam_device_unregister(priv->tc_dev);
	ar0820_eeprom_device_release(priv);


	return 0;
}

static const struct i2c_device_id ar0820_id[] = {
	{ "ar0820", 0 },
	{ }
};

MODULE_DEVICE_TABLE(i2c, ar0820_id);

static struct i2c_driver ar0820_i2c_driver = {
	.driver = {
		   .name = "ar0820",
		   .owner = THIS_MODULE,
		   .of_match_table = of_match_ptr(ar0820_of_match),
		    },
	.probe = ar0820_probe,
	.remove = ar0820_remove,
	.id_table = ar0820_id,
};

static int __init ar0820_init(void)
{
	mutex_init(&serdes_lock__);

	return i2c_add_driver(&ar0820_i2c_driver);
}

static void __exit ar0820_exit(void)
{
	mutex_destroy(&serdes_lock__);

	i2c_del_driver(&ar0820_i2c_driver);
}

module_init(ar0820_init);
module_exit(ar0820_exit);

MODULE_DESCRIPTION("Media Controller driver for Onsemi AR0820");

Thanks

hello chakibdace,

please check eeprom manager, i.e. kernel-dts/common/tegra194-eeprom-manager-p2888-0000.dtsi
you need to add address list and enable eeprom manager in device tree. you may see-also Camera Module Hardware Design Guide for reference,
thanks

@JerryChang can you please give more details or an example about how to add the adress and how to enable the eeprom manger in the device tree ?
Because from now i’ve enabled the I2C AT24 eeprom in the .config like below :

CONFIG_AT24 = y
Also there is steps i’ve done :

  1. at24 eeprom source code implemented and enabled in .dtsi related to the image sensor

  2. at24 enabled in .config, and able to hexdump 30-0050 (eeprom) to get the output

  3. Implement the EEPROM CID

  4. Add has_eeprom = 1 property in the dt

There is the current eeprom manager dtsi

/ {
	eeprom-manager {
		data-size = <0x100>;
		bus@0 {
			i2c-bus = <&gen1_i2c>;
			eeprom@0 {
				slave-address = <0x50>;
				label = "cvm";
			};
			eeprom@1 {
				slave-address = <0x56>;
			};
		};
		bus@1 {
			i2c-bus = <&cam_i2c>;
			eeprom@0 {
				slave-address = <0x54>;
			};
			eeprom@1 {
				slave-address = <0x57>;
			};
			eeprom@2 {
				slave-address = <0x52>;
			};
		};
		bus@2 {
			i2c-bus = <&gen2_i2c>;
			eeprom@0 {
				slave-address = <0x52>;
				label = "cvm";
			};
			eeprom@1 {
				slave-address = <0x50>;
			};
		};
	};
};

Thanks

hello chakibdace,

as mentioned here, this is the default I2C address (0x54) of the EEPROM device is used.

hence,
please add your 0x50 into device tree for the cam_i2c field.
you should also toggle below to enable eeprom-manager.

        eeprom-manager {
                status = "okay";
        };

Hi @JerryChang

Thank you for your support, i’ve added the x50 in cam_i2cfield on the eeprom manager and i’ve set the status to okay but i still have the setup error below

Failed to register i2c client eeprom_ar0820 at 0x50 (-16)

My eeprom manager is like the following

/ {
	eeprom-manager {
		data-size = <0x100>;
                status = "okay";

		bus@0 {
			i2c-bus = <&gen1_i2c>;
			eeprom@0 {
				slave-address = <0x50>;
				label = "cvm";
			};
			eeprom@1 {
				slave-address = <0x56>;
			};
		};
		bus@1 {
			i2c-bus = <&cam_i2c>;
			eeprom@0 {
				slave-address = <0x54>;
			};
			eeprom@1 {
				slave-address = <0x57>;
			};
			eeprom@2 {
				slave-address = <0x52>;
			};
			eeprom@3 {
				slave-address = <0x50>;
			};

Thanks

hello chakibdace,

it might be code implement bugs, since it’s shown EBUSY in your error code.
for example, . #define EBUSY 16 /* Device or resource busy */

Hi @JerryChang

Thank you for your support, that’s a little bit suspicious, the code sensor we’re using has the same implementation as the imx390 sensor, it uses gmsl2 to mipi csi 2 protocols, so from the eeprom support i’ve only added is the EEPROM CID implementation as used in ov5693 sensor.

Is there any code that was added from the eeprom support in gmsl2 sensors ?

Thanks.

hello chakibdace,

are you using eeprom for camera auto detection?
please revisit Camera Module Hardware Design Guide, and check [Figure 2-4. Multiple Camera Case Block Diagram].
here’s reference device tree to enable eeprom at 0x54 and 0x57.

the i2c bus may differ since you’ve device tree implementation to put it behind tca9546@70 {...}
you may see-also Topic 192486, comment #8 and Topic 192486, comment #17 for the explanation for the i2c_mux.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.