NXP i.mx8 driver porting for ToF9036

Hi Team,

we are currently integrating some development project. This main goal is to connect the ADI 9036 TOF Module through mipi on the NXP i.mx8 mini target board.... In addition, the recommended development environment(released) by NXP is yocto, so if we suppose we refer to the following official link to complie the driver of addi9036 and keep it in the Linux kernel v4.19 on the PC/Linux, is there anything else need to concern?  

Link : github.com/.../addi9036.c

Thanks,

Nino

  • 0
    •  Analog Employees 
    on Sep 22, 2020 6:55 AM 2 months ago

    Hi Nino,

    We used ToF camera with an evaluation board from Variscite that is based on I.MX8 Mini.

    In order to get it running you have to perform changes in NXP kernel and ADI Tof SDK.

    Please find the kernel changes bellow: 

    diff --git a/arch/arm64/boot/dts/freescale/fsl-imx8mm-var-dart.dts b/arch/arm64/boot/dts/freescale/fsl-imx8mm-var-dart.dts
    index 73b67f8a3416..028723e8802f 100644
    --- a/arch/arm64/boot/dts/freescale/fsl-imx8mm-var-dart.dts
    +++ b/arch/arm64/boot/dts/freescale/fsl-imx8mm-var-dart.dts
    @@ -748,44 +748,43 @@
     		touchscreen-inverted-y;
     	};
     
    -	ov5640_mipi1: ov5640_mipi1@3c {
    +	/* DS1337 RTC module */
    +	rtc@0x68 {
    +		status = "okay";
    +		compatible = "dallas,ds1337";
    +		reg = <0x68>;
    +		pinctrl-names = "default";
    +		pinctrl-0 = <&pinctrl_rtc>;
    +		interrupt-parent = <&gpio1>;
    +		interrupts = <15 IRQ_TYPE_EDGE_FALLING>;
    +	};
    +
    +	addi9036: addi9036@64 {
    +		compatible = "adi,addi9036";
    +		reg = <0x64>;
     		status = "okay";
    -		compatible = "ovti,ov5640_mipi";
    -		reg = <0x3c>;
     		pinctrl-names = "default";
     		pinctrl-0 = <&pinctrl_csi1>;
     		clocks = <&clk IMX8MM_CLK_CLKO1_DIV>;
     		clock-names = "csi_mclk";
    -/* Disabled CLKO2, since DART-MX8MM camera expansion board uses
    - * its own oscillator. Enable CLK02 if your desing requres it
    - */
    -#if 0
     		assigned-clocks = <&clk IMX8MM_CLK_CLKO1_SRC>,
     				  <&clk IMX8MM_CLK_CLKO1_DIV>;
     		assigned-clock-parents = <&clk IMX8MM_CLK_24M>;
     		assigned-clock-rates = <0>, <24000000>;
    -#endif
    -		csi_id = <0>;
    -		pwn-gpios = <&gpio4 8 GPIO_ACTIVE_HIGH>;
    -		rst-gpios = <&gpio5 28 GPIO_ACTIVE_HIGH>;
    -		mclk = <24000000>;
    -		mclk_source = <0>;
    +		reset-gpios = <&gpio5 28 GPIO_ACTIVE_HIGH>;
     		port {
    -			ov5640_mipi1_ep: endpoint {
    +			addi9036_ep: endpoint {
     				remote-endpoint = <&mipi1_sensor_ep>;
    +				clock-lanes = <0>;
    +				data-lanes = <1 2>;
     			};
     		};
     	};
     
    -	/* DS1337 RTC module */
    -	rtc@0x68 {
    -		status = "okay";
    -		compatible = "dallas,ds1337";
    -		reg = <0x68>;
    -		pinctrl-names = "default";
    -		pinctrl-0 = <&pinctrl_rtc>;
    -		interrupt-parent = <&gpio1>;
    -		interrupts = <15 IRQ_TYPE_EDGE_FALLING>;
    +	eeprom_i2c3: eeprom@56 {
    +		compatible = "atmel,24c1024";
    +		reg = <0x56>;
    +		pagesize = <32>;
     	};
     };
     
    @@ -825,6 +824,7 @@
     
     &csi1_bridge {
     	fsl,mipi-mode;
    +	fsl,two-8bit-sensor-mode;
     	status = "okay";
     	port {
     		csi1_ep: endpoint {
    @@ -837,12 +837,13 @@
     	#address-cells = <1>;
     	#size-cells = <0>;
     	status = "okay";
    +	clock-frequency = <270000000>;
     	port {
     		mipi1_sensor_ep: endpoint1 {
    -			remote-endpoint = <&ov5640_mipi1_ep>;
    +			remote-endpoint = <&addi9036_ep>;
     			data-lanes = <2>;
    -			csis-hs-settle = <13>;
    -			csis-clk-settle = <2>;
    +			csis-hs-settle = <6>;
    +			csis-clk-settle = <0>;
     			csis-wclk;
     		};
     
    diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
    index 3bdc34deae7b..d883ca147800 100644
    --- a/drivers/media/i2c/Kconfig
    +++ b/drivers/media/i2c/Kconfig
    @@ -544,6 +544,18 @@ comment "Camera sensor devices"
     config VIDEO_APTINA_PLL
     	tristate
     
    +config VIDEO_ADDI9036
    +	tristate "Analog Devices ADDI9036 ToF sensor support"
    +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
    +	depends on MEDIA_CAMERA_SUPPORT
    +	select V4L2_FWNODE
    +	help
    +	  This is a Video4Linux2 driver for Analog Devices ADDI9036
    +	  Time of Flight sensor.
    +
    +	  To compile this driver as a module, choose M here: the
    +	  module will be called addi9036.
    +
     config VIDEO_SMIAPP_PLL
     	tristate
     
    diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
    index f104650d6000..5870f0a394fb 100644
    --- a/drivers/media/i2c/Makefile
    +++ b/drivers/media/i2c/Makefile
    @@ -22,6 +22,7 @@ obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
     obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
     obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o
     obj-$(CONFIG_VIDEO_AD5820)  += ad5820.o
    +obj-$(CONFIG_VIDEO_ADDI9036) += addi9036.o
     obj-$(CONFIG_VIDEO_DW9714)  += dw9714.o
     obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
     obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
    diff --git a/drivers/media/i2c/addi9036.c b/drivers/media/i2c/addi9036.c
    new file mode 100644
    index 000000000000..643f5dcb0382
    --- /dev/null
    +++ b/drivers/media/i2c/addi9036.c
    @@ -0,0 +1,683 @@
    +// SPDX-License-Identifier: GPL-2.0
    +/*
    + * Driver for the ADDI9036 ToF camera sensor.
    + *
    + * Copyright (C) 2018-2019 Analog Devices, All Rights Reserved.
    + *
    + * Based on OV5640 Camera Driver
    + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
    + * Copyright (C) 2014-2017 Mentor Graphics Inc.
    + *
    + */
    +
    +#include <linux/device.h>
    +#include <linux/gpio/consumer.h>
    +#include <linux/i2c.h>
    +#include <linux/init.h>
    +#include <linux/module.h>
    +#include <linux/mutex.h>
    +#include <linux/of.h>
    +#include <linux/of_graph.h>
    +#include <linux/regmap.h>
    +#include <linux/regulator/consumer.h>
    +#include <linux/slab.h>
    +#include <linux/types.h>
    +#include <linux/version.h>
    +#include <media/v4l2-ctrls.h>
    +#include <media/v4l2-fwnode.h>
    +#include <media/v4l2-subdev.h>
    +
    +struct addi9036_mode_info {
    +	u32 width;
    +	u32 height;
    +	u32 pixel_clock;
    +	u32 link_freq_idx;
    +};
    +
    +struct addi9036 {
    +	struct regmap *regmap;
    +	struct device *dev;
    +	struct v4l2_subdev sd;
    +	struct media_pad pad;
    +	struct v4l2_fwnode_endpoint ep;
    +	struct v4l2_mbus_framefmt fmt;
    +	struct v4l2_rect crop;
    +
    +	const struct addi9036_mode_info *current_mode;
    +
    +	struct v4l2_ctrl_handler ctrls;
    +	struct v4l2_ctrl *pixel_clock;
    +	struct v4l2_ctrl *link_freq;
    +
    +	/* lock to protect power state */
    +	struct mutex power_lock;
    +	int power_count;
    +
    +	struct gpio_desc *rst_gpio;
    +
    +	/* controls */
    +	struct v4l2_ctrl *set_chip_config;
    +	struct v4l2_ctrl *reg_read;
    +};
    +
    +static inline struct addi9036 *to_addi9036(struct v4l2_subdev *sd)
    +{
    +	return container_of(sd, struct addi9036, sd);
    +}
    +
    +#define V4L2_CID_AD_DEV_SET_CHIP_CONFIG_QUERY  (V4L2_CID_USER_ADDI9036_BASE + 0)
    +#define V4L2_CID_AD_DEV_REG_READ_QUERY  (V4L2_CID_USER_ADDI9036_BASE + 1)
    +
    +static const struct reg_sequence addi9036_powerup_setting[] = {
    +	{ 0xc4c0, 0x001c },
    +	{ 0xc4c3, 0x001c },
    +	{ 0xc4d7, 0x0000 },
    +	{ 0xc4d5, 0x0002 },
    +	{ 0xc4da, 0x0001 },
    +	{ 0xc4f0, 0x0000 },
    +	{ 0xc427, 0x0003 },
    +	{ 0xc427, 0x0001 },
    +	{ 0xc427, 0x0000 },
    +	{ 0xc426, 0x0030 },
    +	{ 0xc426, 0x0010 },
    +	{ 0xc426, 0x0000 },
    +	{ 0xc423, 0x0080 },
    +	{ 0xc431, 0x0080 },
    +	{ 0x4001, 0x0007 },
    +	{ 0x7c22, 0x0004 }
    +};
    +
    +static const struct reg_sequence addi9036_powerdown_setting[] = {
    +	{ 0xc022, 0x0001 },
    +	{ 0x4001, 0x0006 },
    +	{ 0x7c22, 0x0004 },
    +	{ 0xc431, 0x0082 },
    +	{ 0xc423, 0x0000 },
    +	{ 0xc426, 0x0020 },
    +	{ 0xc427, 0x0002 },
    +	{ 0xc4c0, 0x003c },
    +	{ 0xc4c3, 0x003c },
    +	{ 0xc4d5, 0x0003 },
    +	{ 0xc4da, 0x0000 },
    +	{ 0xc4d7, 0x0001 },
    +	{ 0xc4f0, 0x0001 }
    +};
    +
    +static const s64 link_freq_tbl[] = {
    +	147456000,
    +	147456000
    +};
    +
    +/* Elements of the structure must be ordered ascending by width & height */
    +static const struct addi9036_mode_info addi9036_mode_info_data[] = {
    +	{
    +		.width = 640,
    +		.height = 480,
    +		.pixel_clock = 73728000,
    +		.link_freq_idx = 0 /* an index in link_freq_tbl[] */
    +	},
    +	{
    +		.width = 640,
    +		.height = 960,
    +		.pixel_clock = 73728000,
    +		.link_freq_idx = 1 /* an index in link_freq_tbl[] */
    +	},
    +};
    +
    +static bool addi9306_readable_register(struct device *dev, unsigned int reg)
    +{
    +	if (((reg >= 0x4000) && (reg <= 0x6FFF)) ||
    +	    ((reg >= 0x7C00) && (reg <= 0x7C1F)) ||
    +	    ((reg >= 0x7CE0) && (reg <= 0x7FFF)) ||
    +	    ((reg >= 0xC000) && (reg <= 0xC0FF)) ||
    +	    ((reg >= 0xC110) && (reg <= 0xC200)) ||
    +	    ((reg >= 0xC300) && (reg <= 0xC6BF)))
    +		return true;
    +	else
    +		return false;
    +}
    +
    +static bool addi9306_writeable_register(struct device *dev, unsigned int reg)
    +{
    +	if (((reg >= 0x4000) && (reg <= 0x6FFF)) ||
    +	    ((reg >= 0x7C00) && (reg <= 0x7FFF)) ||
    +	    ((reg >= 0xC000) && (reg <= 0xC200)) ||
    +	    ((reg >= 0xC300) && (reg <= 0xC7FF)) ||
    +    	    (reg == 0))
    +		return true;
    +	else
    +		return false;
    +}
    +
    +static const struct regmap_config addi9036_i2c_regmap_config = {
    +	.reg_bits = 16,
    +	.val_bits = 16,
    +
    +	.max_register = 0xC7FF,
    +	.writeable_reg = addi9306_writeable_register,
    +	.readable_reg = addi9306_readable_register,
    +	.cache_type = REGCACHE_NONE,
    +};
    +
    +static int addi9036_set_power_on(struct addi9036 *addi9036)
    +{
    +	int ret;
    +
    +	gpiod_set_value_cansleep(addi9036->rst_gpio, 0);
    +
    +	ret = regmap_register_patch(addi9036->regmap, addi9036_powerup_setting,
    +				    ARRAY_SIZE(addi9036_powerup_setting));
    +	if (ret)
    +		dev_err(addi9036->dev, "could not set power up registers\n");
    +
    +	return ret;
    +}
    +
    +static int addi9036_set_power_off(struct addi9036 *addi9036)
    +{
    +	int ret;
    +
    +	ret = regmap_register_patch(addi9036->regmap,
    +				    addi9036_powerdown_setting,
    +				    ARRAY_SIZE(addi9036_powerdown_setting));
    +	if (ret)
    +		dev_err(addi9036->dev, "could not set power down registers\n");
    +
    +	gpiod_set_value_cansleep(addi9036->rst_gpio, 1);
    +
    +	return ret;
    +}
    +
    +static int addi9036_s_power(struct v4l2_subdev *sd, int on)
    +{
    +	struct addi9036 *addi9036 = to_addi9036(sd);
    +	int ret = 0;
    +
    +	mutex_lock(&addi9036->power_lock);
    +
    +	/* If the power count is modified from 0 to != 0 or from != 0 to 0,
    +	 * update the power state.
    +	 */
    +	if (addi9036->power_count == !on) {
    +		if (on) {
    +			ret = addi9036_set_power_on(addi9036);
    +			if (ret < 0)
    +				goto exit;
    +		} else {
    +			ret = addi9036_set_power_off(addi9036);
    +			if (ret < 0)
    +				goto exit;
    +		}
    +
    +	}
    +
    +	/* Update the power count. */
    +	addi9036->power_count += on ? 1 : -1;
    +	WARN_ON(addi9036->power_count < 0);
    +
    +exit:
    +	mutex_unlock(&addi9036->power_lock);
    +	return ret;
    +}
    +
    +static int addi9036_set_chip_config(struct addi9036 *addi9036,
    +				    struct v4l2_ctrl *ctrl)
    +{
    +	int ret, index;
    +	unsigned short *reg, *val;
    +
    +	reg = (unsigned short *)(ctrl->p_new.p_u16);
    +	val = (unsigned short *)(ctrl->p_new.p_u16 + 1);
    +
    +	for (index = 0; index < ctrl->elems; index += 2) {
    +		ret = regmap_write(addi9036->regmap, *reg, *val);
    +		if (ret)
    +			dev_warn(addi9036->dev,
    +				 "could not write to register %x\n", *reg);
    +
    +		reg += 2;
    +		val += 2;
    +	}
    +
    +	return 0;
    +}
    +
    +static int addi9036_s_ctrl(struct v4l2_ctrl *ctrl)
    +{
    +	struct addi9036 *addi9036 = container_of(ctrl->handler,
    +						 struct addi9036, ctrls);
    +	int ret = 0;
    +	unsigned int val;
    +
    +	switch (ctrl->id) {
    +	case V4L2_CID_AD_DEV_SET_CHIP_CONFIG_QUERY:
    +		ret = addi9036_set_chip_config(addi9036, ctrl);
    +		break;
    +	case V4L2_CID_AD_DEV_REG_READ_QUERY:
    +		ret = regmap_read(addi9036->regmap, *(u16 *)(ctrl->p_new.p_u16),
    +				  &val);
    +		if (ret)
    +			dev_warn(addi9036->dev,
    +				 "could not read from register\n");
    +		else
    +			*(u16 *)(ctrl->p_new.p_u16) = val;
    +		break;
    +	case V4L2_CID_PIXEL_RATE:
    +	case V4L2_CID_LINK_FREQ:
    +		break;
    +	default:
    +		dev_err(addi9036->dev, "%s > Unhandled: %x  param=%x\n",
    +			__func__, ctrl->id, ctrl->val);
    +		ret = -EINVAL;
    +		break;
    +	}
    +
    +	return ret;
    +}
    +
    +static const struct v4l2_ctrl_ops addi9036_ctrl_ops = {
    +	.s_ctrl = addi9036_s_ctrl,
    +};
    +
    +static const struct v4l2_ctrl_config addi9036_ctrl_chip_config = {
    +	.ops		= &addi9036_ctrl_ops,
    +	.id		= V4L2_CID_AD_DEV_SET_CHIP_CONFIG_QUERY,
    +	.name		= "chip_config",
    +	.type		= V4L2_CTRL_TYPE_U16,
    +	.def		= 0xFF,
    +	.min		= 0x00,
    +	.max		= 0xFFFF,
    +	.step		= 1,
    +	.dims		= { 2048 },
    +	.elem_size	= 2
    +};
    +
    +static const struct v4l2_ctrl_config addi9036_ctrl_reg_read = {
    +	.ops	= &addi9036_ctrl_ops,
    +	.id	= V4L2_CID_AD_DEV_REG_READ_QUERY,
    +	.name	= "reg_read",
    +	.type	= V4L2_CTRL_TYPE_U16,
    +	.def	= 0,
    +	.min	= 0x00,
    +	.max	= 0xFFFF,
    +	.step	= 1
    +};
    +
    +static int addi9036_enum_mbus_code(struct v4l2_subdev *sd,
    +				   struct v4l2_subdev_pad_config *cfg,
    +				   struct v4l2_subdev_mbus_code_enum *code)
    +{
    +	if (code->index > 0)
    +		return -EINVAL;
    +
    +	code->code = MEDIA_BUS_FMT_SBGGR12_1X12;
    +
    +	return 0;
    +}
    +
    +static int addi9036_enum_frame_size(struct v4l2_subdev *subdev,
    +				    struct v4l2_subdev_pad_config *cfg,
    +				    struct v4l2_subdev_frame_size_enum *fse)
    +{
    +	if (fse->code != MEDIA_BUS_FMT_SBGGR12_1X12)
    +		return -EINVAL;
    +
    +	if (fse->index >= ARRAY_SIZE(addi9036_mode_info_data))
    +		return -EINVAL;
    +
    +	fse->min_width = addi9036_mode_info_data[fse->index].width;
    +	fse->max_width = addi9036_mode_info_data[fse->index].width;
    +	fse->min_height = addi9036_mode_info_data[fse->index].height;
    +	fse->max_height = addi9036_mode_info_data[fse->index].height;
    +
    +	return 0;
    +}
    +
    +static struct v4l2_mbus_framefmt *
    +addi9036_get_pad_format(struct addi9036 *addi9036,
    +			struct v4l2_subdev_pad_config *cfg,
    +			unsigned int pad,
    +			enum v4l2_subdev_format_whence which)
    +{
    +	switch (which) {
    +	case V4L2_SUBDEV_FORMAT_TRY:
    +		return v4l2_subdev_get_try_format(&addi9036->sd, cfg, pad);
    +	case V4L2_SUBDEV_FORMAT_ACTIVE:
    +		return &addi9036->fmt;
    +	default:
    +		return NULL;
    +	}
    +}
    +
    +static int addi9036_get_format(struct v4l2_subdev *sd,
    +			       struct v4l2_subdev_pad_config *cfg,
    +			       struct v4l2_subdev_format *format)
    +{
    +	struct addi9036 *addi9036 = to_addi9036(sd);
    +	struct v4l2_mbus_framefmt *pad_format;
    +
    +	pad_format = addi9036_get_pad_format(addi9036, cfg, format->pad,
    +					     format->which);
    +
    +	if (!pad_format)
    +		return -EINVAL;
    +
    +	format->format = *pad_format;
    +
    +	return 0;
    +}
    +
    +static struct v4l2_rect *
    +addi9036_get_pad_crop(struct addi9036 *addi9036,
    +		      struct v4l2_subdev_pad_config *cfg,
    +		      unsigned int pad, enum v4l2_subdev_format_whence which)
    +{
    +	switch (which) {
    +	case V4L2_SUBDEV_FORMAT_TRY:
    +		return v4l2_subdev_get_try_crop(&addi9036->sd, cfg, pad);
    +	case V4L2_SUBDEV_FORMAT_ACTIVE:
    +		return &addi9036->crop;
    +	default:
    +		return NULL;
    +	}
    +}
    +
    +static const struct addi9036_mode_info *
    +addi9036_find_nearest_mode(unsigned int width, unsigned int height)
    +{
    +	unsigned int i;
    +
    +	for (i = ARRAY_SIZE(addi9036_mode_info_data) - 1; i > 0; i--) {
    +		if (addi9036_mode_info_data[i].width <= width &&
    +		    addi9036_mode_info_data[i].height <= height)
    +			break;
    +	}
    +
    +	return &addi9036_mode_info_data[i];
    +}
    +
    +static int addi9036_set_format(struct v4l2_subdev *sd,
    +			       struct v4l2_subdev_pad_config *cfg,
    +			       struct v4l2_subdev_format *format)
    +{
    +	struct addi9036 *addi9036 = to_addi9036(sd);
    +	struct v4l2_mbus_framefmt *framefmt;
    +	struct v4l2_rect *crop;
    +	const struct addi9036_mode_info *new_mode;
    +	int ret;
    +
    +	dev_dbg(addi9036->dev, "set_fmt: %x %dx%d\n",
    +		format->format.code, format->format.width,
    +		format->format.height);
    +
    +	crop = addi9036_get_pad_crop(addi9036, cfg, format->pad,
    +				     format->which);
    +
    +	if (!crop)
    +		return -EINVAL;
    +
    +	new_mode = addi9036_find_nearest_mode(format->format.width,
    +					      format->format.height);
    +	crop->width = new_mode->width;
    +	crop->height = new_mode->height;
    +
    +	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
    +		ret = v4l2_ctrl_s_ctrl_int64(addi9036->pixel_clock,
    +					     new_mode->pixel_clock);
    +		if (ret < 0)
    +			return ret;
    +
    +		ret = v4l2_ctrl_s_ctrl(addi9036->link_freq,
    +				       new_mode->link_freq_idx);
    +		if (ret < 0)
    +			return ret;
    +
    +		addi9036->current_mode = new_mode;
    +	}
    +
    +	framefmt = addi9036_get_pad_format(addi9036, cfg, format->pad,
    +					   format->which);
    +
    +	if (!framefmt)
    +		return -EINVAL;
    +
    +	framefmt->width = crop->width;
    +	framefmt->height = crop->height;
    +	framefmt->code = MEDIA_BUS_FMT_SBGGR12_1X12;
    +	framefmt->field = V4L2_FIELD_NONE;
    +	framefmt->colorspace = V4L2_COLORSPACE_SRGB;
    +
    +	format->format = *framefmt;
    +
    +	return 0;
    +}
    +
    +static int addi9036_entity_init_cfg(struct v4l2_subdev *subdev,
    +				    struct v4l2_subdev_pad_config *cfg)
    +{
    +	struct v4l2_subdev_format fmt = { 0 };
    +
    +	if (cfg)
    +		fmt.which = V4L2_SUBDEV_FORMAT_TRY;
    +	else
    +		fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
    +
    +	fmt.format.width = 640;
    +	fmt.format.height = 960;
    +
    +	addi9036_set_format(subdev, cfg, &fmt);
    +
    +	return 0;
    +}
    +
    +static int addi9036_get_selection(struct v4l2_subdev *sd,
    +				  struct v4l2_subdev_pad_config *cfg,
    +				  struct v4l2_subdev_selection *sel)
    +{
    +	struct addi9036 *addi9036 = to_addi9036(sd);
    +
    +	if (sel->target != V4L2_SEL_TGT_CROP)
    +		return -EINVAL;
    +
    +	sel->r = *addi9036_get_pad_crop(addi9036, cfg, sel->pad, sel->which);
    +
    +	return 0;
    +}
    +
    +static int addi9036_s_stream(struct v4l2_subdev *subdev, int enable)
    +{
    +	struct addi9036 *addi9036 = to_addi9036(subdev);
    +
    +	dev_dbg(addi9036->dev, "stream %d\n", enable);
    +
    +	return 0;
    +}
    +
    +static const struct v4l2_subdev_core_ops addi9036_core_ops = {
    +	.s_power	= addi9036_s_power,
    +};
    +
    +static const struct v4l2_subdev_video_ops addi9036_video_ops = {
    +	.s_stream	= addi9036_s_stream,
    +};
    +
    +static const struct v4l2_subdev_pad_ops addi9036_subdev_pad_ops = {
    +	.init_cfg		= addi9036_entity_init_cfg,
    +	.enum_mbus_code		= addi9036_enum_mbus_code,
    +	.enum_frame_size	= addi9036_enum_frame_size,
    +	.get_fmt		= addi9036_get_format,
    +	.set_fmt		= addi9036_set_format,
    +	.get_selection		= addi9036_get_selection,
    +};
    +
    +static const struct v4l2_subdev_ops addi9036_subdev_ops = {
    +	.core	= &addi9036_core_ops,
    +	.video	= &addi9036_video_ops,
    +	.pad	= &addi9036_subdev_pad_ops,
    +};
    +
    +static int addi9036_probe(struct i2c_client *client,
    +			  const struct i2c_device_id *id)
    +{
    +	struct device *dev = &client->dev;
    +	struct fwnode_handle *endpoint;
    +	struct addi9036 *addi9036;
    +	int ret;
    +
    +	dev_dbg(dev, "%s: i2c addr = 0x%x\n", __func__, client->addr);
    +
    +	addi9036 = devm_kzalloc(dev, sizeof(struct addi9036), GFP_KERNEL);
    +	if (!addi9036)
    +		return -ENOMEM;
    +
    +	addi9036->dev = dev;
    +
    +	addi9036->regmap = devm_regmap_init_i2c(client,
    +						&addi9036_i2c_regmap_config);
    +	if (IS_ERR(addi9036->regmap)) {
    +		dev_err(dev, "Error initializing i2c regmap: %ld\n",
    +			PTR_ERR(addi9036->regmap));
    +		return PTR_ERR(addi9036->regmap);
    +	}
    +
    +	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
    +	if (!endpoint) {
    +		dev_err(dev, "endpoint node not found\n");
    +		return -EINVAL;
    +	}
    +
    +	ret = v4l2_fwnode_endpoint_parse(endpoint, &addi9036->ep);
    +	if (ret < 0) {
    +		dev_err(dev, "parsing endpoint node failed\n");
    +		return ret;
    +	}
    +	fwnode_handle_put(endpoint);
    +
    +	if (addi9036->ep.bus_type != V4L2_MBUS_CSI2) {
    +		dev_err(dev, "invalid bus type, must be MIPI CSI2\n");
    +		return -EINVAL;
    +	}
    +
    +	addi9036->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
    +	if (IS_ERR(addi9036->rst_gpio)) {
    +		dev_err(dev, "cannot get reset gpio\n");
    +		return PTR_ERR(addi9036->rst_gpio);
    +	}
    +
    +	mutex_init(&addi9036->power_lock);
    +
    +	v4l2_ctrl_handler_init(&addi9036->ctrls, 4);
    +
    +	addi9036->pixel_clock = v4l2_ctrl_new_std(&addi9036->ctrls,
    +						  &addi9036_ctrl_ops,
    +						  V4L2_CID_PIXEL_RATE,
    +						  1, INT_MAX, 1, 1);
    +	addi9036->link_freq = v4l2_ctrl_new_int_menu(&addi9036->ctrls,
    +						     &addi9036_ctrl_ops,
    +						     V4L2_CID_LINK_FREQ,
    +						     ARRAY_SIZE(
    +							     link_freq_tbl) - 1,
    +						     0, link_freq_tbl);
    +	if (addi9036->link_freq)
    +		addi9036->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
    +
    +	addi9036->set_chip_config = v4l2_ctrl_new_custom(&addi9036->ctrls,
    +						&addi9036_ctrl_chip_config,
    +						NULL);
    +
    +	addi9036->reg_read = v4l2_ctrl_new_custom(&addi9036->ctrls,
    +						  &addi9036_ctrl_reg_read,
    +						  NULL);
    +
    +	addi9036->sd.ctrl_handler = &addi9036->ctrls;
    +
    +	if (addi9036->ctrls.error) {
    +		dev_err(dev, "%s: control initialization error %d\n",
    +			__func__, addi9036->ctrls.error);
    +		ret = addi9036->ctrls.error;
    +		goto free_ctrl;
    +	}
    +
    +	client->flags |= I2C_CLIENT_SCCB;
    +	v4l2_i2c_subdev_init(&addi9036->sd, client, &addi9036_subdev_ops);
    +	addi9036->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
    +	addi9036->pad.flags = MEDIA_PAD_FL_SOURCE;
    +	addi9036->sd.dev = &client->dev;
    +	addi9036->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
    +
    +	ret = media_entity_pads_init(&addi9036->sd.entity, 1, &addi9036->pad);
    +
    +	if (ret < 0) {
    +		dev_err(dev, "could not register media entity\n");
    +		goto free_ctrl;
    +	}
    +
    +	ret = addi9036_s_power(&addi9036->sd, true);
    +	if (ret < 0) {
    +		dev_err(dev, "could not power up addi9036\n");
    +		goto free_entity;
    +	}
    +
    +	dev_info(dev, "addi9036 detected at address 0x%02x\n", client->addr);
    +
    +	ret = addi9036_s_power(&addi9036->sd, false);
    +	if (ret < 0) {
    +		dev_err(dev, "could not power down addi9036\n");
    +		goto free_entity;
    +	}
    +
    +	addi9036_entity_init_cfg(&addi9036->sd, NULL);
    +
    +	ret = v4l2_async_register_subdev(&addi9036->sd);
    +	if (ret < 0)
    +		dev_err(dev, "could not register v4l2 device\n");
    +
    +	return 0;
    +
    +free_entity:
    +	media_entity_cleanup(&addi9036->sd.entity);
    +free_ctrl:
    +	v4l2_ctrl_handler_free(&addi9036->ctrls);
    +	mutex_destroy(&addi9036->power_lock);
    +
    +	return ret;
    +}
    +
    +static int addi9036_remove(struct i2c_client *client)
    +{
    +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
    +	struct addi9036 *addi9036 = to_addi9036(sd);
    +
    +	v4l2_async_unregister_subdev(&addi9036->sd);
    +	media_entity_cleanup(&addi9036->sd.entity);
    +	v4l2_ctrl_handler_free(&addi9036->ctrls);
    +
    +	return 0;
    +}
    +
    +static const struct i2c_device_id addi9036_id[] = {
    +	{ "addi9036", 0 },
    +	{}
    +};
    +MODULE_DEVICE_TABLE(i2c, addi9036_id);
    +
    +static const struct of_device_id addi9036_of_match[] = {
    +	{ .compatible = "adi,addi9036" },
    +	{ /* sentinel */ }
    +};
    +MODULE_DEVICE_TABLE(of, addi9036_of_match);
    +
    +static struct i2c_driver addi9036_i2c_driver = {
    +	.driver			= {
    +		.of_match_table = addi9036_of_match,
    +		.name		= "addi9036",
    +	},
    +	.probe			= addi9036_probe,
    +	.remove			= addi9036_remove,
    +	.id_table		= addi9036_id,
    +};
    +
    +module_i2c_driver(addi9036_i2c_driver);
    +
    +MODULE_DESCRIPTION("Analog Devices ADDI9036 Camera Driver");
    +MODULE_LICENSE("GPL v2");
    diff --git a/drivers/media/platform/mxc/capture/mx6s_capture.c b/drivers/media/platform/mxc/capture/mx6s_capture.c
    index b69369ac0ab9..a0076a9f7f0b 100644
    --- a/drivers/media/platform/mxc/capture/mx6s_capture.c
    +++ b/drivers/media/platform/mxc/capture/mx6s_capture.c
    @@ -138,6 +138,7 @@
     #define BIT_CSI_ENABLE			(0x1 << 31)
     #define BIT_MIPI_DATA_FORMAT_RAW8		(0x2a << 25)
     #define BIT_MIPI_DATA_FORMAT_RAW10		(0x2b << 25)
    +#define BIT_MIPI_DATA_FORMAT_RAW12		(0x2c << 25)
     #define BIT_MIPI_DATA_FORMAT_YUV422_8B	(0x1e << 25)
     #define BIT_MIPI_DATA_FORMAT_MASK	(0x3F << 25)
     #define BIT_MIPI_DATA_FORMAT_OFFSET	25
    @@ -179,6 +180,8 @@
     #define NUM_FORMATS ARRAY_SIZE(formats)
     #define MX6SX_MAX_SENSORS    1
     
    +#define DEBUG
    +
     struct csi_signal_cfg_t {
     	unsigned data_width:3;
     	unsigned clk_mode:2;
    @@ -273,6 +276,12 @@ static struct mx6s_fmt formats[] = {
     		.pixelformat	= V4L2_PIX_FMT_SBGGR8,
     		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,
     		.bpp		= 1,
    +	}, {
    +		.name		= "RAWRGB12 (SBGGR12)",
    +		.fourcc		= V4L2_PIX_FMT_SBGGR12,
    +		.pixelformat	= V4L2_PIX_FMT_SBGGR12,
    +		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,
    +		.bpp		= 2,
     	}
     };
     
    @@ -844,6 +853,7 @@ static int mx6s_configure_csi(struct mx6s_csi_dev *csi_dev)
     	switch (csi_dev->fmt->pixelformat) {
     	case V4L2_PIX_FMT_YUV32:
     	case V4L2_PIX_FMT_SBGGR8:
    +	case V4L2_PIX_FMT_SBGGR12:
     		width = pix->width;
     		break;
     	case V4L2_PIX_FMT_UYVY:
    @@ -877,6 +887,9 @@ static int mx6s_configure_csi(struct mx6s_csi_dev *csi_dev)
     		case V4L2_PIX_FMT_SBGGR8:
     			cr18 |= BIT_MIPI_DATA_FORMAT_RAW8;
     			break;
    +		case V4L2_PIX_FMT_SBGGR12:
    +			cr18 |= BIT_MIPI_DATA_FORMAT_RAW12;
    +			break;
     		default:
     			pr_debug("   fmt not supported\n");
     			return -EINVAL;
    @@ -1447,7 +1460,6 @@ static int mx6s_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
     
     	if (pix->field != V4L2_FIELD_INTERLACED)
     		pix->field = V4L2_FIELD_NONE;
    -
     	pix->sizeimage = fmt->bpp * pix->height * pix->width;
     	pix->bytesperline = fmt->bpp * pix->width;
     
    @@ -1473,6 +1485,7 @@ static int mx6s_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
     	csi_dev->pix.width     = f->fmt.pix.width;
     	csi_dev->pix.height    = f->fmt.pix.height;
     	csi_dev->pix.sizeimage = f->fmt.pix.sizeimage;
    +	csi_dev->pix.bytesperline = f->fmt.pix.bytesperline;
     	csi_dev->pix.field     = f->fmt.pix.field;
     	csi_dev->type          = f->type;
     	dev_dbg(csi_dev->dev, "set to pixelformat '%4.6s'\n",
    @@ -1646,8 +1659,15 @@ static int mx6s_vidioc_enum_framesizes(struct file *file, void *priv,
     	int ret;
     
     	fmt = format_by_fourcc(fsize->pixel_format);
    -	if (fmt->pixelformat != fsize->pixel_format)
    +	if (!fmt) {
    +		dev_err(csi_dev->dev, "Fourcc format (0x%08x) invalid.",
    +			fsize->pixel_format);
     		return -EINVAL;
    +	} else {
    +		if (fmt->pixelformat != fsize->pixel_format)
    +			return -EINVAL;
    +	}
    +
     	fse.code = fmt->mbus_code;
     
     	ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse);
    @@ -1688,8 +1708,15 @@ static int mx6s_vidioc_enum_frameintervals(struct file *file, void *priv,
     	int ret;
     
     	fmt = format_by_fourcc(interval->pixel_format);
    -	if (fmt->pixelformat != interval->pixel_format)
    +	if (!fmt) {
    +		dev_err(csi_dev->dev, "Fourcc format (0x%08x) invalid.",
    +			interval->pixel_format);
     		return -EINVAL;
    +	} else {
    +		if (fmt->pixelformat != interval->pixel_format)
    +			return -EINVAL;
    +	}
    +
     	fie.code = fmt->mbus_code;
     
     	ret = v4l2_subdev_call(sd, pad, enum_frame_interval, NULL, &fie);
    diff --git a/drivers/media/platform/mxc/capture/mxc_mipi_csi.c b/drivers/media/platform/mxc/capture/mxc_mipi_csi.c
    index a7964b7b8345..9266d47b842d 100644
    --- a/drivers/media/platform/mxc/capture/mxc_mipi_csi.c
    +++ b/drivers/media/platform/mxc/capture/mxc_mipi_csi.c
    @@ -331,6 +331,10 @@ static const struct csis_pix_format mipi_csis_formats[] = {
     		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
     		.fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8,
     		.data_alignment = 8,
    +	}, {
    +		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
    +		.fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12,
    +		.data_alignment = 16,
     	}
     };
     
    @@ -955,6 +959,13 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
     	return 0;
     }
     
    +static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
    +{
    +	struct csi_state *state = notifier_to_mipi_dev(notifier);
    +
    +	return v4l2_device_register_subdev_nodes(&state->v4l2_dev);
    +}
    +
     static int mipi_csis_parse_dt(struct platform_device *pdev,
     			    struct csi_state *state)
     {
    @@ -1029,6 +1040,7 @@ static int mipi_csis_subdev_host(struct csi_state *state)
     	state->subdev_notifier.subdevs = state->async_subdevs;
     	state->subdev_notifier.num_subdevs = 1;
     	state->subdev_notifier.bound = subdev_notifier_bound;
    +	state->subdev_notifier.complete = subdev_notifier_complete;
     
     	ret = v4l2_async_notifier_register(&state->v4l2_dev,
     					&state->subdev_notifier);
    diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
    index a692623e0236..6baaf22870cc 100644
    --- a/include/uapi/linux/v4l2-controls.h
    +++ b/include/uapi/linux/v4l2-controls.h
    @@ -190,6 +190,9 @@ enum v4l2_colorfx {
      * We reserve 16 controls for this driver. */
     #define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x1090)
     
    +/* The base for the addi9036 driver controls, 4 controls are reserved */
    +#define V4L2_CID_USER_ADDI9036_BASE		(V4L2_CID_DV_CLASS_BASE + 0x100)
    +
     /* MPEG-class control IDs */
     /* The MPEG controls are applicable to all codec controls
      * and the 'MPEG' part of the define is historical */
     

    And the SDK changes here:

    diff --git a/examples/imshow-jetson/main.cpp b/examples/imshow-jetson/main.cpp
    index 278120a..69ad64b 100644
    --- a/examples/imshow-jetson/main.cpp
    +++ b/examples/imshow-jetson/main.cpp
    @@ -48,7 +48,7 @@ aditof::Status fromFrameToDepthMat(aditof::Frame &frame, cv::Mat &mat) {
         aditof::FrameDetails frameDetails;
         frame.getDetails(frameDetails);
     
    -    const int frameHeight = static_cast<int>(frameDetails.height) / 2;
    +    const int frameHeight = static_cast<int>(frameDetails.height);
         const int frameWidth = static_cast<int>(frameDetails.width);
     
         uint16_t *depthData;
    @@ -67,7 +67,7 @@ aditof::Status fromFrameToIrMat(aditof::Frame &frame, cv::Mat &mat) {
         aditof::FrameDetails frameDetails;
         frame.getDetails(frameDetails);
     
    -    const int frameHeight = static_cast<int>(frameDetails.height) / 2;
    +    const int frameHeight = static_cast<int>(frameDetails.height);
         const int frameWidth = static_cast<int>(frameDetails.width);
     
         uint16_t *irData;
    diff --git a/sdk/src/connections/mipi/jetson/device_enumerator_jetson.cpp b/sdk/src/connections/mipi/jetson/device_enumerator_jetson.cpp
    index d3d572d..a62585d 100644
    --- a/sdk/src/connections/mipi/jetson/device_enumerator_jetson.cpp
    +++ b/sdk/src/connections/mipi/jetson/device_enumerator_jetson.cpp
    @@ -74,7 +74,7 @@ aditof::Status DeviceEnumeratorImpl::findDevices(
         // we've got the right device and is compatible with the SDK
         DeviceConstructionData devData;
         devData.deviceType = DeviceType::LOCAL;
    -    devData.driverPath = "/dev/video0";
    +    devData.driverPath = "/dev/video0;/dev/v4l-subdev0";
         devices.emplace_back(devData);
     
         DLOG(INFO) << "Looking at: " << devData.driverPath
    diff --git a/sdk/src/connections/mipi/jetson/target_definitions.h b/sdk/src/connections/mipi/jetson/target_definitions.h
    index 0d56596..546687d 100644
    --- a/sdk/src/connections/mipi/jetson/target_definitions.h
    +++ b/sdk/src/connections/mipi/jetson/target_definitions.h
    @@ -32,14 +32,14 @@
     #ifndef TARGET_DEFINITIONS_H
     #define TARGET_DEFINITIONS_H
     
    -static const char *TEMP_SENSOR_DEV_PATH = "/dev/i2c-6";
    -static const char *EEPROM_DEV_PATH = "/sys/bus/i2c/devices/6-0056/eeprom";
    +static const char *TEMP_SENSOR_DEV_PATH = "/dev/i2c-1";
    +static const char *EEPROM_DEV_PATH = "/sys/bus/i2c/devices/1-0056/eeprom";
     
     static const char *EEPROM_REPLACEMENT_PATH =
         "/home/analog/workspace/github/aditof_sdk/chicony_firmware.bin";
     
     static const char *TEMP_SENSOR_REPLACEMENT_DEV_PATH = "";
     
    -static const char *CAPTURE_DEVICE_NAME = "vi-output, addi9036 6-0064";
    +static const char *CAPTURE_DEVICE_NAME = "i.MX6S_CSI";
     
     #endif // TARGET_DEFINITIONS_H
    diff --git a/sdk/src/connections/mipi/local_device.cpp b/sdk/src/connections/mipi/local_device.cpp
    index ec19e87..a3d3df8 100644
    --- a/sdk/src/connections/mipi/local_device.cpp
    +++ b/sdk/src/connections/mipi/local_device.cpp
    @@ -310,12 +310,12 @@ LocalDevice::getAvailableFrameTypes(std::vector<aditof::FrameDetails> &types) {
         types.push_back(details);
     
         details.width = 640;
    -    details.height = 960;
    +    details.height = 480;
         details.type = "depth_only";
         types.push_back(details);
     
         details.width = 640;
    -    details.height = 960;
    +    details.height = 480;
         details.type = "ir_only";
         types.push_back(details);
     
    @@ -357,6 +357,7 @@ aditof::Status LocalDevice::setFrameType(const aditof::FrameDetails &details) {
         fmt.type = m_implData->videoBuffersType;
         fmt.fmt.pix.width = details.width;
         fmt.fmt.pix.height = details.height;
    +    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR12;
     
         if (xioctl(m_implData->fd, VIDIOC_S_FMT, &fmt) == -1) {
             LOG(WARNING) << "Setting Pixel Format error, errno: " << errno