From b4b425991f0514d4ad03524b576ea2ab2011ed4e Mon Sep 17 00:00:00 2001
From: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
Date: Sun, 14 May 2017 15:20:01 +0300
Subject: [PATCH 22/61] Gen3: LVDS cameras

This add Gen3 LVDS cameras support:
- deserializers: MAX9286, DS90UB954/960/964
- cameras: ov10635, ov490+ov10640, ov495+OV2775, ar0132, ar0220,
           ap0101+ar014x, ov2775

Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
 drivers/media/i2c/soc_camera/Kconfig             |   18 +
 drivers/media/i2c/soc_camera/Makefile            |    4 +
 drivers/media/i2c/soc_camera/ap0101_ar014x.c     |  589 +++++++
 drivers/media/i2c/soc_camera/ap0101_ar014x.h     |   28 +
 drivers/media/i2c/soc_camera/ar0132.c            |  566 +++++++
 drivers/media/i2c/soc_camera/ar0132.h            |  213 +++
 drivers/media/i2c/soc_camera/ar0220.c            |  532 +++++++
 drivers/media/i2c/soc_camera/ar0220.h            |   43 +
 drivers/media/i2c/soc_camera/max9286.c           |  694 ++++++++
 drivers/media/i2c/soc_camera/max9286.h           |  244 +++
 drivers/media/i2c/soc_camera/ov10635.c           |  760 +++++++++
 drivers/media/i2c/soc_camera/ov10635.h           | 1139 +++++++++++++
 drivers/media/i2c/soc_camera/ov10635_debug.h     |   54 +
 drivers/media/i2c/soc_camera/ov106xx.c           |  139 ++
 drivers/media/i2c/soc_camera/ov2775.c            |  528 +++++++
 drivers/media/i2c/soc_camera/ov2775.h            | 1841 ++++++++++++++++++++++
 drivers/media/i2c/soc_camera/ov490_ov10640.c     | 1134 +++++++++++++
 drivers/media/i2c/soc_camera/ov490_ov10640.h     |  102 ++
 drivers/media/i2c/soc_camera/ov495_ov2775.c      |  640 ++++++++
 drivers/media/i2c/soc_camera/ov495_ov2775.h      |   23 +
 drivers/media/i2c/soc_camera/ti9x4.c             |  520 ++++++
 drivers/media/i2c/soc_camera/ti9x4.h             |  156 ++
 drivers/media/platform/soc_camera/rcar_csi2.c    |  330 ++--
 drivers/media/platform/soc_camera/rcar_vin.c     |  309 +++-
 drivers/media/platform/soc_camera/soc_camera.c   |   17 +-
 drivers/media/platform/soc_camera/soc_mediabus.c |   16 +
 drivers/media/v4l2-core/v4l2-async.c             |    2 +
 include/media/drv-intf/soc_mediabus.h            |    3 +
 include/media/soc_camera.h                       |    1 +
 29 files changed, 10512 insertions(+), 133 deletions(-)
 create mode 100644 drivers/media/i2c/soc_camera/ap0101_ar014x.c
 create mode 100644 drivers/media/i2c/soc_camera/ap0101_ar014x.h
 create mode 100644 drivers/media/i2c/soc_camera/ar0132.c
 create mode 100644 drivers/media/i2c/soc_camera/ar0132.h
 create mode 100644 drivers/media/i2c/soc_camera/ar0220.c
 create mode 100644 drivers/media/i2c/soc_camera/ar0220.h
 create mode 100644 drivers/media/i2c/soc_camera/max9286.c
 create mode 100644 drivers/media/i2c/soc_camera/max9286.h
 create mode 100644 drivers/media/i2c/soc_camera/ov10635.c
 create mode 100644 drivers/media/i2c/soc_camera/ov10635.h
 create mode 100644 drivers/media/i2c/soc_camera/ov10635_debug.h
 create mode 100644 drivers/media/i2c/soc_camera/ov106xx.c
 create mode 100644 drivers/media/i2c/soc_camera/ov2775.c
 create mode 100644 drivers/media/i2c/soc_camera/ov2775.h
 create mode 100644 drivers/media/i2c/soc_camera/ov490_ov10640.c
 create mode 100644 drivers/media/i2c/soc_camera/ov490_ov10640.h
 create mode 100644 drivers/media/i2c/soc_camera/ov495_ov2775.c
 create mode 100644 drivers/media/i2c/soc_camera/ov495_ov2775.h
 create mode 100644 drivers/media/i2c/soc_camera/ti9x4.c
 create mode 100644 drivers/media/i2c/soc_camera/ti9x4.h

diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig
index 72b3698..d30a3f9 100644
--- a/drivers/media/i2c/soc_camera/Kconfig
+++ b/drivers/media/i2c/soc_camera/Kconfig
@@ -76,3 +76,21 @@ config SOC_CAMERA_TW9910
 	depends on SOC_CAMERA && I2C
 	help
 	  This is a tw9910 video driver
+
+config SOC_CAMERA_MAX9286
+	tristate "max9286 GMSL support"
+	depends on SOC_CAMERA && I2C
+	help
+	  This is a MAXIM max9286 GMSL driver
+
+config SOC_CAMERA_TI9X4
+	tristate "ti9x4 FPDLink3 support"
+	depends on SOC_CAMERA && I2C
+	help
+	  This is an Texas Instruments ti9X4 FPDLink3 driver
+
+config SOC_CAMERA_OV106XX
+	tristate "ov106xx camera support"
+	depends on SOC_CAMERA && I2C
+	help
+	  This is a runtime detected GMSL/FPDLink3 sensors driver
diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile
index faa2df8..1134c03 100644
--- a/drivers/media/i2c/soc_camera/Makefile
+++ b/drivers/media/i2c/soc_camera/Makefile
@@ -10,3 +10,7 @@ obj-$(CONFIG_SOC_CAMERA_OV9640)		+= ov9640.o
 obj-$(CONFIG_SOC_CAMERA_OV9740)		+= ov9740.o
 obj-$(CONFIG_SOC_CAMERA_RJ54N1)		+= rj54n1cb0c.o
 obj-$(CONFIG_SOC_CAMERA_TW9910)		+= tw9910.o
+obj-$(CONFIG_SOC_CAMERA_MAX9286)	+= max9286.o
+obj-$(CONFIG_SOC_CAMERA_TI9X4)		+= ti9x4.o
+obj-$(CONFIG_SOC_CAMERA_OV106XX)	+= ov106xx.o
+
diff --git a/drivers/media/i2c/soc_camera/ap0101_ar014x.c b/drivers/media/i2c/soc_camera/ap0101_ar014x.c
new file mode 100644
index 0000000..cae0b19
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ap0101_ar014x.c
@@ -0,0 +1,589 @@
+/*
+ * ON Semiconductor AP0101-AR014X sensor camera driver
+ *
+ * Copyright (C) 2018 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+
+#include "ap0101_ar014x.h"
+
+#define AP0101_I2C_ADDR		0x5d
+
+#define AP0101_PID		0x0000
+#define AP0101_VERSION_REG	0x0160
+
+#define AP0101_MEDIA_BUS_FMT	MEDIA_BUS_FMT_YUYV8_2X8
+
+static void ap0101_otp_id_read(struct i2c_client *client);
+
+struct ap0101_priv {
+	struct v4l2_subdev		sd;
+	struct v4l2_ctrl_handler	hdl;
+	struct media_pad		pad;
+	struct v4l2_rect		rect;
+	int				init_complete;
+	u8				id[6];
+	int				exposure;
+	int				gain;
+	int				autogain;
+	/* serializers */
+	int				max9286_addr;
+	int				max9271_addr;
+	int				port;
+	int				gpio_resetb;
+	int				gpio_fsin;
+};
+
+static inline struct ap0101_priv *to_ap0101(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ap0101_priv, sd);
+}
+
+static void ap0101_s_port(struct i2c_client *client, int fwd_en)
+{
+	struct ap0101_priv *priv = to_ap0101(client);
+	int tmp_addr;
+
+	if (priv->max9286_addr) {
+		tmp_addr = client->addr;
+		client->addr = priv->max9286_addr;				/* Deserializer I2C address */
+		reg8_write(client, 0x0a, fwd_en ? 0x11 << priv->port : 0);	/* Enable/disable reverse/forward control for this port */
+		usleep_range(5000, 5500);					/* wait 5ms */
+		client->addr = tmp_addr;
+	};
+}
+
+static int ap0101_set_regs(struct i2c_client *client,
+			  const struct ap0101_reg *regs, int nr_regs)
+{
+	int i;
+
+	for (i = 0; i < nr_regs; i++) {
+		if (regs[i].reg == AP0101_DELAY) {
+			mdelay(regs[i].val);
+			continue;
+		}
+
+		reg16_write16(client, regs[i].reg, regs[i].val);
+	}
+
+	return 0;
+}
+
+static u16 ap0101_ar014x_read(struct i2c_client *client, u16 addr)
+{
+	u16 reg_val = 0;
+
+	reg16_write16(client, 0x0040, 0x8d00);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write16(client, 0xfc00, addr);
+	reg16_write16(client, 0xfc02, 0x0200); /* 2 bytes */
+	reg16_write16(client, 0x0040, 0x8d05);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write16(client, 0x0040, 0x8d08);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_read16(client, 0xfc00, &reg_val);
+	reg16_write16(client, 0x0040, 0x8d02);
+	usleep_range(100, 150); /* wait 100 us */
+
+	return reg_val;
+}
+
+static void ap0101_ar014x_write(struct i2c_client *client, u16 addr, u16 val)
+{
+	reg16_write16(client, 0x0040, 0x8d00);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write16(client, 0xfc00, addr);
+	reg16_write16(client, 0xfc02, 0x0200 | (val >> 8)); /* 2 bytes */
+	reg16_write16(client, 0xfc04, (val & 0xff) << 8);
+	reg16_write16(client, 0x0040, 0x8d06);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write16(client, 0x0040, 0x8d08);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write16(client, 0x0040, 0x8d02);
+	usleep_range(100, 150); /* wait 100 us */
+}
+
+static int ap0101_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static int ap0101_get_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ap0101_priv *priv = to_ap0101(client);
+
+	if (format->pad)
+		return -EINVAL;
+
+	mf->width = priv->rect.width;
+	mf->height = priv->rect.height;
+	mf->code = AP0101_MEDIA_BUS_FMT;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int ap0101_set_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+
+	mf->code = AP0101_MEDIA_BUS_FMT;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		cfg->try_fmt = *mf;
+
+	return 0;
+}
+
+static int ap0101_enum_mbus_code(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->pad || code->index > 0)
+		return -EINVAL;
+
+	code->code = AP0101_MEDIA_BUS_FMT;
+
+	return 0;
+}
+
+static int ap0101_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ap0101_priv *priv = to_ap0101(client);
+
+	ap0101_otp_id_read(client);
+
+	memcpy(edid->edid, priv->id, 6);
+
+	edid->edid[6] = 0xff;
+	edid->edid[7] = client->addr;
+	edid->edid[8] = AP0101_VERSION_REG >> 8;
+	edid->edid[9] = AP0101_VERSION_REG & 0xff;
+
+	return 0;
+}
+
+static int ap0101_set_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct v4l2_rect *rect = &sel->r;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ap0101_priv *priv = to_ap0101(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
+	    sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	rect->left = ALIGN(rect->left, 2);
+	rect->top = ALIGN(rect->top, 2);
+	rect->width = ALIGN(rect->width, 2);
+	rect->height = ALIGN(rect->height, 2);
+
+	if ((rect->left + rect->width > AP0101_MAX_WIDTH) ||
+	    (rect->top + rect->height > AP0101_MAX_HEIGHT))
+		*rect = priv->rect;
+
+	priv->rect.left = rect->left;
+	priv->rect.top = rect->top;
+	priv->rect.width = rect->width;
+	priv->rect.height = rect->height;
+
+	return 0;
+}
+
+static int ap0101_get_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ap0101_priv *priv = to_ap0101(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = AP0101_MAX_WIDTH;
+		sel->r.height = AP0101_MAX_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = AP0101_MAX_WIDTH;
+		sel->r.height = AP0101_MAX_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP:
+		sel->r = priv->rect;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ap0101_g_mbus_config(struct v4l2_subdev *sd,
+			       struct v4l2_mbus_config *cfg)
+{
+	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	cfg->type = V4L2_MBUS_CSI2;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ap0101_g_register(struct v4l2_subdev *sd,
+			    struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+	u16 val = 0;
+
+	ret = reg16_read16(client, (u16)reg->reg, &val);
+	if (ret < 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = sizeof(u16);
+
+	return 0;
+}
+
+static int ap0101_s_register(struct v4l2_subdev *sd,
+			    const struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	return reg16_write16(client, (u16)reg->reg, (u16)reg->val);
+}
+#endif
+
+static struct v4l2_subdev_core_ops ap0101_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = ap0101_g_register,
+	.s_register = ap0101_s_register,
+#endif
+};
+
+static int ap0101_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ap0101_priv *priv = to_ap0101(client);
+	int ret = -EINVAL;
+
+	if (!priv->init_complete)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+	case V4L2_CID_CONTRAST:
+	case V4L2_CID_SATURATION:
+	case V4L2_CID_HUE:
+	case V4L2_CID_GAMMA:
+	case V4L2_CID_SHARPNESS:
+	case V4L2_CID_AUTOGAIN:
+	case V4L2_CID_GAIN:
+	case V4L2_CID_EXPOSURE:
+	case V4L2_CID_HFLIP:
+	case V4L2_CID_VFLIP:
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ap0101_ctrl_ops = {
+	.s_ctrl = ap0101_s_ctrl,
+};
+
+static struct v4l2_subdev_video_ops ap0101_video_ops = {
+	.s_stream	= ap0101_s_stream,
+	.g_mbus_config	= ap0101_g_mbus_config,
+};
+
+static const struct v4l2_subdev_pad_ops ap0101_subdev_pad_ops = {
+	.get_edid	= ap0101_get_edid,
+	.enum_mbus_code	= ap0101_enum_mbus_code,
+	.get_selection	= ap0101_get_selection,
+	.set_selection	= ap0101_set_selection,
+	.get_fmt	= ap0101_get_fmt,
+	.set_fmt	= ap0101_set_fmt,
+};
+
+static struct v4l2_subdev_ops ap0101_subdev_ops = {
+	.core	= &ap0101_core_ops,
+	.video	= &ap0101_video_ops,
+	.pad	= &ap0101_subdev_pad_ops,
+};
+
+static void ap0101_otp_id_read(struct i2c_client *client)
+{
+	struct ap0101_priv *priv = to_ap0101(client);
+	int i;
+
+	/* read camera id from ar014x OTP memory */
+	ap0101_ar014x_write(client, 0x3054, 0x400);
+	ap0101_ar014x_write(client, 0x304a, 0x110);
+	usleep_range(25000, 25500); /* wait 25 ms */
+
+	for (i = 0; i < 6; i += 2) {
+		/* first 4 bytes are equal on all ar014x */
+		priv->id[i]     = ap0101_ar014x_read(client, 0x3800 + i + 4) >> 8;
+		priv->id[i + 1] = ap0101_ar014x_read(client, 0x3800 + i + 4) & 0xff;
+	}
+}
+
+static ssize_t ap0101_otp_id_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev));
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ap0101_priv *priv = to_ap0101(client);
+
+	ap0101_otp_id_read(client);
+
+	return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+			priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+}
+
+static DEVICE_ATTR(otp_id_ap0101, S_IRUGO, ap0101_otp_id_show, NULL);
+
+static int ap0101_initialize(struct i2c_client *client)
+{
+	struct ap0101_priv *priv = to_ap0101(client);
+	u16 pid = 0;
+	int ret = 0;
+
+	ap0101_s_port(client, 1);
+
+	/* check and show model ID */
+	reg16_read16(client, AP0101_PID, &pid);
+
+	if (pid != AP0101_VERSION_REG) {
+		dev_dbg(&client->dev, "Product ID error %x\n", pid);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	/* Program wizard registers */
+	ap0101_set_regs(client, ap0101_regs_wizard, ARRAY_SIZE(ap0101_regs_wizard));
+	/* Read OTP IDs */
+	ap0101_otp_id_read(client);
+
+	dev_info(&client->dev, "ap0101 PID %x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n",
+		 pid, AP0101_MAX_WIDTH, AP0101_MAX_HEIGHT, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+err:
+	ap0101_s_port(client, 0);
+
+	return ret;
+}
+
+static int ap0101_parse_dt(struct device_node *np, struct ap0101_priv *priv)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+	int i;
+	struct device_node *endpoint = NULL, *rendpoint = NULL;
+	int tmp_addr = 0;
+
+	for (i = 0; ; i++) {
+		endpoint = of_graph_get_next_endpoint(np, endpoint);
+		if (!endpoint)
+			break;
+
+		of_node_put(endpoint);
+
+		rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0);
+		if (!rendpoint)
+			continue;
+
+		if (!of_property_read_u32(rendpoint, "max9271-addr", &priv->max9271_addr) &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->max9286_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port))
+			break;
+	}
+
+	if (!priv->max9286_addr) {
+		dev_err(&client->dev, "deserializer does not present for AP0101\n");
+		return -EINVAL;
+	}
+
+	ap0101_s_port(client, 1);
+
+	/* setup I2C translator address */
+	tmp_addr = client->addr;
+	if (priv->max9286_addr) {
+		client->addr = priv->max9271_addr;			/* Serializer I2C address */
+
+		reg8_write(client, 0x09, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x0A, AP0101_I2C_ADDR << 1);		/* Sensor native I2C address */
+		usleep_range(2000, 2500);				/* wait 2ms */
+	};
+	client->addr = tmp_addr;
+
+	mdelay(10);
+
+	return 0;
+}
+
+static int ap0101_probe(struct i2c_client *client,
+		       const struct i2c_device_id *did)
+{
+	struct ap0101_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&priv->sd, client, &ap0101_subdev_ops);
+	priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	priv->exposure = 0x100;
+	priv->gain = 0x100;
+	priv->autogain = 1;
+	v4l2_ctrl_handler_init(&priv->hdl, 4);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_BRIGHTNESS, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_CONTRAST, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_SATURATION, 0, 7, 1, 2);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_HUE, 0, 23, 1, 12);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_GAMMA, -128, 128, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_SHARPNESS, 0, 10, 1, 3);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_AUTOGAIN, 0, 1, 1, priv->autogain);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_GAIN, 0, 0xffff, 1, priv->gain);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_EXPOSURE, 0, 0xffff, 1, priv->exposure);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_HFLIP, 0, 1, 1, 1);
+	v4l2_ctrl_new_std(&priv->hdl, &ap0101_ctrl_ops,
+			  V4L2_CID_VFLIP, 0, 1, 1, 0);
+	priv->sd.ctrl_handler = &priv->hdl;
+
+	ret = priv->hdl.error;
+	if (ret)
+		goto cleanup;
+
+	v4l2_ctrl_handler_setup(&priv->hdl);
+
+	priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+	priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad);
+	if (ret < 0)
+		goto cleanup;
+
+	ret = ap0101_parse_dt(client->dev.of_node, priv);
+	if (ret)
+		goto cleanup;
+
+	ret = ap0101_initialize(client);
+	if (ret < 0)
+		goto cleanup;
+
+	priv->rect.left = 0;
+	priv->rect.top = 0;
+	priv->rect.width = AP0101_MAX_WIDTH;
+	priv->rect.height = AP0101_MAX_HEIGHT;
+
+	ret = v4l2_async_register_subdev(&priv->sd);
+	if (ret)
+		goto cleanup;
+
+	if (device_create_file(&client->dev, &dev_attr_otp_id_ap0101) != 0) {
+		dev_err(&client->dev, "sysfs otp_id entry creation failed\n");
+		goto cleanup;
+	}
+
+	priv->init_complete = 1;
+
+	return 0;
+
+cleanup:
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+#ifdef CONFIG_SOC_CAMERA_AP0101
+	v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
+		client->addr, client->adapter->name);
+#endif
+	return ret;
+}
+
+static int ap0101_remove(struct i2c_client *client)
+{
+	struct ap0101_priv *priv = i2c_get_clientdata(client);
+
+	device_remove_file(&client->dev, &dev_attr_otp_id_ap0101);
+	v4l2_async_unregister_subdev(&priv->sd);
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+
+	return 0;
+}
+
+#ifdef CONFIG_SOC_CAMERA_AP0101
+static const struct i2c_device_id ap0101_id[] = {
+	{ "ap0101", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ap0101_id);
+
+static const struct of_device_id ap0101_of_ids[] = {
+	{ .compatible = "aptina,ap0101", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ap0101_of_ids);
+
+static struct i2c_driver ap0101_i2c_driver = {
+	.driver	= {
+		.name		= "ap0101",
+		.of_match_table	= ap0101_of_ids,
+	},
+	.probe		= ap0101_probe,
+	.remove		= ap0101_remove,
+	.id_table	= ap0101_id,
+};
+
+module_i2c_driver(ap0101_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for AP0101");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/i2c/soc_camera/ap0101_ar014x.h b/drivers/media/i2c/soc_camera/ap0101_ar014x.h
new file mode 100644
index 0000000..16599a1
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ap0101_ar014x.h
@@ -0,0 +1,28 @@
+/*
+ * ON Semiconductor ap0101-ar014x sensor camera wizard 1280x720@30/UYVY/BT601/8bit
+ *
+ * Copyright (C) 2018 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#define AP0101_MAX_WIDTH	1280
+#define AP0101_MAX_HEIGHT	720
+
+#define AP0101_DELAY		0xffff
+
+struct ap0101_reg {
+	u16	reg;
+	u16	val;
+};
+
+static const struct ap0101_reg ap0101_regs_wizard[] = {
+/* enable FSIN */
+{0xc88c, 0x0303},
+{0xfc00, 0x2800},
+{0x0040, 0x8100},
+{AP0101_DELAY, 100},
+};
diff --git a/drivers/media/i2c/soc_camera/ar0132.c b/drivers/media/i2c/soc_camera/ar0132.c
new file mode 100644
index 0000000..a7ee868
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ar0132.c
@@ -0,0 +1,566 @@
+/*
+ * ON Semiconductor AR0132 sensor camera driver
+ *
+ * Copyright (C) 2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+
+#include "ar0132.h"
+
+#define AR0132_I2C_ADDR		0x18
+//#define AR0132_I2C_ADDR		0x50 // eeprom
+
+#define AR0132_PID		0x3000
+#define AR0132_VERSION_REG	0x2400
+
+#define AR0132_MEDIA_BUS_FMT	MEDIA_BUS_FMT_SBGGR12_1X12
+
+struct ar0132_priv {
+	struct v4l2_subdev		sd;
+	struct v4l2_ctrl_handler	hdl;
+	struct media_pad		pad;
+	struct v4l2_rect		rect;
+	int				init_complete;
+	u8				id[6];
+	int				exposure;
+	int				gain;
+	int				autogain;
+	int				dvp_order;
+	/* serializers */
+	int				max9286_addr;
+	int				max9271_addr;
+	int				ti9x4_addr;
+	int				ti9x3_addr;
+	int				port;
+	int				gpio_resetb;
+	int				gpio_fsin;
+
+};
+
+static inline struct ar0132_priv *to_ar0132(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ar0132_priv, sd);
+}
+
+static void ar0132_s_port(struct i2c_client *client, int fwd_en)
+{
+	struct ar0132_priv *priv = to_ar0132(client);
+	int tmp_addr;
+
+	if (priv->max9286_addr) {
+		tmp_addr = client->addr;
+		client->addr = priv->max9286_addr;				/* Deserializer I2C address */
+		reg8_write(client, 0x0a, fwd_en ? 0x11 << priv->port : 0);	/* Enable/disable reverse/forward control for this port */
+		usleep_range(5000, 5500);					/* wait 5ms */
+		client->addr = tmp_addr;
+	};
+}
+
+static int ar0132_set_regs(struct i2c_client *client,
+			  const struct ar0132_reg *regs, int nr_regs)
+{
+	int i;
+
+	for (i = 0; i < nr_regs; i++) {
+		if (regs[i].reg == AR0132_DELAY) {
+			mdelay(regs[i].val);
+			continue;
+		}
+
+		reg16_write16(client, regs[i].reg, regs[i].val);
+	}
+
+	return 0;
+}
+
+static int ar0132_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static int ar0132_get_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0132_priv *priv = to_ar0132(client);
+
+	if (format->pad)
+		return -EINVAL;
+
+	mf->width = priv->rect.width;
+	mf->height = priv->rect.height;
+	mf->code = AR0132_MEDIA_BUS_FMT;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int ar0132_set_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+
+	mf->code = AR0132_MEDIA_BUS_FMT;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		cfg->try_fmt = *mf;
+
+	return 0;
+}
+
+static int ar0132_enum_mbus_code(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->pad || code->index > 0)
+		return -EINVAL;
+
+	code->code = AR0132_MEDIA_BUS_FMT;
+
+	return 0;
+}
+
+static int ar0132_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0132_priv *priv = to_ar0132(client);
+
+	memcpy(edid->edid, priv->id, 6);
+
+	edid->edid[6] = 0xff;
+	edid->edid[7] = client->addr;
+	edid->edid[8] = AR0132_VERSION_REG >> 8;
+	edid->edid[9] = AR0132_VERSION_REG & 0xff;
+
+	return 0;
+}
+
+static int ar0132_set_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct v4l2_rect *rect = &sel->r;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0132_priv *priv = to_ar0132(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
+	    sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	rect->left = ALIGN(rect->left, 2);
+	rect->top = ALIGN(rect->top, 2);
+	rect->width = ALIGN(rect->width, 2);
+	rect->height = ALIGN(rect->height, 2);
+
+	if ((rect->left + rect->width > AR0132_MAX_WIDTH) ||
+	    (rect->top + rect->height > AR0132_MAX_HEIGHT))
+		*rect = priv->rect;
+
+	priv->rect.left = rect->left;
+	priv->rect.top = rect->top;
+	priv->rect.width = rect->width;
+	priv->rect.height = rect->height;
+
+	return 0;
+}
+
+static int ar0132_get_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0132_priv *priv = to_ar0132(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = AR0132_MAX_WIDTH;
+		sel->r.height = AR0132_MAX_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = AR0132_MAX_WIDTH;
+		sel->r.height = AR0132_MAX_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP:
+		sel->r = priv->rect;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ar0132_g_mbus_config(struct v4l2_subdev *sd,
+			       struct v4l2_mbus_config *cfg)
+{
+	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	cfg->type = V4L2_MBUS_CSI2;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ar0132_g_register(struct v4l2_subdev *sd,
+			    struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+	u16 val = 0;
+
+	ret = reg16_read16(client, (u16)reg->reg, &val);
+	if (ret < 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = sizeof(u16);
+
+	return 0;
+}
+
+static int ar0132_s_register(struct v4l2_subdev *sd,
+			    const struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	return reg16_write16(client, (u16)reg->reg, (u16)reg->val);
+}
+#endif
+
+static struct v4l2_subdev_core_ops ar0132_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = ar0132_g_register,
+	.s_register = ar0132_s_register,
+#endif
+};
+
+static int ar0132_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0132_priv *priv = to_ar0132(client);
+	int ret = -EINVAL;
+
+	if (!priv->init_complete)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+	case V4L2_CID_CONTRAST:
+	case V4L2_CID_SATURATION:
+	case V4L2_CID_HUE:
+	case V4L2_CID_GAMMA:
+	case V4L2_CID_SHARPNESS:
+	case V4L2_CID_AUTOGAIN:
+	case V4L2_CID_GAIN:
+	case V4L2_CID_EXPOSURE:
+	case V4L2_CID_HFLIP:
+	case V4L2_CID_VFLIP:
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ar0132_ctrl_ops = {
+	.s_ctrl = ar0132_s_ctrl,
+};
+
+static struct v4l2_subdev_video_ops ar0132_video_ops = {
+	.s_stream	= ar0132_s_stream,
+	.g_mbus_config	= ar0132_g_mbus_config,
+};
+
+static const struct v4l2_subdev_pad_ops ar0132_subdev_pad_ops = {
+	.get_edid	= ar0132_get_edid,
+	.enum_mbus_code	= ar0132_enum_mbus_code,
+	.get_selection	= ar0132_get_selection,
+	.set_selection	= ar0132_set_selection,
+	.get_fmt	= ar0132_get_fmt,
+	.set_fmt	= ar0132_set_fmt,
+};
+
+static struct v4l2_subdev_ops ar0132_subdev_ops = {
+	.core	= &ar0132_core_ops,
+	.video	= &ar0132_video_ops,
+	.pad	= &ar0132_subdev_pad_ops,
+};
+
+static void ar0132_otp_id_read(struct i2c_client *client)
+{
+}
+
+static ssize_t ar0132_otp_id_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev));
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0132_priv *priv = to_ar0132(client);
+
+	return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+			priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+}
+
+static DEVICE_ATTR(otp_id_ar0132, S_IRUGO, ar0132_otp_id_show, NULL);
+
+static int ar0132_initialize(struct i2c_client *client)
+{
+	struct ar0132_priv *priv = to_ar0132(client);
+	u16 val = 0;
+	u16 pid = 0;
+	int ret = 0;
+
+	ar0132_s_port(client, 1);
+
+	/* check and show model ID */
+	reg16_read16(client, AR0132_PID, &pid);
+
+	if (pid != AR0132_VERSION_REG) {
+		dev_dbg(&client->dev, "Product ID error %x\n", pid);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	/* Program wizard registers */
+	ar0132_set_regs(client, ar0132_regs_wizard, ARRAY_SIZE(ar0132_regs_wizard));
+
+	/* Enable stream */
+	reg16_read16(client, 0x301a, &val);	// read inital reset_register value
+	val |= (1 << 2);			// Set streamOn bit
+	reg16_write16(client, 0x301a, val);	// Start Streaming
+
+	/* Read OTP IDs */
+	ar0132_otp_id_read(client);
+
+	dev_info(&client->dev, "ar0132 PID %x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n",
+		 pid, AR0132_MAX_WIDTH, AR0132_MAX_HEIGHT, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+err:
+	ar0132_s_port(client, 0);
+
+	return ret;
+}
+
+static int ar0132_parse_dt(struct device_node *np, struct ar0132_priv *priv)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+	int i;
+	struct device_node *endpoint = NULL, *rendpoint = NULL;
+	int tmp_addr = 0;
+
+	for (i = 0; ; i++) {
+		endpoint = of_graph_get_next_endpoint(np, endpoint);
+		if (!endpoint)
+			break;
+
+		of_node_put(endpoint);
+
+		of_property_read_u32(endpoint, "dvp-order", &priv->dvp_order);
+
+		rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0);
+		if (!rendpoint)
+			continue;
+
+		if (!of_property_read_u32(rendpoint, "max9271-addr", &priv->max9271_addr) &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->max9286_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port))
+			break;
+
+		if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) &&
+		    !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti9x4") &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti9x4_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port))
+			break;
+	}
+
+	if (!priv->max9286_addr && !priv->ti9x4_addr) {
+		dev_err(&client->dev, "deserializer does not present for AR0132\n");
+		return -EINVAL;
+	}
+
+	ar0132_s_port(client, 1);
+
+	/* setup I2C translator address */
+	tmp_addr = client->addr;
+	if (priv->max9286_addr) {
+		client->addr = priv->max9271_addr;			/* Serializer I2C address */
+
+		reg8_write(client, 0x09, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x0A, AR0132_I2C_ADDR << 1);		/* Sensor native I2C address */
+		usleep_range(2000, 2500);				/* wait 2ms */
+	};
+	if (priv->ti9x4_addr) {
+		client->addr = priv->ti9x4_addr;			/* Deserializer I2C address */
+
+		reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */
+		usleep_range(2000, 2500);				/* wait 2ms */
+		reg8_write(client, 0x65, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x5d, AR0132_I2C_ADDR << 1);		/* Sensor native I2C address */
+
+		reg8_write(client, 0x6e, 0xa9);				/* GPIO0 - reset, GPIO1 - fsin */
+	}
+	client->addr = tmp_addr;
+
+	mdelay(10);
+
+	return 0;
+}
+
+static int ar0132_probe(struct i2c_client *client,
+		       const struct i2c_device_id *did)
+{
+	struct ar0132_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&priv->sd, client, &ar0132_subdev_ops);
+	priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	priv->exposure = 0x100;
+	priv->gain = 0x100;
+	priv->autogain = 1;
+	v4l2_ctrl_handler_init(&priv->hdl, 4);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_BRIGHTNESS, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_CONTRAST, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_SATURATION, 0, 7, 1, 2);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_HUE, 0, 23, 1, 12);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_GAMMA, -128, 128, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_SHARPNESS, 0, 10, 1, 3);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_AUTOGAIN, 0, 1, 1, priv->autogain);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_GAIN, 0, 0xffff, 1, priv->gain);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_EXPOSURE, 0, 0xffff, 1, priv->exposure);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_HFLIP, 0, 1, 1, 1);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0132_ctrl_ops,
+			  V4L2_CID_VFLIP, 0, 1, 1, 0);
+	priv->sd.ctrl_handler = &priv->hdl;
+
+	ret = priv->hdl.error;
+	if (ret)
+		goto cleanup;
+
+	v4l2_ctrl_handler_setup(&priv->hdl);
+
+	priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+	priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad);
+	if (ret < 0)
+		goto cleanup;
+
+	ret = ar0132_parse_dt(client->dev.of_node, priv);
+	if (ret)
+		goto cleanup;
+
+	ret = ar0132_initialize(client);
+	if (ret < 0)
+		goto cleanup;
+
+	priv->rect.left = 0;
+	priv->rect.top = 0;
+	priv->rect.width = AR0132_MAX_WIDTH;
+	priv->rect.height = AR0132_MAX_HEIGHT;
+
+	ret = v4l2_async_register_subdev(&priv->sd);
+	if (ret)
+		goto cleanup;
+
+	if (device_create_file(&client->dev, &dev_attr_otp_id_ar0132) != 0) {
+		dev_err(&client->dev, "sysfs otp_id entry creation failed\n");
+		goto cleanup;
+	}
+
+	priv->init_complete = 1;
+
+	return 0;
+
+cleanup:
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+#ifdef CONFIG_SOC_CAMERA_AR0132
+	v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
+		client->addr, client->adapter->name);
+#endif
+	return ret;
+}
+
+static int ar0132_remove(struct i2c_client *client)
+{
+	struct ar0132_priv *priv = i2c_get_clientdata(client);
+
+	device_remove_file(&client->dev, &dev_attr_otp_id_ar0132);
+	v4l2_async_unregister_subdev(&priv->sd);
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+
+	return 0;
+}
+
+#ifdef CONFIG_SOC_CAMERA_AR0132
+static const struct i2c_device_id ar0132_id[] = {
+	{ "ar0132", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ar0132_id);
+
+static const struct of_device_id ar0132_of_ids[] = {
+	{ .compatible = "aptina,ar0132", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ar0132_of_ids);
+
+static struct i2c_driver ar0132_i2c_driver = {
+	.driver	= {
+		.name		= "ar0132",
+		.of_match_table	= ar0132_of_ids,
+	},
+	.probe		= ar0132_probe,
+	.remove		= ar0132_remove,
+	.id_table	= ar0132_id,
+};
+
+module_i2c_driver(ar0132_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for AR0132");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/i2c/soc_camera/ar0132.h b/drivers/media/i2c/soc_camera/ar0132.h
new file mode 100644
index 0000000..bafa193
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ar0132.h
@@ -0,0 +1,213 @@
+/*
+ * ON Semiconductor AR0132 sensor camera wizard 1110x620@30/BGGR/BT601/12bit
+ *
+ * Copyright (C) 2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+//#define AR0132_DISPLAY_PATTERN_FIXED
+//#define AR0132_DISPLAY_PATTERN_COLOR_BAR
+
+#define AR0132_EMBEDDED_LINE
+
+#define AR0132_MAX_WIDTH		1665 // (1110*3/2)
+#define AR0132_MAX_HEIGHT		624
+
+#define AR0132_DELAY			0xffff
+
+#define AR0132_MAX_ROI_DIM_X		1288
+#define AR0132_MAX_ROI_DIM_Y		968
+#define AR0132_InfoLines		4
+
+#define AR0132_ROI_DIM_X		1110 // 1104
+#define AR0132_ROI_DIM_Y		620 // AR0132_MAX_HEIGHT
+
+#define AR0132_ROI_Y_START		0x00AE
+#define AR0132_ROI_X_START		0x005C
+#define AR0132_ROI_Y_END		AR0132_ROI_Y_START+AR0132_ROI_DIM_Y-1
+#define AR0132_ROI_X_END		AR0132_ROI_X_START+AR0132_ROI_DIM_X-1
+
+#define AR0132_FrameLength_Lines	0x029E
+#define AR0132_LineLength_Ticks		0x06B6
+
+#define AR0132_PLL_VT_Pix_Clk_Div	0x0008
+#define AR0132_PLL_VT_Sys_Clk_Div	0x0001
+#define AR0132_PLL_Pre_Clk_Div		0x0004
+#define AR0132_PLL_Multiplier		0x003C
+
+#define AR0132_DigitalTest		0x2002
+
+struct ar0132_reg {
+	u16	reg;
+	u16	val;
+};
+
+static const struct ar0132_reg ar0132_regs_wizard[] = {
+{0x301A, 0x0001},	// reset
+{AR0132_DELAY, 100},
+{0x301A, 0x10D8},	// Stream off and setup parallel
+{0x3070, 0x0001},
+{0x3070, 0x0000},	//  1: Solid color test pattern,
+			//  2: Full color bar test pattern,
+			//  3: Fade to grey color bar test pattern,
+			//256: Walking 1 test pattern (12 bit)
+#ifdef AR0132_DISPLAY_PATTERN_FIXED
+{0x3070, 0x0001},
+{0x3072, 0x0123},	// R
+{0x3074, 0x0456},	// G(GR row)
+{0x3076, 0x0abc},	// B
+{0x3078, 0x0def},	// G(GB row)
+#endif
+#ifdef AR0132_DISPLAY_PATTERN_COLOR_BAR
+{0x3070, 0x0002},
+#endif
+{AR0132_DELAY, 250},
+// patch begin
+{0x3088, 0x8000},
+{0x3086, 0x0025}, {0x3086, 0x5050}, {0x3086, 0x2D26}, {0x3086, 0x0828}, {0x3086, 0x0D17}, {0x3086, 0x0926}, {0x3086, 0x0028}, {0x3086, 0x0526},
+{0x3086, 0xA728}, {0x3086, 0x0725}, {0x3086, 0x8080}, {0x3086, 0x2925}, {0x3086, 0x0040}, {0x3086, 0x2702}, {0x3086, 0x1616}, {0x3086, 0x2706},
+{0x3086, 0x1736}, {0x3086, 0x26A6}, {0x3086, 0x1703}, {0x3086, 0x26A4}, {0x3086, 0x171F}, {0x3086, 0x2805}, {0x3086, 0x2620}, {0x3086, 0x2804},
+{0x3086, 0x2520}, {0x3086, 0x2027}, {0x3086, 0x0017}, {0x3086, 0x1D25}, {0x3086, 0x0020}, {0x3086, 0x1F17}, {0x3086, 0x1028}, {0x3086, 0x0519},
+{0x3086, 0x1703}, {0x3086, 0x2706}, {0x3086, 0x1703}, {0x3086, 0x1741}, {0x3086, 0x2660}, {0x3086, 0x17AE}, {0x3086, 0x2500}, {0x3086, 0x9027},
+{0x3086, 0x0026}, {0x3086, 0x1828}, {0x3086, 0x002E}, {0x3086, 0x2A28}, {0x3086, 0x081C}, {0x3086, 0x1470}, {0x3086, 0x7003}, {0x3086, 0x1470},
+{0x3086, 0x7004}, {0x3086, 0x1470}, {0x3086, 0x7005}, {0x3086, 0x1470}, {0x3086, 0x7009}, {0x3086, 0x170C}, {0x3086, 0x0014}, {0x3086, 0x0020},
+{0x3086, 0x2300}, {0x3086, 0x1400}, {0x3086, 0x5003}, {0x3086, 0x1400}, {0x3086, 0x2003}, {0x3086, 0x1400}, {0x3086, 0x5022}, {0x3086, 0x0414},
+{0x3086, 0x0020}, {0x3086, 0x0414}, {0x3086, 0x0050}, {0x3086, 0x0514}, {0x3086, 0x0020}, {0x3086, 0x2405}, {0x3086, 0x1400}, {0x3086, 0x5001},
+{0x3086, 0x2550}, {0x3086, 0x502D}, {0x3086, 0x2608}, {0x3086, 0x280D}, {0x3086, 0x1709}, {0x3086, 0x2600}, {0x3086, 0x2805}, {0x3086, 0x26A7},
+{0x3086, 0x2807}, {0x3086, 0x2580}, {0x3086, 0x8029}, {0x3086, 0x2500}, {0x3086, 0x4027}, {0x3086, 0x0216}, {0x3086, 0x1627}, {0x3086, 0x0617},
+{0x3086, 0x3626}, {0x3086, 0xA617}, {0x3086, 0x0326}, {0x3086, 0xA417}, {0x3086, 0x1F28}, {0x3086, 0x0526}, {0x3086, 0x2028}, {0x3086, 0x0425},
+{0x3086, 0x2020}, {0x3086, 0x2700}, {0x3086, 0x171D}, {0x3086, 0x2500}, {0x3086, 0x2020}, {0x3086, 0x1710}, {0x3086, 0x2805}, {0x3086, 0x1A17},
+{0x3086, 0x0327}, {0x3086, 0x0617}, {0x3086, 0x0317}, {0x3086, 0x4126}, {0x3086, 0x6017}, {0x3086, 0xAE25}, {0x3086, 0x0090}, {0x3086, 0x2700},
+{0x3086, 0x2618}, {0x3086, 0x2800}, {0x3086, 0x2E2A}, {0x3086, 0x2808}, {0x3086, 0x1D05}, {0x3086, 0x1470}, {0x3086, 0x7009}, {0x3086, 0x1720},
+{0x3086, 0x1400}, {0x3086, 0x2024}, {0x3086, 0x1400}, {0x3086, 0x5002}, {0x3086, 0x2550}, {0x3086, 0x502D}, {0x3086, 0x2608}, {0x3086, 0x280D},
+{0x3086, 0x1709}, {0x3086, 0x2600}, {0x3086, 0x2805}, {0x3086, 0x26A7}, {0x3086, 0x2807}, {0x3086, 0x2580}, {0x3086, 0x8029}, {0x3086, 0x2500},
+{0x3086, 0x4027}, {0x3086, 0x0216}, {0x3086, 0x1627}, {0x3086, 0x0617}, {0x3086, 0x3626}, {0x3086, 0xA617}, {0x3086, 0x0326}, {0x3086, 0xA417},
+{0x3086, 0x1F28}, {0x3086, 0x0526}, {0x3086, 0x2028}, {0x3086, 0x0425}, {0x3086, 0x2020}, {0x3086, 0x2700}, {0x3086, 0x171D}, {0x3086, 0x2500},
+{0x3086, 0x2021}, {0x3086, 0x1710}, {0x3086, 0x2805}, {0x3086, 0x1B17}, {0x3086, 0x0327}, {0x3086, 0x0617}, {0x3086, 0x0317}, {0x3086, 0x4126},
+{0x3086, 0x6017}, {0x3086, 0xAE25}, {0x3086, 0x0090}, {0x3086, 0x2700}, {0x3086, 0x2618}, {0x3086, 0x2800}, {0x3086, 0x2E2A}, {0x3086, 0x2808},
+{0x3086, 0x1E17}, {0x3086, 0x0A05}, {0x3086, 0x1470}, {0x3086, 0x7009}, {0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1616},
+{0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1616},
+{0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1616}, {0x3086, 0x1400}, {0x3086, 0x2024}, {0x3086, 0x1400}, {0x3086, 0x502B},
+{0x3086, 0x302C}, {0x3086, 0x2C2C}, {0x3086, 0x2C00}, {0x3086, 0x0225}, {0x3086, 0x5050}, {0x3086, 0x2D26}, {0x3086, 0x0828}, {0x3086, 0x0D17},
+{0x3086, 0x0926}, {0x3086, 0x0028}, {0x3086, 0x0526}, {0x3086, 0xA728}, {0x3086, 0x0725}, {0x3086, 0x8080}, {0x3086, 0x2917}, {0x3086, 0x0525},
+{0x3086, 0x0040}, {0x3086, 0x2702}, {0x3086, 0x1616}, {0x3086, 0x2706}, {0x3086, 0x1736}, {0x3086, 0x26A6}, {0x3086, 0x1703}, {0x3086, 0x26A4},
+{0x3086, 0x171F}, {0x3086, 0x2805}, {0x3086, 0x2620}, {0x3086, 0x2804}, {0x3086, 0x2520}, {0x3086, 0x2027}, {0x3086, 0x0017}, {0x3086, 0x1E25},
+{0x3086, 0x0020}, {0x3086, 0x2117}, {0x3086, 0x1028}, {0x3086, 0x051B}, {0x3086, 0x1703}, {0x3086, 0x2706}, {0x3086, 0x1703}, {0x3086, 0x1747},
+{0x3086, 0x2660}, {0x3086, 0x17AE}, {0x3086, 0x2500}, {0x3086, 0x9027}, {0x3086, 0x0026}, {0x3086, 0x1828}, {0x3086, 0x002E}, {0x3086, 0x2A28},
+{0x3086, 0x081E}, {0x3086, 0x0831}, {0x3086, 0x1440}, {0x3086, 0x4014}, {0x3086, 0x2020}, {0x3086, 0x1410}, {0x3086, 0x1034}, {0x3086, 0x1400},
+{0x3086, 0x1014}, {0x3086, 0x0020}, {0x3086, 0x1400}, {0x3086, 0x4013}, {0x3086, 0x1802}, {0x3086, 0x1470}, {0x3086, 0x7004}, {0x3086, 0x1470},
+{0x3086, 0x7003}, {0x3086, 0x1470}, {0x3086, 0x7017}, {0x3086, 0x2002}, {0x3086, 0x1400}, {0x3086, 0x2002}, {0x3086, 0x1400}, {0x3086, 0x5004},
+{0x3086, 0x1400}, {0x3086, 0x2004}, {0x3086, 0x1400}, {0x3086, 0x5022}, {0x3086, 0x0314}, {0x3086, 0x0020}, {0x3086, 0x0314}, {0x3086, 0x0050},
+{0x3086, 0x2C2C}, {0x3086, 0x2C2C},
+{0x309E, 0x0186},
+{0x309E, 0x0186},
+// patch end
+{AR0132_DELAY, 250},
+{0x301A, 0x10D8},	// WR= RESET_REGISTER, 0x10D8 - stop streaming
+{0x3082, 0x0028},	// Set   HiDy OPERATION_MODE_CTRL(A) Requested integration time ratio (T2 to T3):  8  &  (T1 t0 T2): 16
+{0x3084, 0x0028},	// Set   HiDy OPERATION_MODE_CTRL(B) Requested integration time ratio (T2 to T3): 16  &  (T1 t0 T2): 16
+{0x301E, 0x00C8},	// set datapedestal to 200 to avoid clipping near saturation
+{0x3EDA, 0x0F03},	// Set vln_dac to 0x3 as recommended by Sergey
+{0x3EDE, 0xC007},
+{0x3ED8, 0x01EF},	// Vrst_low = +1
+{0x3EE2, 0xA46B},
+{0x3EE0, 0x067D},	// enable anti eclipse and adjust setting for high conversion gain
+{0x3EDC, 0x0070},	// adjust anti eclipse setting for low conversion gain
+{0x3044, 0x0404},	// disable digital row noise correction and cancels TX during column correction
+{0x3EE6, 0x4303},	// Helps with column noise at low light
+{0x3EE4, 0xD208},	// enable analog row noise correction
+{0x3ED6, 0x00BD},
+{0x3EE6, 0x8303},	// improves low light FPN
+{0x30E4, 0x6372},	// ADC settings to improve noise performance
+{0x30E2, 0x7253},
+{0x30E0, 0x5470},
+{0x30E6, 0xC4CC},
+{0x30E8, 0x8050},
+{AR0132_DELAY, 250},
+{0x3058, 0x003F},	// WR= BLUE_GAIN, 0x003F
+{0x3014, 0},		// Fine_IT_Time(A)
+{0x3002, AR0132_ROI_Y_START}, // WR= Y_ADDR_START_(A)
+{0x3004, AR0132_ROI_X_START}, // WR= X_ADDR_START_(A)
+{0x3006, AR0132_ROI_Y_END}, // WR= Y_ADDR_END_(A)
+{0x3008, AR0132_ROI_X_END}, // WR= X_ADDR_END_(A)
+{0x300A, AR0132_FrameLength_Lines}, // WR= FRAME_LENGTH_LINES_(A)
+{0x3018, 0},		// Fine_IT_Time(B)
+{0x308C, AR0132_ROI_Y_START}, // Y_ADDR_START_(B)
+{0x308A, AR0132_ROI_X_START}, // X_ADDR_START_(B)
+{0x3090, AR0132_ROI_Y_END}, // Y_ADDR_END_(B)
+{0x308E, AR0132_ROI_X_END}, // X_ADDR_END_(B)
+{0x30AA, AR0132_FrameLength_Lines}, // FRAME_LENGTH_LINES_(B)
+{0x300C, AR0132_LineLength_Ticks}, // Line Length
+{0x301A, 0x10D8},	// Disable Streaming and setup parallel
+{0x31D0, 0x0001},	// Set to 12 bits
+{0x3028, 0x0010},	// ROW_SPEED = 16
+{0x302A, AR0132_PLL_VT_Pix_Clk_Div},
+{0x302C, AR0132_PLL_VT_Sys_Clk_Div},
+{0x302E, AR0132_PLL_Pre_Clk_Div},
+{0x3030, AR0132_PLL_Multiplier},
+{0x3032, 0x0000},	// SCALING_MODE = 0
+{0x3040, 0xC000},	// READ_MODE    = read_mode_vert_flip    | read_mode_horiz_mirror
+{0x3044, 0x0404},	// Dark Control = 1028
+{0x30A6, 0x0001},	// Y Odd Inc. (A) = 1
+{0x30A8, 0x0001},	// Y Odd Inc. (B) = 1
+{0x30B0, AR0132_DigitalTest},
+{AR0132_DELAY, 100},
+#ifdef AR0132_EMBEDDED_LINE
+{0x3064, 0x1982},	// Embedded Data on
+#else
+{0x3064, 0x1802},	// Embedded Data off
+#endif
+{0x3100, 0x0084},	// WR= AECTRLREG,
+{0x3190, 0x6BA0},
+{0x3194, 0x0E74},
+{0x3196, 0x0ED8},
+{0x3198, 0x0FA0},
+{0x319E, 0x5040},	// resetvalue
+{0x31A2, 0x0FA0},
+//FrontCamera Specific Section
+//Common
+#ifdef AR0132_EMBEDDED_LINE
+{0x3064, 0x1982},
+#else
+{0x3064, 0x1802},
+#endif
+{0x30B4, 0x0011},
+{0x30ba, 0x0008},
+{0x3180, 0xE000},
+{0x3182, 0x012C},
+{0x3190, 0x6BA0},
+{0x3194, 0x0E74},
+{0x3196, 0x0ED8},
+{0x3198, 0x0FA0},
+{0x319E, 0x5040},
+{0x31A2, 0x0FA0},
+//Context A:0
+{0x3012, 0x0021},
+{0x3014, 0x0000},
+{0x30A6, 0x0001},
+{0x3056, 0x0008},
+{0x3058, 0x0008},
+{0x305A, 0x0008},
+{0x305C, 0x0008},
+{0x305E, 0x0008},
+{0x3082, 0x0014},
+//Context B:0
+{0x3016, 0x007F},
+{0x3018, 0x0000},
+{0x30A8, 0x0001},
+{0x30BC, 0x0020},
+{0x30BE, 0x0020},
+{0x30C0, 0x0020},
+{0x30C2, 0x0020},
+{0x30C4, 0x0020},
+{0x3084, 0x0028},
+//not covered
+{0x301E, 0x00C8},
+{0x3044, 0x0404},
+{0x31D0, 0x0001},
+{0x30B0, 0x2002},
+};
diff --git a/drivers/media/i2c/soc_camera/ar0220.c b/drivers/media/i2c/soc_camera/ar0220.c
new file mode 100644
index 0000000..e00fe4a
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ar0220.c
@@ -0,0 +1,532 @@
+/*
+ * ON Semiconductor AR0220 sensor camera driver
+ *
+ * Copyright (C) 2017-2018 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+
+#include "ar0220.h"
+
+#define AR0220_I2C_ADDR		0x10
+//#define AR0220_I2C_ADDR		0x54 // eeprom
+
+#define AR0220_PID		0x3000
+#define AR0220_VERSION_REG	0x0C54
+
+#define AR0220_MEDIA_BUS_FMT	MEDIA_BUS_FMT_SBGGR8_1X8
+
+struct ar0220_priv {
+	struct v4l2_subdev		sd;
+	struct v4l2_ctrl_handler	hdl;
+	struct media_pad		pad;
+	struct v4l2_rect		rect;
+	int				init_complete;
+	u8				id[6];
+	int				exposure;
+	int				gain;
+	int				autogain;
+	/* serializers */
+	int				ti9x4_addr;
+	int				ti9x3_addr;
+	int				port;
+	int				gpio_resetb;
+	int				gpio_fsin;
+
+};
+
+static inline struct ar0220_priv *to_ar0220(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ar0220_priv, sd);
+}
+
+static int ar0220_set_regs(struct i2c_client *client,
+			  const struct ar0220_reg *regs, int nr_regs)
+{
+	int i;
+
+	for (i = 0; i < nr_regs; i++) {
+		if (regs[i].reg == AR0220_DELAY) {
+			mdelay(regs[i].val);
+			continue;
+		}
+
+		reg16_write16(client, regs[i].reg, regs[i].val);
+	}
+
+	return 0;
+}
+
+static int ar0220_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static int ar0220_get_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0220_priv *priv = to_ar0220(client);
+
+	if (format->pad)
+		return -EINVAL;
+
+	mf->width = priv->rect.width;
+	mf->height = priv->rect.height;
+	mf->code = AR0220_MEDIA_BUS_FMT;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int ar0220_set_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+
+	mf->code = AR0220_MEDIA_BUS_FMT;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		cfg->try_fmt = *mf;
+
+	return 0;
+}
+
+static int ar0220_enum_mbus_code(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->pad || code->index > 0)
+		return -EINVAL;
+
+	code->code = AR0220_MEDIA_BUS_FMT;
+
+	return 0;
+}
+
+static int ar0220_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0220_priv *priv = to_ar0220(client);
+
+	memcpy(edid->edid, priv->id, 6);
+
+	edid->edid[6] = 0xff;
+	edid->edid[7] = client->addr;
+	edid->edid[8] = AR0220_VERSION_REG >> 8;
+	edid->edid[9] = AR0220_VERSION_REG & 0xff;
+
+	return 0;
+}
+
+static int ar0220_set_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct v4l2_rect *rect = &sel->r;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0220_priv *priv = to_ar0220(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
+	    sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	rect->left = ALIGN(rect->left, 2);
+	rect->top = ALIGN(rect->top, 2);
+	rect->width = ALIGN(rect->width, 2);
+	rect->height = ALIGN(rect->height, 2);
+
+	if ((rect->left + rect->width > AR0220_MAX_WIDTH) ||
+	    (rect->top + rect->height > AR0220_MAX_HEIGHT))
+		*rect = priv->rect;
+
+	priv->rect.left = rect->left;
+	priv->rect.top = rect->top;
+	priv->rect.width = rect->width;
+	priv->rect.height = rect->height;
+
+	return 0;
+}
+
+static int ar0220_get_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0220_priv *priv = to_ar0220(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = AR0220_MAX_WIDTH;
+		sel->r.height = AR0220_MAX_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = AR0220_MAX_WIDTH;
+		sel->r.height = AR0220_MAX_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP:
+		sel->r = priv->rect;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ar0220_g_mbus_config(struct v4l2_subdev *sd,
+			       struct v4l2_mbus_config *cfg)
+{
+	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	cfg->type = V4L2_MBUS_CSI2;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ar0220_g_register(struct v4l2_subdev *sd,
+			    struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+	u16 val = 0;
+
+	ret = reg16_read16(client, (u16)reg->reg, &val);
+	if (ret < 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = sizeof(u16);
+
+	return 0;
+}
+
+static int ar0220_s_register(struct v4l2_subdev *sd,
+			    const struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	return reg16_write16(client, (u16)reg->reg, (u16)reg->val);
+}
+#endif
+
+static struct v4l2_subdev_core_ops ar0220_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = ar0220_g_register,
+	.s_register = ar0220_s_register,
+#endif
+};
+
+static int ar0220_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0220_priv *priv = to_ar0220(client);
+	int ret = -EINVAL;
+
+	if (!priv->init_complete)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+	case V4L2_CID_CONTRAST:
+	case V4L2_CID_SATURATION:
+	case V4L2_CID_HUE:
+	case V4L2_CID_GAMMA:
+	case V4L2_CID_SHARPNESS:
+	case V4L2_CID_AUTOGAIN:
+	case V4L2_CID_GAIN:
+	case V4L2_CID_EXPOSURE:
+	case V4L2_CID_HFLIP:
+	case V4L2_CID_VFLIP:
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ar0220_ctrl_ops = {
+	.s_ctrl = ar0220_s_ctrl,
+};
+
+static struct v4l2_subdev_video_ops ar0220_video_ops = {
+	.s_stream	= ar0220_s_stream,
+	.g_mbus_config	= ar0220_g_mbus_config,
+};
+
+static const struct v4l2_subdev_pad_ops ar0220_subdev_pad_ops = {
+	.get_edid	= ar0220_get_edid,
+	.enum_mbus_code	= ar0220_enum_mbus_code,
+	.get_selection	= ar0220_get_selection,
+	.set_selection	= ar0220_set_selection,
+	.get_fmt	= ar0220_get_fmt,
+	.set_fmt	= ar0220_set_fmt,
+};
+
+static struct v4l2_subdev_ops ar0220_subdev_ops = {
+	.core	= &ar0220_core_ops,
+	.video	= &ar0220_video_ops,
+	.pad	= &ar0220_subdev_pad_ops,
+};
+
+static void ar0220_otp_id_read(struct i2c_client *client)
+{
+}
+
+static ssize_t ar0220_otp_id_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev));
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ar0220_priv *priv = to_ar0220(client);
+
+	return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+			priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+}
+
+static DEVICE_ATTR(otp_id_ar0220, S_IRUGO, ar0220_otp_id_show, NULL);
+
+static int ar0220_initialize(struct i2c_client *client)
+{
+	struct ar0220_priv *priv = to_ar0220(client);
+	u16 val = 0;
+	u16 pid = 0;
+	int ret = 0;
+
+	/* check and show model ID */
+	reg16_read16(client, AR0220_PID, &pid);
+
+	if (pid != AR0220_VERSION_REG) {
+		dev_dbg(&client->dev, "Product ID error %x\n", pid);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	/* Program wizard registers */
+	ar0220_set_regs(client, ar0220_regs_wizard, ARRAY_SIZE(ar0220_regs_wizard));
+
+	/* Enable stream */
+	reg16_read16(client, 0x301a, &val);	// read inital reset_register value
+	val |= (1 << 2);			// Set streamOn bit
+	reg16_write16(client, 0x301a, val);	// Start Streaming
+
+	/* Read OTP IDs */
+	ar0220_otp_id_read(client);
+
+	dev_info(&client->dev, "ar0220 PID %x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n",
+		 pid, AR0220_MAX_WIDTH, AR0220_MAX_HEIGHT, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+err:
+	return ret;
+}
+
+static int ar0220_parse_dt(struct device_node *np, struct ar0220_priv *priv)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+	int i;
+	struct device_node *endpoint = NULL, *rendpoint = NULL;
+	int tmp_addr = 0;
+
+	for (i = 0; ; i++) {
+		endpoint = of_graph_get_next_endpoint(np, endpoint);
+		if (!endpoint)
+			break;
+
+		of_node_put(endpoint);
+
+		rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0);
+		if (!rendpoint)
+			continue;
+
+		if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) &&
+		    !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti9x4") &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti9x4_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port))
+			break;
+	}
+
+	if (!priv->ti9x4_addr) {
+		dev_err(&client->dev, "deserializer does not present\n");
+		return -EINVAL;
+	}
+
+	/* setup I2C translator address */
+	tmp_addr = client->addr;
+	if (priv->ti9x4_addr) {
+		client->addr = priv->ti9x4_addr;			/* Deserializer I2C address */
+
+		reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */
+		usleep_range(2000, 2500);				/* wait 2ms */
+		reg8_write(client, 0x65, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x5d, AR0220_I2C_ADDR << 1);		/* Sensor native I2C address */
+//		reg8_write(client, 0x6e, 0xa9);				/* GPIO0 - reset, GPIO1 - fsin */
+
+		client->addr = priv->ti9x3_addr;			/* Serializer I2C address */
+		reg8_write(client, 0x06, 0x41);				/* Set clock divider M */
+		reg8_write(client, 0x07, 0x25);				/* Set clock divider N = 27MHz */
+	}
+	client->addr = tmp_addr;
+
+	mdelay(10);
+
+	return 0;
+}
+
+static int ar0220_probe(struct i2c_client *client,
+		       const struct i2c_device_id *did)
+{
+	struct ar0220_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&priv->sd, client, &ar0220_subdev_ops);
+	priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	priv->exposure = 0x100;
+	priv->gain = 0x100;
+	priv->autogain = 1;
+	v4l2_ctrl_handler_init(&priv->hdl, 4);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_BRIGHTNESS, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_CONTRAST, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_SATURATION, 0, 7, 1, 2);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_HUE, 0, 23, 1, 12);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_GAMMA, -128, 128, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_SHARPNESS, 0, 10, 1, 3);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_AUTOGAIN, 0, 1, 1, priv->autogain);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_GAIN, 0, 0xffff, 1, priv->gain);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_EXPOSURE, 0, 0xffff, 1, priv->exposure);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_HFLIP, 0, 1, 1, 1);
+	v4l2_ctrl_new_std(&priv->hdl, &ar0220_ctrl_ops,
+			  V4L2_CID_VFLIP, 0, 1, 1, 0);
+	priv->sd.ctrl_handler = &priv->hdl;
+
+	ret = priv->hdl.error;
+	if (ret)
+		goto cleanup;
+
+	v4l2_ctrl_handler_setup(&priv->hdl);
+
+	priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+	priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad);
+	if (ret < 0)
+		goto cleanup;
+
+	ret = ar0220_parse_dt(client->dev.of_node, priv);
+	if (ret)
+		goto cleanup;
+
+	ret = ar0220_initialize(client);
+	if (ret < 0)
+		goto cleanup;
+
+	priv->rect.left = 0;
+	priv->rect.top = 0;
+	priv->rect.width = AR0220_MAX_WIDTH;
+	priv->rect.height = AR0220_MAX_HEIGHT;
+
+	ret = v4l2_async_register_subdev(&priv->sd);
+	if (ret)
+		goto cleanup;
+
+	if (device_create_file(&client->dev, &dev_attr_otp_id_ar0220) != 0) {
+		dev_err(&client->dev, "sysfs otp_id entry creation failed\n");
+		goto cleanup;
+	}
+
+	priv->init_complete = 1;
+
+	return 0;
+
+cleanup:
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+#ifdef CONFIG_SOC_CAMERA_AR0220
+	v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
+		client->addr, client->adapter->name);
+#endif
+	return ret;
+}
+
+static int ar0220_remove(struct i2c_client *client)
+{
+	struct ar0220_priv *priv = i2c_get_clientdata(client);
+
+	device_remove_file(&client->dev, &dev_attr_otp_id_ar0220);
+	v4l2_async_unregister_subdev(&priv->sd);
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+
+	return 0;
+}
+
+#ifdef CONFIG_SOC_CAMERA_AR0220
+static const struct i2c_device_id ar0220_id[] = {
+	{ "ar0220", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ar0220_id);
+
+static const struct of_device_id ar0220_of_ids[] = {
+	{ .compatible = "aptina,ar0220", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ar0220_of_ids);
+
+static struct i2c_driver ar0220_i2c_driver = {
+	.driver	= {
+		.name		= "ar0220",
+		.of_match_table	= ar0220_of_ids,
+	},
+	.probe		= ar0220_probe,
+	.remove		= ar0220_remove,
+	.id_table	= ar0220_id,
+};
+
+module_i2c_driver(ar0220_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for AR0220");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/i2c/soc_camera/ar0220.h b/drivers/media/i2c/soc_camera/ar0220.h
new file mode 100644
index 0000000..8ef557d
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ar0220.h
@@ -0,0 +1,43 @@
+/*
+ * ON Semiconductor AR0220 sensor camera wizard 1820x940@44/RCCB/MIPI
+ *
+ * Copyright (C) 2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+//#define AR0220_DISPLAY_PATTERN_FIXED
+//#define AR0220_DISPLAY_PATTERN_COLOR_BAR
+
+#define AR0220_MAX_WIDTH	3648 // (1820*2=3640) <- must be multiple of 16 - requred by R-CAR VIN
+#define AR0220_MAX_HEIGHT	944
+
+#define AR0220_DELAY		0xffff
+
+struct ar0220_reg {
+	u16	reg;
+	u16	val;
+};
+
+static const struct ar0220_reg ar0220_regs_wizard[] = {
+{0x301A, 0x0018}, // RESET_REGISTER
+{AR0220_DELAY, 500}, // Wait 500ms
+{0x3070, 0x0000}, //  1: Solid color test pattern,
+		  //  2: Full color bar test pattern,
+		  //  3: Fade to grey color bar test pattern,
+		  //256: Walking 1 test pattern (12 bit)
+{0x3072, 0x0123}, // R
+{0x3074, 0x0456}, // G(GR row)
+{0x3076, 0x0abc}, // B
+{0x3078, 0x0def}, // G(GB row)
+#ifdef AR0220_DISPLAY_PATTERN_FIXED
+{0x3070, 0x0001},
+#endif
+#ifdef AR0220_DISPLAY_PATTERN_COLOR_BAR
+{0x3070, 0x0002},
+#endif
+{AR0220_DELAY, 100}, // Wait 100ms
+};
diff --git a/drivers/media/i2c/soc_camera/max9286.c b/drivers/media/i2c/soc_camera/max9286.c
new file mode 100644
index 0000000..6a5d0889
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/max9286.c
@@ -0,0 +1,694 @@
+/*
+ * MAXIM max9286 GMSL driver
+ *
+ * Copyright (C) 2015-2018 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#include "max9286.h"
+
+#define MAXIM_I2C_I2C_SPEED_837KHZ	(0x7 << 2) /* 837kbps */
+#define MAXIM_I2C_I2C_SPEED_533KHZ	(0x6 << 2) /* 533kbps */
+#define MAXIM_I2C_I2C_SPEED_339KHZ	(0x5 << 2) /* 339 kbps */
+#define MAXIM_I2C_I2C_SPEED_173KHZ	(0x4 << 2) /* 174kbps */
+#define MAXIM_I2C_I2C_SPEED_105KHZ	(0x3 << 2) /* 105 kbps */
+#define MAXIM_I2C_I2C_SPEED_085KHZ	(0x2 << 2) /* 84.7 kbps */
+#define MAXIM_I2C_I2C_SPEED_028KHZ	(0x1 << 2) /* 28.3 kbps */
+#define MAXIM_I2C_I2C_SPEED		MAXIM_I2C_I2C_SPEED_339KHZ
+
+struct max9286_priv {
+	struct v4l2_subdev	sd[4];
+	struct fwnode_handle	*sd_fwnode[4];
+	int			des_addr;
+	int			des_quirk_addr; /* second MAX9286 on the same I2C bus */
+	int			links;
+	int			links_mask;
+	int			lanes;
+	int			csi_rate;
+	const char		*fsync_mode;
+	int			fsync_period;
+	char			pclk_rising_edge;
+	int			gpio_resetb;
+	int			active_low_resetb;
+	int			him;
+	int			hsync;
+	int			vsync;
+	int			timeout;
+	int			poc_delay;
+	atomic_t		use_count;
+	u32			csi2_outord;
+	struct i2c_client	*client;
+	int			max9271_addr_map[4];
+	int			ser_id;
+	struct gpio_desc	*poc_gpio[4]; /* PoC power supply */
+};
+
+static char fsync_mode_default[20] = "manual"; /* manual, automatic, semi-automatic, external */
+
+static int conf_link;
+module_param(conf_link, int, 0644);
+MODULE_PARM_DESC(conf_link, " Force configuration link. Used only if robust firmware flashing required (f.e. recovery)");
+
+static int poc_trig;
+module_param(poc_trig, int, 0644);
+MODULE_PARM_DESC(poc_trig, " Use PoC triggering during reverse channel setup. Useful on systems with dedicated PoC and unstable ser-des lock");
+
+static int him;
+module_param(him, int, 0644);
+MODULE_PARM_DESC(him, " Use High-Immunity mode (default: leagacy mode)");
+
+static int fsync_period;
+module_param(fsync_period, int, 0644);
+MODULE_PARM_DESC(fsync_period, " Frame sync period (default: 3.2MHz)");
+
+static int hsync;
+module_param(hsync, int, 0644);
+MODULE_PARM_DESC(hsync, " HSYNC invertion (default: 0 - not inverted)");
+
+static int vsync = 1;
+module_param(vsync, int, 0644);
+MODULE_PARM_DESC(vsync, " VSYNC invertion (default: 1 - inverted)");
+
+static int gpio_resetb;
+module_param(gpio_resetb, int, 0644);
+MODULE_PARM_DESC(gpio_resetb, " Serializer GPIO reset (default: 0 - not used)");
+
+static int active_low_resetb;
+module_param(active_low_resetb, int, 0644);
+MODULE_PARM_DESC(active_low_resetb, " Serializer GPIO reset level (default: 0 - active high)");
+
+static int poc_delay;
+module_param(poc_delay, int, 0644);
+MODULE_PARM_DESC(poc_delay, " Delay in ms after POC enable (default: 0 ms)");
+
+static char* ser_name(int id)
+{
+	switch (id) {
+	case MAX9271_ID:
+		return "MAX9271";
+	case MAX96705_ID:
+		return "MAX96705";
+	default:
+		return "unknown";
+	}
+}
+
+static void max9286_preinit(struct i2c_client *client, int addr)
+{
+	struct max9286_priv *priv = i2c_get_clientdata(client);
+
+	client->addr = addr;			/* MAX9286-CAMx I2C */
+	reg8_write(client, 0x0a, 0x00);		/* disable reverse control for all cams */
+	reg8_write(client, 0x00, 0x00);		/* disable all GMSL links [0:3] */
+	usleep_range(2000, 2500);		/* wait 2ms after any change of reverse channel settings */
+	reg8_write(client, 0x1c, priv->him ? 0xf4 : 0x04); /* high-immunity or legacy mode */
+}
+
+static void max9286_sensor_reset(struct i2c_client *client, int addr, int reset_on)
+{
+	struct max9286_priv *priv = i2c_get_clientdata(client);
+
+	if (priv->gpio_resetb < 1 || priv->gpio_resetb > 5)
+		return;
+
+	/* sensor reset/unreset */
+	client->addr = addr;					/* MAX9271-CAMx I2C */
+	reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | /* set GPIOn value to reset/unreset */
+		   ((priv->active_low_resetb ? BIT(priv->gpio_resetb) : 0) ^ reset_on));
+	reg8_write(client, 0x0e, 0x42 | BIT(priv->gpio_resetb)); /* set GPIOn direction output */
+}
+
+static void max9286_postinit(struct i2c_client *client, int addr)
+{
+	struct max9286_priv *priv = i2c_get_clientdata(client);
+	int idx;
+
+	for (idx = 0; idx < priv->links; idx++) {
+		client->addr = priv->des_addr;			/* MAX9286 I2C */
+		reg8_write(client, 0x0a, 0x11 << idx);		/* enable reverse/forward control for CAMx */
+
+		client->addr = priv->max9271_addr_map[idx];	/* MAX9271-CAMx I2C */
+		max9286_sensor_reset(client, client->addr, 0); /* sensor unreset */
+	}
+
+	client->addr = addr;					/* MAX9286 I2C */
+	reg8_write(client, 0x0a, 0x00);				/* disable reverse control for all cams */
+	reg8_write(client, 0x00, 0xe0 | priv->links_mask);	/* enable GMSL link for CAMs */
+	reg8_write(client, 0x0b, priv->csi2_outord);		/* CSI2 output order */
+	reg8_write(client, 0x15, 0x9b);				/* enable CSI output, VC is set accordingly to Link number, BIT7 magic must be set */
+	reg8_write(client, 0x1b, priv->links_mask);		/* enable equalizer for CAMs */
+	usleep_range(5000, 5500);				/* wait 2ms after any change of reverse channel settings */
+
+	if (strcmp(priv->fsync_mode, "manual") == 0) {
+		reg8_write(client, 0x01, 0x00);			/* manual: FRAMESYNC set manually via [0x06:0x08] regs */
+	} else if (strcmp(priv->fsync_mode, "automatic") == 0) {
+		reg8_write(client, 0x01, 0x02);			/* automatic: FRAMESYNC taken from the slowest Link */
+	} else if (strcmp(priv->fsync_mode, "semi-automatic") == 0) {
+		reg8_write(client, 0x01, 0x01);			/* semi-automatic: FRAMESYNC taken from the slowest Link */
+	} else if (strcmp(priv->fsync_mode, "external") == 0) {
+		reg8_write(client, 0x01, 0xc0);			/* ECU (aka MCU) based FrameSync using GPI-to-GPO */
+	}
+}
+
+static int max9286_reverse_channel_setup(struct i2c_client *client, int idx)
+{
+	struct max9286_priv *priv = i2c_get_clientdata(client);
+	u8 val = 0;
+	int timeout = priv->timeout;
+	char timeout_str[10];
+	int ret = 0;
+
+	/* Reverse channel enable */
+	client->addr = priv->des_addr;				/* MAX9286-CAMx I2C */
+	reg8_write(client, 0x3f, 0x4f);				/* enable custom reverse channel & first pulse length */
+	reg8_write(client, 0x34, 0xa2 | MAXIM_I2C_I2C_SPEED);	/* enable artificial ACKs, I2C speed set */
+	usleep_range(2000, 2500);				/* wait 2ms after any change of reverse channel settings */
+	reg8_write(client, 0x00, 0xe0 | BIT(idx));		/* enable GMSL link for CAMx */
+	reg8_write(client, 0x0a, 0x11 << idx);			/* enable reverse control for CAMx */
+	usleep_range(2000, 2500);				/* wait 2ms after any change of reverse channel settings */
+
+	for (;;) {
+		client->addr = priv->des_addr;			/* MAX9286-CAMx I2C */
+		reg8_write(client, 0x3b, 0x1e);			/* first pulse length rise time changed from 300ns to 200ns, amplitude 100mV */
+		usleep_range(2000, 2500);			/* wait 2ms after any change of reverse channel settings */
+
+		client->addr = 0x40;				/* MAX9271-CAMx I2C */
+		reg8_write(client, 0x04, 0x43);			/* wake-up, enable reverse_control/conf_link */
+		usleep_range(2000, 2500);			/* wait 2ms after any change of reverse channel settings */
+		reg8_write(client, 0x08, 0x01);			/* reverse channel receiver high threshold enable */
+		reg8_write(client, 0x97, priv->him ? 0xaf : 0x5f); /* enable reverse control channel programming (MAX96705-MAX96711 only) */
+		usleep_range(2000, 2500);			/* wait 2ms after any change of reverse channel settings */
+
+		client->addr = priv->des_addr;			/* MAX9286-CAMx I2C */
+		reg8_write(client, 0x3b, 0x19);			/* reverse channel increase amplitude 170mV to compensate high threshold enabled */
+		usleep_range(2000, 2500);			/* wait 2ms after any change of reverse channel settings */
+
+		client->addr = 0x40;				/* MAX9271-CAMx I2C */
+		reg8_read(client, 0x1e, &val);			/* read max9271 ID */
+		if (val == MAX9271_ID || val == MAX96705_ID || --timeout == 0) {
+			priv->ser_id = val;
+			break;
+		}
+
+		/* Check if already initialized (after reboot/reset ?) */
+		client->addr = priv->max9271_addr_map[idx];	/* MAX9271-CAMx I2C */
+		reg8_read(client, 0x1e, &val);			/* read max9271 ID */
+		if (val == MAX9271_ID || val == MAX96705_ID) {
+			priv->ser_id = val;
+			reg8_write(client, 0x04, 0x43);		/* enable reverse_control/conf_link */
+			usleep_range(2000, 2500);		/* wait 2ms after any change of reverse channel settings */
+			ret = -EADDRINUSE;
+			break;
+		}
+
+		if (timeout == priv->timeout / 2 && poc_trig) {
+			if (!IS_ERR(priv->poc_gpio[idx])) {
+				gpiod_direction_output(priv->poc_gpio[idx], 0); /* POC power off */
+				mdelay(200);
+				gpiod_direction_output(priv->poc_gpio[idx], 1); /* POC power on */
+				mdelay(priv->poc_delay);
+			}
+		}
+	}
+
+	max9286_sensor_reset(client, client->addr, 1);	/* sensor reset */
+
+	if (!timeout) {
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	priv->links_mask |= BIT(idx);
+	priv->csi2_outord &= ~(0x3 << (idx * 2));
+	priv->csi2_outord |= ((hweight8(priv->links_mask) - 1) << (idx * 2));
+
+out:
+	sprintf(timeout_str, "retries=%d", priv->timeout - timeout);
+	dev_info(&client->dev, "link%d %s %sat 0x%x %s %s\n", idx, ser_name(priv->ser_id),
+			       ret == -EADDRINUSE ? "already " : "", priv->max9271_addr_map[idx],
+			       ret == -ETIMEDOUT ? "not found: timeout GMSL link establish" : "",
+			       priv->timeout - timeout? timeout_str : "");
+
+	return ret;
+}
+
+static void max9286_initial_setup(struct i2c_client *client)
+{
+	struct max9286_priv *priv = i2c_get_clientdata(client);
+
+	/* Initial setup */
+	client->addr = priv->des_addr;				/* MAX9286-CAMx I2C */
+	reg8_write(client, 0x15, 0x13);				/* disable CSI output, VC is set accordingly to Link number */
+	reg8_write(client, 0x69, 0x0f);				/* mask CSI forwarding from all links */
+	switch (priv->lanes) {
+	case 1:
+		reg8_write(client, 0x12, 0x33);			/* enable CSI-2 Lane D0, DBL mode, YUV422 8-bit*/
+		break;
+	case 2:
+		reg8_write(client, 0x12, 0x73);			/* enable CSI-2 Lanes D0,D1, DBL mode, YUV422 8-bit*/
+		break;
+	case 3:
+		reg8_write(client, 0x12, 0xd3);			/* enable CSI-2 Lanes D0-D2, DBL mode, YUV422 8-bit*/
+		break;
+	case 4:
+		reg8_write(client, 0x12, 0xf3);			/* enable CSI-2 Lanes D0-D3, DBL mode, YUV422 8-bit*/
+		break;
+	default:
+		dev_err(&client->dev, "CSI2 lanes number is invalid (%d)\n", priv->lanes);
+	}
+
+	/* Start GMSL initialization with FSYNC disabled. This is required for some odd LVDS cameras */
+	reg8_write(client, 0x01, 0xc0);				/* ECU (aka MCU) based FrameSync using GPI-to-GPO */
+	reg8_write(client, 0x06, priv->fsync_period & 0xff);
+	reg8_write(client, 0x07, (priv->fsync_period >> 8) & 0xff);
+	reg8_write(client, 0x08, priv->fsync_period >> 16);
+
+	reg8_write(client, 0x63, 0);				/* disable overlap window */
+	reg8_write(client, 0x64, 0);
+	reg8_write(client, 0x0c, 0x91 | (priv->vsync ? BIT(3) : 0) | (priv->hsync ? BIT(2) : 0)); /* enable HS/VS encoding, use D14/15 for HS/VS, invert HS/VS */
+	reg8_write(client, 0x19, 0x0c);				/* Drive HSTRAIL state for 120ns after the last payload bit */
+}
+
+static void max9286_gmsl_link_setup(struct i2c_client *client, int idx)
+{
+	struct max9286_priv *priv = i2c_get_clientdata(client);
+
+	/* GMSL setup */
+	client->addr = 0x40;					/* MAX9271-CAMx I2C */
+	reg8_write(client, 0x0d, 0x22 | MAXIM_I2C_I2C_SPEED);	/* disable artificial ACK, I2C speed set */
+	reg8_write(client, 0x07, 0x84 | (priv->pclk_rising_edge ? 0 : 0x10)); /* RAW/YUV, PCLK edge, HS/VS encoding enabled */
+	usleep_range(2000, 2500);				/* wait 2ms */
+	reg8_write(client, 0x02, 0xff);				/* spread spectrum +-4%, pclk range automatic, Gbps automatic  */
+	usleep_range(2000, 2500);				/* wait 2ms */
+
+	if (priv->ser_id == MAX96705_ID) {
+		/* setup crossbar in DBL mode: reverse DVP bus */
+		reg8_write(client, 0x20, 0x07);
+		reg8_write(client, 0x21, 0x06);
+		reg8_write(client, 0x22, 0x05);
+		reg8_write(client, 0x23, 0x04);
+		reg8_write(client, 0x24, 0x03);
+		reg8_write(client, 0x25, 0x02);
+		reg8_write(client, 0x26, 0x01);
+		reg8_write(client, 0x27, 0x00);
+
+		reg8_write(client, 0x30, 0x17);
+		reg8_write(client, 0x31, 0x16);
+		reg8_write(client, 0x32, 0x15);
+		reg8_write(client, 0x33, 0x14);
+		reg8_write(client, 0x34, 0x13);
+		reg8_write(client, 0x35, 0x12);
+		reg8_write(client, 0x36, 0x11);
+		reg8_write(client, 0x37, 0x10);
+	}
+
+	client->addr = priv->des_addr;				/* MAX9286-CAMx I2C */
+	reg8_write(client, 0x34, 0x22 | MAXIM_I2C_I2C_SPEED);	/* disable artificial ACK, I2C speed set */
+	usleep_range(2000, 2500);				/* wait 2ms */
+
+	/* I2C translator setup */
+	client->addr = 0x40;					/* MAX9271-CAMx I2C */
+//	reg8_write(client, 0x09, maxim_map[2][idx] << 1);	/* SENSOR I2C translated - must be set by sensor driver */
+//	reg8_write(client, 0x0A, 0x30 << 1);			/* SENSOR I2C native - must be set by sensor driver */
+	reg8_write(client, 0x0B, BROADCAST << 1);		/* broadcast I2C */
+	reg8_write(client, 0x0C, priv->max9271_addr_map[idx] << 1); /* MAX9271-CAMx I2C new */
+	/* I2C addresse change */
+	reg8_write(client, 0x01, priv->des_addr << 1);		/* MAX9286 I2C */
+	reg8_write(client, 0x00, priv->max9271_addr_map[idx] << 1); /* MAX9271-CAM0 I2C new */
+	usleep_range(2000, 2500);				/* wait 2ms */
+	/* put MAX9271 in configuration link state  */
+	client->addr = priv->max9271_addr_map[idx];		/* MAX9271-CAMx I2C new */
+	reg8_write(client, 0x04, 0x43);				/* enable reverse_control/conf_link */
+	usleep_range(2000, 2500);				/* wait 2ms */
+#ifdef MAXIM_DUMP
+	client->addr = priv->des_addr;				/* MAX9286-CAMx I2C */
+	maxim_max927x_dump_regs(client);
+	client->addr = priv->max9271_addr_map[idx];		/* MAX9271-CAMx I2C new */
+	maxim_max927x_dump_regs(client);
+#endif
+}
+
+static int max9286_initialize(struct i2c_client *client)
+{
+	struct max9286_priv *priv = i2c_get_clientdata(client);
+	int idx, ret;
+
+	dev_info(&client->dev, "LINKs=%d, LANES=%d, FSYNC mode=%s, FSYNC period=%d, PCLK edge=%s\n",
+			       priv->links, priv->lanes, priv->fsync_mode, priv->fsync_period,
+			       priv->pclk_rising_edge ? "rising" : "falling");
+
+	if (priv->des_quirk_addr)
+		max9286_preinit(client, priv->des_quirk_addr);
+
+	max9286_preinit(client, priv->des_addr);
+	max9286_initial_setup(client);
+
+	for (idx = 0; idx < priv->links; idx++) {
+		if (!IS_ERR(priv->poc_gpio[idx])) {
+			gpiod_direction_output(priv->poc_gpio[idx], 1); /* POC power on */
+			mdelay(priv->poc_delay);
+		}
+
+		ret = max9286_reverse_channel_setup(client, idx);
+		if (ret)
+			continue;
+		max9286_gmsl_link_setup(client, idx);
+	}
+
+	max9286_postinit(client, priv->des_addr);
+
+	client->addr = priv->des_addr;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int max9286_g_register(struct v4l2_subdev *sd,
+				      struct v4l2_dbg_register *reg)
+{
+	struct max9286_priv *priv = v4l2_get_subdevdata(sd);
+	struct i2c_client *client = priv->client;
+	int ret;
+	u8 val = 0;
+
+	ret = reg8_read(client, (u8)reg->reg, &val);
+	if (ret < 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = sizeof(u8);
+
+	return 0;
+}
+
+static int max9286_s_register(struct v4l2_subdev *sd,
+				      const struct v4l2_dbg_register *reg)
+{
+	struct max9286_priv *priv = v4l2_get_subdevdata(sd);
+	struct i2c_client *client = priv->client;
+
+	return reg8_write(client, (u8)reg->reg, (u8)reg->val);
+}
+#endif
+
+static int max9286_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct max9286_priv *priv = v4l2_get_subdevdata(sd);
+	struct i2c_client *client = priv->client;
+
+	if (on) {
+		if (atomic_inc_return(&priv->use_count) == 1)
+			reg8_write(client, 0x69, priv->links_mask ^ 0x0f); /* unmask CSI forwarding from detected links */
+	} else {
+		if (atomic_dec_return(&priv->use_count) == 0)
+			reg8_write(client, 0x69, 0x0f);		/* mask CSI forwarding from all links */
+	}
+
+	return 0;
+}
+
+static int max9286_registered_async(struct v4l2_subdev *sd)
+{
+	struct max9286_priv *priv = v4l2_get_subdevdata(sd);
+	struct i2c_client *client = priv->client;
+	int idx, tmp_addr;
+
+	/* switch to GMSL serial_link for streaming video */
+	tmp_addr = client->addr;
+	idx = sd->grp_id;
+
+	client->addr = priv->des_addr;				/* MAX9286 I2C */
+	reg8_write(client, 0x0a, 0x11 << idx);			/* enable reverse/forward control for CAMx */
+
+	client->addr = priv->max9271_addr_map[idx];		/* MAX9271-CAMx */
+	reg8_write(client, 0x04, conf_link ? 0x43 : 0x83);	/* enable serial_link */
+	usleep_range(2000, 2500);				/* wait 2ms after changing reverse_control */
+
+	client->addr = priv->des_addr;				/* MAX9286 I2C */
+	reg8_write(client, 0x0a, (priv->links_mask << 4) | priv->links_mask); /* enable reverse/forward control for all CAMs */
+
+	client->addr = tmp_addr;
+
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops max9286_subdev_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register		= max9286_g_register,
+	.s_register		= max9286_s_register,
+#endif
+	.s_power		= max9286_s_power,
+	.registered_async	= max9286_registered_async,
+};
+
+static struct v4l2_subdev_ops max9286_subdev_ops = {
+	.core	= &max9286_subdev_core_ops,
+};
+
+static int max9286_parse_dt(struct i2c_client *client)
+{
+	struct max9286_priv *priv = i2c_get_clientdata(client);
+	struct device_node *np = client->dev.of_node;
+	struct device_node *endpoint = NULL;
+	struct property *prop;
+	int err, pwen, i;
+	int sensor_delay, gpio0 = 1, gpio1 = 1;
+	u8 val = 0;
+	char poc_name[10];
+
+	if (of_property_read_u32(np, "maxim,links", &priv->links))
+		priv->links = 4;
+
+	if (of_property_read_u32(np, "maxim,lanes", &priv->lanes))
+		priv->lanes = 4;
+
+	pwen = of_get_gpio(np, 0);
+	if (pwen > 0) {
+		err = gpio_request_one(pwen, GPIOF_OUT_INIT_HIGH, dev_name(&client->dev));
+		if (err)
+			dev_err(&client->dev, "cannot request PWEN gpio %d: %d\n", pwen, err);
+	}
+
+	mdelay(250);
+
+	for (i = 0; i < 4; i++) {
+		sprintf(poc_name, "POC%d", i);
+		priv->poc_gpio[i] = devm_gpiod_get_optional(&client->dev, poc_name, 0);
+	}
+
+	reg8_read(client, 0x1e, &val);				/* read max9286 ID */
+	if (val != MAX9286_ID) {
+		prop = of_find_property(np, "reg", NULL);
+		if (prop)
+			of_remove_property(np, prop);
+		return -ENODEV;
+	}
+
+	if (!of_property_read_u32(np, "maxim,gpio0", &gpio0) ||
+	    !of_property_read_u32(np, "maxim,gpio1", &gpio1))
+		reg8_write(client, 0x0f, 0x08 | (gpio1 << 1) | gpio0);
+
+	if (of_property_read_u32(np, "maxim,resetb-gpio", &priv->gpio_resetb)) {
+		priv->gpio_resetb = -1;
+	} else {
+		if (of_property_read_bool(np, "maxim,resetb-active-high"))
+			priv->active_low_resetb = 0;
+		else
+			priv->active_low_resetb = 1;
+	}
+
+	if (!of_property_read_u32(np, "maxim,sensor_delay", &sensor_delay))
+		mdelay(sensor_delay);
+	if (of_property_read_string(np, "maxim,fsync-mode", &priv->fsync_mode))
+		priv->fsync_mode = fsync_mode_default;
+	if (of_property_read_u32(np, "maxim,fsync-period", &priv->fsync_period))
+		priv->fsync_period = 3200000;			/* 96MHz/30fps */
+	priv->pclk_rising_edge = true;
+	if (of_property_read_bool(np, "maxim,pclk-falling-edge"))
+		priv->pclk_rising_edge = false;
+	if (of_property_read_u32(np, "maxim,timeout", &priv->timeout))
+		priv->timeout = 100;
+	if (of_property_read_u32(np, "maxim,i2c-quirk", &priv->des_quirk_addr))
+		priv->des_quirk_addr = 0;
+	if (of_property_read_u32(np, "maxim,him", &priv->him))
+		priv->him = 0;
+	if (of_property_read_u32(np, "maxim,hsync", &priv->hsync))
+		priv->hsync = 0;
+	if (of_property_read_u32(np, "maxim,vsync", &priv->vsync))
+		priv->vsync = 1;
+	if (of_property_read_u32(np, "maxim,poc-delay", &priv->poc_delay))
+		priv->poc_delay = 50;
+
+	/* module params override dts */
+	if (him)
+		priv->him = him;
+	if (fsync_period) {
+		priv->fsync_period = fsync_period;
+		priv->fsync_mode = fsync_mode_default;
+	}
+	if (hsync)
+		priv->hsync = hsync;
+	if (!vsync)
+		priv->vsync = vsync;
+	if (gpio_resetb)
+		priv->gpio_resetb = gpio_resetb;
+	if (active_low_resetb)
+		priv->active_low_resetb = active_low_resetb;
+	if (poc_delay)
+		priv->poc_delay = poc_delay;
+
+	for (i = 0; i < priv->links; i++) {
+		endpoint = of_graph_get_next_endpoint(np, endpoint);
+		if (!endpoint)
+			break;
+
+		of_node_put(endpoint);
+
+		if (of_property_read_u32(endpoint, "max9271-addr", &priv->max9271_addr_map[i])) {
+			dev_err(&client->dev, "max9271-addr not set\n");
+			return -EINVAL;
+		}
+
+		priv->sd_fwnode[i] = of_fwnode_handle(endpoint);
+	}
+
+	return 0;
+}
+
+static void max9286_setup_remote_endpoint(struct i2c_client *client)
+{
+	struct max9286_priv *priv = i2c_get_clientdata(client);
+	struct device_node *np = client->dev.of_node;
+	struct device_node *endpoint = NULL, *rendpoint = NULL;
+	int i;
+	struct property *csi_rate_prop, *dvp_order_prop;
+
+	for (i = 0; ; i++) {
+		endpoint = of_graph_get_next_endpoint(np, endpoint);
+		if (!endpoint)
+			break;
+
+		of_node_put(endpoint);
+
+		rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0);
+		if (!rendpoint)
+			continue;
+
+		csi_rate_prop = of_find_property(endpoint, "csi-rate", NULL);
+		if (csi_rate_prop) {
+			/* CSI2_RATE = PCLK*sizeof(YUV8)*links/lanes */
+			priv->csi_rate = cpu_to_be32(100 * 8 * hweight8(priv->links_mask) / priv->lanes);
+			csi_rate_prop->value = &priv->csi_rate;
+			of_update_property(rendpoint, csi_rate_prop);
+		}
+
+		dvp_order_prop = of_find_property(endpoint, "dvp-order", NULL);
+		if (dvp_order_prop)
+			of_update_property(rendpoint, dvp_order_prop);
+	}
+}
+
+static int max9286_probe(struct i2c_client *client,
+				 const struct i2c_device_id *did)
+{
+	struct max9286_priv *priv;
+	int err, i;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, priv);
+	priv->des_addr = client->addr;
+	priv->client = client;
+	atomic_set(&priv->use_count, 0);
+	priv->csi2_outord = 0xff;
+
+	err = max9286_parse_dt(client);
+	if (err)
+		goto out;
+
+	err = max9286_initialize(client);
+	if (err < 0)
+		goto out;
+
+	max9286_setup_remote_endpoint(client);
+
+	for (i = 0; i < 4; i++) {
+		v4l2_subdev_init(&priv->sd[i], &max9286_subdev_ops);
+		priv->sd[i].owner = client->dev.driver->owner;
+		priv->sd[i].dev = &client->dev;
+		priv->sd[i].grp_id = i;
+		v4l2_set_subdevdata(&priv->sd[i], priv);
+		priv->sd[i].fwnode = priv->sd_fwnode[i];
+
+		snprintf(priv->sd[i].name, V4L2_SUBDEV_NAME_SIZE, "%s.%d %d-%04x",
+			 client->dev.driver->name, i, i2c_adapter_id(client->adapter),
+			 client->addr);
+
+		err = v4l2_async_register_subdev(&priv->sd[i]);
+		if (err < 0)
+			goto out;
+	}
+out:
+	return err;
+}
+
+static int max9286_remove(struct i2c_client *client)
+{
+	struct max9286_priv *priv = i2c_get_clientdata(client);
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		v4l2_async_unregister_subdev(&priv->sd[i]);
+		v4l2_device_unregister_subdev(&priv->sd[i]);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id max9286_dt_ids[] = {
+	{ .compatible = "maxim,max9286" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, max9286_dt_ids);
+
+static const struct i2c_device_id max9286_id[] = {
+	{ "max9286", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max9286_id);
+
+static struct i2c_driver max9286_i2c_driver = {
+	.driver	= {
+		.name		= "max9286",
+		.of_match_table	= of_match_ptr(max9286_dt_ids),
+	},
+	.probe		= max9286_probe,
+	.remove		= max9286_remove,
+	.id_table	= max9286_id,
+};
+
+module_i2c_driver(max9286_i2c_driver);
+
+MODULE_DESCRIPTION("GMSL driver for MAX9286");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/soc_camera/max9286.h b/drivers/media/i2c/soc_camera/max9286.h
new file mode 100644
index 0000000..6c2a9e0
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/max9286.h
@@ -0,0 +1,244 @@
+/*
+ * MAXIM max9286-max9271 GMSL driver include file
+ *
+ * Copyright (C) 2015-2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _MAX9286_MAX9271_H
+#define _MAX9286_MAX9271_H
+
+//#define DEBUG
+#ifdef DEBUG
+//#define WRITE_VERIFY
+#define MAXIM_DUMP
+#undef dev_dbg
+#define dev_dbg dev_info
+#endif
+
+#define REG8_NUM_RETRIES	1 /* number of read/write retries */
+#define REG16_NUM_RETRIES	10 /* number of read/write retries */
+#define MAX9271_ID		0x9
+#define MAX96705_ID		0x41
+#define MAX9286_ID		0x40
+#define BROADCAST		0x6f
+
+static inline int reg8_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int ret, retries;
+
+	for (retries = REG8_NUM_RETRIES; retries; retries--) {
+		ret = i2c_smbus_read_byte_data(client, reg);
+		if (!(ret < 0))
+			break;
+	}
+
+	if (ret < 0) {
+		dev_dbg(&client->dev,
+			"read fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	} else {
+		*val = ret;
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+static inline int reg8_write(struct i2c_client *client, u8 reg, u8 val)
+{
+	int ret, retries;
+
+	for (retries = REG8_NUM_RETRIES; retries; retries--) {
+		ret = i2c_smbus_write_byte_data(client, reg, val);
+		if (!(ret < 0))
+			break;
+	}
+
+	if (ret < 0) {
+		dev_dbg(&client->dev,
+			"write fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	} else {
+#ifdef WRITE_VERIFY
+		u8 val2;
+		reg8_read(client, reg, &val2);
+		if (val != val2)
+			dev_err(&client->dev,
+				"write verify mismatch: chip 0x%x reg=0x%x "
+				"0x%x->0x%x\n", client->addr, reg, val, val2);
+#endif
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+static inline int reg16_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+	int ret, retries;
+	u8 buf[2] = {reg >> 8, reg & 0xff};
+
+	for (retries = REG16_NUM_RETRIES; retries; retries--) {
+		ret = i2c_master_send(client, buf, 2);
+		if (ret == 2) {
+			ret = i2c_master_recv(client, buf, 1);
+			if (ret == 1)
+				break;
+		}
+	}
+
+	if (ret < 0) {
+		dev_dbg(&client->dev,
+			"read fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	} else {
+		*val = buf[0];
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+static inline int reg16_write(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret, retries;
+	u8 buf[3] = {reg >> 8, reg & 0xff, val};
+
+	for (retries = REG16_NUM_RETRIES; retries; retries--) {
+		ret = i2c_master_send(client, buf, 3);
+		if (ret == 3)
+			break;
+	}
+
+	if (ret < 0) {
+		dev_dbg(&client->dev,
+			"write fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	} else {
+#ifdef WRITE_VERIFY
+		u8 val2;
+		reg16_read(client, reg, &val2);
+		if (val != val2)
+			dev_err(&client->dev,
+				"write verify mismatch: chip 0x%x reg=0x%x "
+				"0x%x->0x%x\n", client->addr, reg, val, val2);
+#endif
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+
+static inline int reg16_read16(struct i2c_client *client, u16 reg, u16 *val)
+{
+	int ret, retries;
+	u8 buf[2] = {reg >> 8, reg & 0xff};
+
+	for (retries = REG8_NUM_RETRIES; retries; retries--) {
+		ret = i2c_master_send(client, buf, 2);
+		if (ret == 2) {
+			ret = i2c_master_recv(client, buf, 2);
+			if (ret == 2)
+				break;
+		}
+	}
+
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"read fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	} else {
+		*val = ((u16)buf[0] << 8) | buf[1];
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+static inline int reg16_write16(struct i2c_client *client, u16 reg, u16 val)
+{
+	int ret, retries;
+	u8 buf[4] = {reg >> 8, reg & 0xff, val >> 8, val & 0xff};
+
+	for (retries = REG8_NUM_RETRIES; retries; retries--) {
+		ret = i2c_master_send(client, buf, 4);
+		if (ret == 4)
+			break;
+	}
+
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"write fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+
+#ifdef MAXIM_DUMP
+static void maxim_ovsensor_dump_regs(struct i2c_client *client)
+{
+	int ret, i;
+	u8 val = 0;
+	u16 regs[] = {0x300a, 0x300b, 0x300c};
+
+	dev_dbg(&client->dev, "dump regs 0x%x\n", client->addr);
+
+	for (i = 0; i < sizeof(regs) / 2; i++) {
+		ret = reg16_read(client, regs[i], &val);
+		if (ret < 0)
+			dev_err(&client->dev,
+				"read fail: chip 0x%x register 0x%02x: %d\n",
+				client->addr, regs[i], ret);
+		printk("0x%02x -> 0x%x\n", regs[i], val);
+	}
+}
+
+static void maxim_ov10635_dump_format_regs(struct i2c_client *client)
+{
+	int ret, i;
+	u8 val;
+	u16 regs[] = {0x3003, 0x3004, 0x4300,
+		      0x4605, 0x3621, 0x3702, 0x3703, 0x3704,
+		      0x3802, 0x3803, 0x3806, 0x3807, 0x3808, 0x3809, 0x380a,
+		      0x380b, 0x380c, 0x380d, 0x380e, 0x380f,
+		      0x4606, 0x4607, 0x460a, 0x460b,
+		      0xc488, 0xc489, 0xc48a, 0xc48b,
+		      0xc4cc, 0xc4cd, 0xc4ce, 0xc4cf, 0xc512, 0xc513,
+		      0xc518, 0xc519, 0xc51a, 0xc51b,
+	};
+
+	dev_dbg(&client->dev, "dump regs 0x%x\n", client->addr);
+
+	for (i = 0; i < sizeof(regs) / 2; i++) {
+		ret = reg16_read(client, regs[i], &val);
+		if (ret < 0)
+			dev_err(&client->dev,
+				"read fail: chip 0x%x register 0x%02x: %d\n",
+				client->addr, regs[i], ret);
+		printk("0x%02x -> 0x%x\n", regs[i], val);
+	}
+}
+
+static void maxim_max927x_dump_regs(struct i2c_client *client)
+{
+	int ret;
+	u8 reg;
+
+	dev_dbg(&client->dev, "dump regs 0x%x\n", client->addr);
+
+	for (reg = 0; reg < 0x20; reg++) {
+		ret = i2c_smbus_read_byte_data(client, reg);
+		if (ret < 0)
+			dev_err(&client->dev,
+				"read fail: chip 0x%x register 0x%x: %d\n",
+				client->addr, reg, ret);
+		printk("0x%02x ", ret);
+		if (((reg + 1) % 0x10) == 0)
+			printk("\n");
+	}
+}
+#endif /* MAXIM_DUMP */
+#endif /* _MAX9286_MAX9271_H */
diff --git a/drivers/media/i2c/soc_camera/ov10635.c b/drivers/media/i2c/soc_camera/ov10635.c
new file mode 100644
index 0000000..eacf015
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov10635.c
@@ -0,0 +1,760 @@
+/*
+ * OmniVision ov10635 sensor camera driver
+ *
+ * Copyright (C) 2015-2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+
+#include "max9286.h"
+#include "ov10635.h"
+
+#define OV10635_I2C_ADDR		0x30
+
+#define OV10635_PID			0x300a
+#define OV10635_VER			0x300b
+#define OV10635_VERSION_REG		0xa635
+#define OV10635_VERSION(pid, ver)	(((pid) << 8) | ((ver) & 0xff))
+
+struct ov10635_priv {
+	struct v4l2_subdev		sd;
+	struct v4l2_ctrl_handler	hdl;
+	struct media_pad		pad;
+	struct v4l2_rect		rect;
+	int				subsampling;
+	int				fps_denominator;
+	int				init_complete;
+	u8				id[6];
+	int				dvp_order;
+	/* serializers */
+	int				max9286_addr;
+	int				max9271_addr;
+	int				ti964_addr;
+	int				ti954_addr;
+	int				ti9x3_addr;
+	int				port;
+	int				gpio_resetb;
+	int				gpio_fsin;
+};
+
+static inline struct ov10635_priv *to_ov10635(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ov10635_priv, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct ov10635_priv, hdl)->sd;
+}
+
+static void ov10635_s_port(struct i2c_client *client, int fwd_en)
+{
+	struct ov10635_priv *priv = to_ov10635(client);
+	int tmp_addr;
+
+	if (priv->max9286_addr) {
+		tmp_addr = client->addr;
+		client->addr = priv->max9286_addr;				/* Deserializer I2C address */
+		reg8_write(client, 0x0a, fwd_en ? 0x11 << priv->port : 0);	/* Enable/disable reverse/forward control for this port */
+		usleep_range(5000, 5500);					/* wait 5ms */
+		client->addr = tmp_addr;
+	};
+}
+
+static int ov10635_set_regs(struct i2c_client *client,
+			    const struct ov10635_reg *regs, int nr_regs)
+{
+	int i;
+
+	for (i = 0; i < nr_regs; i++) {
+		if (reg16_write(client, regs[i].reg, regs[i].val)) {
+			usleep_range(100, 150); /* wait 100ns */
+			reg16_write(client, regs[i].reg, regs[i].val);
+		}
+	}
+
+	return 0;
+}
+
+static int ov10635_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static int ov10635_set_window(struct v4l2_subdev *sd, int subsampling)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov10635_priv *priv = to_ov10635(client);
+
+	/* disable clocks */
+	reg16_write(client, 0x302e, 0x00);
+	reg16_write(client, 0x301b, 0xff);
+	reg16_write(client, 0x301c, 0xff);
+	reg16_write(client, 0x301a, 0xff);
+
+	/* setup resolution */
+	reg16_write(client, 0x3808, priv->rect.width >> 8);
+	reg16_write(client, 0x3809, priv->rect.width & 0xff);
+	reg16_write(client, 0x380a, priv->rect.height >> 8);
+	reg16_write(client, 0x380b, priv->rect.height & 0xff);
+
+	/* enable/disable subsampling */
+	reg16_write(client, 0x5005, subsampling ? 0x89 : 0x08);
+	reg16_write(client, 0x3007, subsampling ? 0x02 : 0x01);
+	reg16_write(client, 0x4004, subsampling ? 0x02 : 0x04);
+
+#if 0 /* This is implemented in VIN via SOC_CAMERA layer, hence skip */
+	/* horiz crop start */
+	reg16_write(client, 0x3800, priv->rect.left >> 8);
+	reg16_write(client, 0x3801, priv->rect.left & 0xff);
+	/* horiz crop end */
+	reg16_write(client, 0x3804, (priv->rect.left + priv->rect.width + 1) >> 8);
+	reg16_write(client, 0x3805, (priv->rect.left + priv->rect.width + 1) & 0xff);
+	/* vert crop start */
+	reg16_write(client, 0x3802, priv->rect.top >> 8);
+	reg16_write(client, 0x3803, priv->rect.top & 0xff);
+	/* vert crop end */
+	reg16_write(client, 0x3806, (priv->rect.top + priv->rect.height + 1) >> 8);
+	reg16_write(client, 0x3807, (priv->rect.top + priv->rect.height + 1) & 0xff);
+#endif
+	/* enable clocks */
+	reg16_write(client, 0x301b, 0xf0);
+	reg16_write(client, 0x301c, 0xf0);
+	reg16_write(client, 0x301a, 0xf0);
+	reg16_write(client, 0x302e, 0x01);
+
+	return 0;
+};
+
+static int ov10635_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov10635_priv *priv = to_ov10635(client);
+
+	if (format->pad)
+		return -EINVAL;
+
+	mf->width = priv->rect.width;
+	mf->height = priv->rect.height;
+	mf->code = MEDIA_BUS_FMT_YUYV8_2X8;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int ov10635_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+
+	mf->code = MEDIA_BUS_FMT_YUYV8_2X8;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		cfg->try_fmt = *mf;
+
+	return 0;
+}
+
+static int ov10635_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->pad || code->index > 0)
+		return -EINVAL;
+
+	code->code = MEDIA_BUS_FMT_YUYV8_2X8;
+
+	return 0;
+}
+
+static int ov10635_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov10635_priv *priv = to_ov10635(client);
+
+	memcpy(edid->edid, priv->id, 6);
+
+	edid->edid[6] = 0xff;
+	edid->edid[7] = client->addr;
+	edid->edid[8] = OV10635_VERSION_REG >> 8;
+	edid->edid[9] = OV10635_VERSION_REG & 0xff;
+
+	return 0;
+}
+
+static int ov10635_set_selection(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_selection *sel)
+{
+	struct v4l2_rect *rect = &sel->r;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov10635_priv *priv = to_ov10635(client);
+	int subsampling = 0;
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
+	    sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	rect->left = ALIGN(rect->left, 2);
+	rect->top = ALIGN(rect->top, 2);
+	rect->width = ALIGN(rect->width, 2);
+	rect->height = ALIGN(rect->height, 2);
+
+	if ((rect->left + rect->width > OV10635_MAX_WIDTH) ||
+	    (rect->top + rect->height > OV10635_MAX_HEIGHT))
+		*rect = priv->rect;
+
+	if (rect->width == OV10635_MAX_WIDTH / 2 &&
+	    rect->height == OV10635_MAX_HEIGHT / 2)
+		subsampling = 1;
+
+	priv->rect.left = rect->left;
+	priv->rect.top = rect->top;
+	priv->rect.width = rect->width;
+	priv->rect.height = rect->height;
+
+	/* change window only for subsampling, crop is done by VIN */
+	if (subsampling != priv->subsampling) {
+		ov10635_set_window(sd, subsampling);
+		priv->subsampling = subsampling;
+	}
+
+	return 0;
+}
+
+static int ov10635_get_selection(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_selection *sel)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov10635_priv *priv = to_ov10635(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = OV10635_MAX_WIDTH;
+		sel->r.height = OV10635_MAX_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = OV10635_MAX_WIDTH;
+		sel->r.height = OV10635_MAX_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP:
+		sel->r = priv->rect;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ov10635_g_mbus_config(struct v4l2_subdev *sd,
+				 struct v4l2_mbus_config *cfg)
+{
+	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	cfg->type = V4L2_MBUS_CSI2;
+
+	return 0;
+}
+
+static int ov10635_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov10635_priv *priv = to_ov10635(client);
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(cp, 0, sizeof(struct v4l2_captureparm));
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+	cp->timeperframe.numerator = 1;
+	cp->timeperframe.denominator = priv->fps_denominator;
+
+	return 0;
+}
+
+static int ov10635_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov10635_priv *priv = to_ov10635(client);
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	int ret = 0;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (cp->extendedmode != 0)
+		return -EINVAL;
+
+	if (priv->fps_denominator != cp->timeperframe.denominator) {
+		switch (cp->timeperframe.denominator) {
+		case 5:
+			ret = ov10635_set_regs(client, ov10635_regs_5fps,
+					       ARRAY_SIZE(ov10635_regs_5fps));
+			break;
+		case 10:
+			ret = ov10635_set_regs(client, ov10635_regs_10fps,
+					       ARRAY_SIZE(ov10635_regs_10fps));
+			break;
+		case 15:
+			ret = ov10635_set_regs(client, ov10635_regs_15fps,
+					       ARRAY_SIZE(ov10635_regs_15fps));
+			break;
+		case 30:
+			ret = ov10635_set_regs(client, ov10635_regs_30fps,
+					       ARRAY_SIZE(ov10635_regs_30fps));
+			break;
+		default:
+			ret = -EINVAL;
+			goto out;
+		}
+
+		priv->fps_denominator = cp->timeperframe.denominator;
+	}
+
+out:
+	return ret;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov10635_g_register(struct v4l2_subdev *sd,
+			      struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+	u8 val = 0;
+
+	ret = reg16_read(client, (u16)reg->reg, &val);
+	if (ret < 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = sizeof(u16);
+
+	return 0;
+}
+
+static int ov10635_s_register(struct v4l2_subdev *sd,
+			      const struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	return reg16_write(client, (u16)reg->reg, (u8)reg->val);
+}
+#endif
+
+static struct v4l2_subdev_core_ops ov10635_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = ov10635_g_register,
+	.s_register = ov10635_s_register,
+#endif
+};
+
+static int ov10635_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov10635_priv *priv = to_ov10635(client);
+	int ret = -EINVAL;
+	u8 val = 0;
+
+	if (!priv->init_complete)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		/* AEC/AGC target */
+		ret = reg16_write(client, 0xc46a, ctrl->val);
+		break;
+	case V4L2_CID_CONTRAST:
+		udelay(100);
+		ret = ov10635_set_regs(client, &ov10635_regs_contrast[ctrl->val][0], 18);
+		break;
+	case V4L2_CID_SATURATION:
+		ret = reg16_write(client, 0xc316, ctrl->val);
+		break;
+	case V4L2_CID_HUE:
+		/* CMX ? */
+		ret = 0;
+		break;
+	case V4L2_CID_GAMMA:
+		ret = reg16_write(client, 0xc4be, ctrl->val >> 8);
+		ret |= reg16_write(client, 0xc4bf, ctrl->val & 0xff);
+		break;
+	case V4L2_CID_AUTOGAIN:
+		/* automatic gain/exposure */
+		ret = reg16_write(client, 0x56d0, !ctrl->val);
+		break;
+	case V4L2_CID_GAIN:
+		/* manual gain */
+		ret = reg16_write(client, 0x3504, 0);
+		ret |= reg16_write(client, 0x56d1, ctrl->val >> 8);
+		ret |= reg16_write(client, 0x56d2, ctrl->val & 0xff);
+		ret |= reg16_write(client, 0x3504, 1); /* validate gain */
+		break;
+	case V4L2_CID_EXPOSURE:
+		/* manual exposure */
+		ret = reg16_write(client, 0x3504, 0);
+		ret |= reg16_write(client, 0x56d5, ctrl->val >> 8);
+		ret |= reg16_write(client, 0x56d6, ctrl->val & 0xff);
+		ret |= reg16_write(client, 0x3504, 1); /* validate exposure */
+		break;
+	case V4L2_CID_HFLIP:
+		ret = reg16_read(client, 0x381d, &val);
+		if (ret < 0)
+			goto out;
+		if (ctrl->val)
+			val |= 0x3;
+		else
+			val &= ~0x3;
+		ret = reg16_write(client, 0x381d, val);
+		break;
+	case V4L2_CID_VFLIP:
+		ret = reg16_read(client, 0x381c, &val);
+		if (ctrl->val)
+			val |= 0xc0;
+		else
+			val &= ~0xc0;
+		ret = reg16_write(client, 0x381c, val);
+		break;
+	case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+		ret = 0;
+		break;
+	}
+
+out:
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov10635_ctrl_ops = {
+	.s_ctrl = ov10635_s_ctrl,
+};
+
+static struct v4l2_subdev_video_ops ov10635_video_ops = {
+	.s_stream	= ov10635_s_stream,
+	.g_mbus_config	= ov10635_g_mbus_config,
+	.g_parm		= ov10635_g_parm,
+	.s_parm		= ov10635_s_parm,
+};
+
+static const struct v4l2_subdev_pad_ops ov10635_subdev_pad_ops = {
+	.get_edid	= ov10635_get_edid,
+	.enum_mbus_code	= ov10635_enum_mbus_code,
+	.get_selection	= ov10635_get_selection,
+	.set_selection	= ov10635_set_selection,
+	.get_fmt	= ov10635_get_fmt,
+	.set_fmt	= ov10635_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov10635_subdev_ops = {
+	.core	= &ov10635_core_ops,
+	.video	= &ov10635_video_ops,
+	.pad	= &ov10635_subdev_pad_ops,
+};
+
+static void ov10635_otp_id_read(struct i2c_client *client)
+{
+	struct ov10635_priv *priv = to_ov10635(client);
+	int i;
+
+	/* read camera id from OTP memory */
+	reg16_write(client, 0x3d10, 1);
+
+	usleep_range(15000, 16000); /* wait 15ms */
+
+	for (i = 0; i < 6; i++)
+		reg16_read(client, 0x3d00 + i, &priv->id[i]);
+}
+
+static ssize_t ov10635_otp_id_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev));
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov10635_priv *priv = to_ov10635(client);
+
+	return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+			priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+}
+
+static DEVICE_ATTR(otp_id_ov10635, S_IRUGO, ov10635_otp_id_show, NULL);
+
+static int ov10635_initialize(struct i2c_client *client)
+{
+	struct ov10635_priv *priv = to_ov10635(client);
+	u8 pid = 0, ver = 0;
+	int ret = 0;
+
+	ov10635_s_port(client, 1);
+
+	/* check and show product ID and manufacturer ID */
+	reg16_read(client, OV10635_PID, &pid);
+	reg16_read(client, OV10635_VER, &ver);
+
+	if (OV10635_VERSION(pid, ver) != OV10635_VERSION_REG) {
+		dev_dbg(&client->dev, "Product ID error %x:%x\n", pid, ver);
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/* s/w reset sensor */
+	reg16_write(client, 0x103, 0x1);
+	udelay(100);
+	/* Program wizard registers */
+	ov10635_set_regs(client, ov10635_regs_wizard, ARRAY_SIZE(ov10635_regs_wizard));
+	/* Set DVP bit swap */
+	reg16_write(client, 0x4709, priv->dvp_order << 4);
+	/* Read OTP IDs */
+	ov10635_otp_id_read(client);
+
+	dev_info(&client->dev, "ov10635 Product ID %x Manufacturer ID %x OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n",
+		 pid, ver, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+out:
+	ov10635_s_port(client, 0);
+
+	return ret;
+}
+
+static int ov10635_parse_dt(struct device_node *np, struct ov10635_priv *priv)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+	int i;
+	struct device_node *endpoint = NULL, *rendpoint = NULL;
+	int tmp_addr = 0;
+
+	for (i = 0; ; i++) {
+		endpoint = of_graph_get_next_endpoint(np, endpoint);
+		if (!endpoint)
+			break;
+
+		of_node_put(endpoint);
+
+		of_property_read_u32(endpoint, "dvp-order", &priv->dvp_order);
+
+		rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0);
+		if (!rendpoint)
+			continue;
+
+		if (!of_property_read_u32(rendpoint, "max9271-addr", &priv->max9271_addr) &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->max9286_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port))
+			break;
+
+		if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) &&
+		    !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti964-ti9x3") &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti964_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port))
+			break;
+
+		if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) &&
+		    !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti954-ti9x3") &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti954_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port))
+			break;
+	}
+
+	if (!priv->max9286_addr && !priv->ti964_addr && !priv->ti954_addr) {
+		dev_err(&client->dev, "deserializer does not present for OV10635\n");
+		return -EINVAL;
+	}
+
+	ov10635_s_port(client, 1);
+
+	/* setup I2C translator address */
+	tmp_addr = client->addr;
+	if (priv->max9286_addr) {
+		client->addr = priv->max9271_addr;			/* Serializer I2C address */
+
+		reg8_write(client, 0x09, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x0A, OV10635_I2C_ADDR << 1);	/* Sensor native I2C address */
+		usleep_range(2000, 2500);				/* wait 2ms */
+	};
+
+	if (priv->ti964_addr) {
+		client->addr = priv->ti964_addr;			/* Deserializer I2C address */
+
+		reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */
+		usleep_range(2000, 2500);				/* wait 2ms */
+		reg8_write(client, 0x65, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x5d, OV10635_I2C_ADDR << 1);	/* Sensor native I2C address */
+
+		reg8_write(client, 0x6e, 0xa9);				/* GPIO0 - resetb, GPIO1 - fsin */
+	}
+
+	if (priv->ti954_addr) {
+		client->addr = priv->ti954_addr;			/* Deserializer I2C address */
+
+		reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */
+		usleep_range(2000, 2500);				/* wait 2ms */
+		reg8_write(client, 0x65, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x5d, OV10635_I2C_ADDR << 1);	/* Sensor native I2C address */
+
+		reg8_write(client, 0x6e, 0xa9);				/* GPIO0 - resetb, GPIO1 - fsin */
+	}
+	client->addr = tmp_addr;
+
+	udelay(100);
+
+	return 0;
+}
+
+static int ov10635_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	struct ov10635_priv *priv;
+	struct v4l2_ctrl *ctrl;
+	int ret;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&priv->sd, client, &ov10635_subdev_ops);
+	priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+	priv->rect.left = 0;
+	priv->rect.top = 0;
+	priv->rect.width = OV10635_MAX_WIDTH;
+	priv->rect.height = OV10635_MAX_HEIGHT;
+	priv->fps_denominator = 30;
+
+	v4l2_ctrl_handler_init(&priv->hdl, 4);
+	v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x30);
+	v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_CONTRAST, 0, 4, 1, 2);
+	v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_SATURATION, 0, 0xff, 1, 0xff);
+	v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_HUE, 0, 255, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_GAMMA, 0, 0xffff, 1, 0x233);
+	v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+	v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_GAIN, 0, 0x3ff, 1, 0x10);
+	v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_EXPOSURE, 0, 0xffff, 1, 0x80);
+	v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_HFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_VFLIP, 0, 1, 1, 0);
+	ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops,
+			  V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 9);
+	if (ctrl)
+		ctrl->flags &= ~V4L2_CTRL_FLAG_READ_ONLY;
+	priv->sd.ctrl_handler = &priv->hdl;
+
+	ret = priv->hdl.error;
+	if (ret)
+		goto cleanup;
+
+	v4l2_ctrl_handler_setup(&priv->hdl);
+
+	priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+	priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad);
+	if (ret < 0)
+		goto cleanup;
+
+	ret = ov10635_parse_dt(client->dev.of_node, priv);
+	if (ret)
+		goto cleanup;
+
+	ret = ov10635_initialize(client);
+	if (ret < 0)
+		goto cleanup;
+
+	ret = v4l2_async_register_subdev(&priv->sd);
+	if (ret)
+		goto cleanup;
+
+	if (device_create_file(&client->dev, &dev_attr_otp_id_ov10635) != 0) {
+		dev_err(&client->dev, "sysfs otp_id entry creation failed\n");
+		goto cleanup;
+	}
+
+	priv->init_complete = 1;
+
+	return 0;
+
+cleanup:
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+#ifdef CONFIG_SOC_CAMERA_OV10635
+	v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
+		client->addr, client->adapter->name);
+#endif
+	return ret;
+}
+
+static int ov10635_remove(struct i2c_client *client)
+{
+	struct ov10635_priv *priv = i2c_get_clientdata(client);
+
+	device_remove_file(&client->dev, &dev_attr_otp_id_ov10635);
+	v4l2_async_unregister_subdev(&priv->sd);
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+
+	return 0;
+}
+
+#ifdef CONFIG_SOC_CAMERA_OV10635
+static const struct i2c_device_id ov10635_id[] = {
+	{ "ov10635", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov10635_id);
+
+static const struct of_device_id ov10635_of_ids[] = {
+	{ .compatible = "ovti,ov10635", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ov10635_of_ids);
+
+static struct i2c_driver ov10635_i2c_driver = {
+	.driver	= {
+		.name		= "ov10635",
+		.of_match_table	= ov10635_of_ids,
+	},
+	.probe		= ov10635_probe,
+	.remove		= ov10635_remove,
+	.id_table	= ov10635_id,
+};
+
+module_i2c_driver(ov10635_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OV10635");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/i2c/soc_camera/ov10635.h b/drivers/media/i2c/soc_camera/ov10635.h
new file mode 100644
index 0000000..a0e510d
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov10635.h
@@ -0,0 +1,1139 @@
+/*
+ * OmniVision ov10635 sensor camera wizard 1280x800@30/UYVY/BT601/8bit
+ *
+ * Copyright (C) 2015-2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+//#define OV10635_DISPLAY_PATTERN
+
+#define OV10635_SENSOR_WIDTH		1312
+#define OV10635_SENSOR_HEIGHT		814
+
+#define OV10635_MAX_WIDTH		1280
+#define OV10635_MAX_HEIGHT		800
+
+//#define OV10635_PCLK_96MHZ
+#define OV10635_PCLK_88MHZ
+
+#if defined(OV10635_PCLK_96MHZ)
+/* VTS=PCLK/FPS/HTS/2 (=96MHz/30/1600/2) */
+ #define OV10635_HTS			1600
+ #define OV10635_VTS			1000 /* fps=30 */
+#elif defined(OV10635_PCLK_88MHZ)
+/* VTS=PCLK/FPS/HTS/2 (=88MHz/1572/30/2) */
+ #define OV10635_HTS			1572
+ #define OV10635_VTS			933 /* fps=29.9998 */
+#else
+ #error PCLK not defined
+#endif
+
+struct ov10635_reg {
+	u16	reg;
+	u8	val;
+};
+
+static const struct ov10635_reg ov10635_regs_wizard[] = {
+//{0x0103, 0x01},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x300C, 0x61},
+{0x301B, 0xFF},
+{0x301C, 0xFF},
+{0x301A, 0xFF},
+{0x3011, 0x42},
+{0x6900, 0x0C},
+{0x6901, 0x19},
+{0x3503, 0x10},
+{0x3025, 0x03},
+#if defined(OV10635_PCLK_96MHZ)
+{0x3003, 0x20},
+{0x3004, 0x21},
+#elif defined(OV10635_PCLK_88MHZ)
+{0x3003, 0x16},
+{0x3004, 0x30},
+#endif
+{0x3005, 0x40},
+{0x3006, 0x91},
+{0x3600, 0x74},
+{0x3601, 0x2B},
+{0x3612, 0x00},
+{0x3611, 0x67},
+{0x3633, 0xCA},
+{0x3602, 0xAF},
+{0x3603, 0x04},
+{0x3630, 0x28},
+{0x3631, 0x16},
+{0x3714, 0x10},
+{0x371D, 0x01},
+{0x4300, 0x3A},
+{0x3007, 0x01},
+{0x3024, 0x03},
+{0x3020, 0x0A},
+{0x3702, 0x0D},
+{0x3703, 0x20},
+{0x3704, 0x15},
+{0x3709, 0xA8},
+{0x370C, 0xC7},
+{0x370D, 0x80},
+{0x3712, 0x00},
+{0x3713, 0x20},
+{0x3715, 0x04},
+{0x381D, 0x40},
+{0x381C, 0x00},
+{0x3822, 0x50},
+{0x3824, 0x10},
+{0x3815, 0x8C},
+{0x3804, 0x05},
+{0x3805, 0x1F},
+{0x3800, 0x00},
+{0x3801, 0x00},
+{0x3806, 0x03},
+{0x3807, 0x28},
+{0x3802, 0x00},
+{0x3803, 0x07},
+{0x3808, 0x05},
+{0x3809, 0x00},
+{0x380A, 0x03},
+{0x380B, 0x20},
+{0x380C, OV10635_HTS >> 8},
+{0x380D, OV10635_HTS & 0xff},
+{0x380E, OV10635_VTS >> 8},
+{0x380F, OV10635_VTS & 0xff},
+{0x3813, 0x02},
+{0x3811, 0x08},
+{0x381F, 0x0C},
+{0x3819, 0x04},
+{0x3804, 0x01},
+{0x3805, 0x00},
+{0x3828, 0x03},
+{0x3829, 0x10},
+{0x382A, 0x10},
+{0x3621, 0x63},
+{0x5005, 0x08},
+{0x56D5, 0x00},
+{0x56D6, 0x80},
+{0x56D7, 0x00},
+{0x56D8, 0x00},
+{0x56D9, 0x00},
+{0x56DA, 0x80},
+{0x56DB, 0x00},
+{0x56DC, 0x00},
+{0x56E8, 0x00},
+{0x56E9, 0x7F},
+{0x56EA, 0x00},
+{0x56EB, 0x7F},
+{0x5100, 0x00},
+{0x5101, 0x80},
+{0x5102, 0x00},
+{0x5103, 0x80},
+{0x5104, 0x00},
+{0x5105, 0x80},
+{0x5106, 0x00},
+{0x5107, 0x80},
+{0x5108, 0x00},
+{0x5109, 0x00},
+{0x510A, 0x00},
+{0x510B, 0x00},
+{0x510C, 0x00},
+{0x510D, 0x00},
+{0x510E, 0x00},
+{0x510F, 0x00},
+{0x5110, 0x00},
+{0x5111, 0x80},
+{0x5112, 0x00},
+{0x5113, 0x80},
+{0x5114, 0x00},
+{0x5115, 0x80},
+{0x5116, 0x00},
+{0x5117, 0x80},
+{0x5118, 0x00},
+{0x5119, 0x00},
+{0x511A, 0x00},
+{0x511B, 0x00},
+{0x511C, 0x00},
+{0x511D, 0x00},
+{0x511E, 0x00},
+{0x511F, 0x00},
+{0x56D0, 0x00},
+{0x5006, 0x04},
+{0x5608, 0x05},
+{0x52D7, 0x06},
+{0x528D, 0x08},
+{0x5293, 0x12},
+{0x52D3, 0x12},
+{0x5288, 0x06},
+{0x5289, 0x20},
+{0x52C8, 0x06},
+{0x52C9, 0x20},
+{0x52CD, 0x04},
+{0x5381, 0x00},
+{0x5382, 0xFF},
+{0x5589, 0x76},
+{0x558A, 0x47},
+{0x558B, 0xEF},
+{0x558C, 0xC9},
+{0x558D, 0x49},
+{0x558E, 0x30},
+{0x558F, 0x67},
+{0x5590, 0x3F},
+{0x5591, 0xF0},
+{0x5592, 0x10},
+{0x55A2, 0x6D},
+{0x55A3, 0x55},
+{0x55A4, 0xC3},
+{0x55A5, 0xB5},
+{0x55A6, 0x43},
+{0x55A7, 0x38},
+{0x55A8, 0x5F},
+{0x55A9, 0x4B},
+{0x55AA, 0xF0},
+{0x55AB, 0x10},
+{0x5581, 0x52},
+{0x5300, 0x01},
+{0x5301, 0x00},
+{0x5302, 0x00},
+{0x5303, 0x0E},
+{0x5304, 0x00},
+{0x5305, 0x0E},
+{0x5306, 0x00},
+{0x5307, 0x36},
+{0x5308, 0x00},
+{0x5309, 0xD9},
+{0x530A, 0x00},
+{0x530B, 0x0F},
+{0x530C, 0x00},
+{0x530D, 0x2C},
+{0x530E, 0x00},
+{0x530F, 0x59},
+{0x5310, 0x00},
+{0x5311, 0x7B},
+{0x5312, 0x00},
+{0x5313, 0x22},
+{0x5314, 0x00},
+{0x5315, 0xD5},
+{0x5316, 0x00},
+{0x5317, 0x13},
+{0x5318, 0x00},
+{0x5319, 0x18},
+{0x531A, 0x00},
+{0x531B, 0x26},
+{0x531C, 0x00},
+{0x531D, 0xDC},
+{0x531E, 0x00},
+{0x531F, 0x02},
+{0x5320, 0x00},
+{0x5321, 0x24},
+{0x5322, 0x00},
+{0x5323, 0x56},
+{0x5324, 0x00},
+{0x5325, 0x85},
+{0x5326, 0x00},
+{0x5327, 0x20},
+{0x5609, 0x01},
+{0x560A, 0x40},
+{0x560B, 0x01},
+{0x560C, 0x40},
+{0x560D, 0x00},
+{0x560E, 0xFA},
+{0x560F, 0x00},
+{0x5610, 0xFA},
+{0x5611, 0x02},
+{0x5612, 0x80},
+{0x5613, 0x02},
+{0x5614, 0x80},
+{0x5615, 0x01},
+{0x5616, 0x2C},
+{0x5617, 0x01},
+{0x5618, 0x2C},
+{0x563B, 0x01},
+{0x563C, 0x01},
+{0x563D, 0x01},
+{0x563E, 0x01},
+{0x563F, 0x03},
+{0x5640, 0x03},
+{0x5641, 0x03},
+{0x5642, 0x05},
+{0x5643, 0x09},
+{0x5644, 0x05},
+{0x5645, 0x05},
+{0x5646, 0x05},
+{0x5647, 0x05},
+{0x5651, 0x00},
+{0x5652, 0x80},
+{0x521A, 0x01},
+{0x521B, 0x03},
+{0x521C, 0x06},
+{0x521D, 0x0A},
+{0x521E, 0x0E},
+{0x521F, 0x12},
+{0x5220, 0x16},
+{0x5223, 0x02},
+{0x5225, 0x04},
+{0x5227, 0x08},
+{0x5229, 0x0C},
+{0x522B, 0x12},
+{0x522D, 0x18},
+{0x522F, 0x1E},
+{0x5241, 0x04},
+{0x5242, 0x01},
+{0x5243, 0x03},
+{0x5244, 0x06},
+{0x5245, 0x0A},
+{0x5246, 0x0E},
+{0x5247, 0x12},
+{0x5248, 0x16},
+{0x524A, 0x03},
+{0x524C, 0x04},
+{0x524E, 0x08},
+{0x5250, 0x0C},
+{0x5252, 0x12},
+{0x5254, 0x18},
+{0x5256, 0x1E},
+{0x4606, (2*OV10635_HTS) >> 8}, /* fifo_line_length = 2*hts */
+{0x4607, (2*OV10635_HTS) & 0xff},
+{0x460a, (2*(OV10635_HTS-OV10635_MAX_WIDTH)) >> 8}, /* fifo_hsync_start = 2*(hts - xres) */
+{0x460b, (2*(OV10635_HTS-OV10635_MAX_WIDTH)) & 0xff },
+{0x460C, 0x00},
+{0x4620, 0x0E},
+#if 0
+{0x4700, 0x02}, // BT656: mode is acceptable but artefact lines on left/bottom due to BT656 SAV/EAV are parsed as image data
+#else
+{0x4700, 0x04}, // BT601: 0x08 is also accaptable as HS/VS mode
+#endif
+{0x4701, 0x00},
+{0x4702, 0x01},
+{0x4004, 0x04},
+{0x4005, 0x18},
+{0x4001, 0x06},
+{0x4050, 0x22},
+{0x4051, 0x24},
+{0x4052, 0x02},
+{0x4057, 0x9C},
+{0x405A, 0x00},
+{0x4202, 0x02},
+{0x3023, 0x10},
+{0x0100, 0x01},
+{0x0100, 0x01},
+{0x6F10, 0x07},
+{0x6F11, 0x82},
+{0x6F12, 0x04},
+{0x6F13, 0x00},
+{0xD000, 0x19},
+{0xD001, 0xA0},
+{0xD002, 0x00},
+{0xD003, 0x01},
+{0xD004, 0xA9},
+{0xD005, 0xAD},
+{0xD006, 0x10},
+{0xD007, 0x40},
+{0xD008, 0x44},
+{0xD009, 0x00},
+{0xD00A, 0x68},
+{0xD00B, 0x00},
+{0xD00C, 0x15},
+{0xD00D, 0x00},
+{0xD00E, 0x00},
+{0xD00F, 0x00},
+{0xD040, 0x9C},
+{0xD041, 0x21},
+{0xD042, 0xFF},
+{0xD043, 0xF8},
+{0xD044, 0xD4},
+{0xD045, 0x01},
+{0xD046, 0x48},
+{0xD047, 0x00},
+{0xD048, 0xD4},
+{0xD049, 0x01},
+{0xD04A, 0x50},
+{0xD04B, 0x04},
+{0xD04C, 0x18},
+{0xD04D, 0x60},
+{0xD04E, 0x00},
+{0xD04F, 0x01},
+{0xD050, 0xA8},
+{0xD051, 0x63},
+{0xD052, 0x02},
+{0xD053, 0xA4},
+{0xD054, 0x85},
+{0xD055, 0x43},
+{0xD056, 0x00},
+{0xD057, 0x00},
+{0xD058, 0x18},
+{0xD059, 0x60},
+{0xD05A, 0x00},
+{0xD05B, 0x01},
+{0xD05C, 0xA8},
+{0xD05D, 0x63},
+{0xD05E, 0x03},
+{0xD05F, 0xF0},
+{0xD060, 0x98},
+{0xD061, 0xA3},
+{0xD062, 0x00},
+{0xD063, 0x00},
+{0xD064, 0x8C},
+{0xD065, 0x6A},
+{0xD066, 0x00},
+{0xD067, 0x6E},
+{0xD068, 0xE5},
+{0xD069, 0x85},
+{0xD06A, 0x18},
+{0xD06B, 0x00},
+{0xD06C, 0x10},
+{0xD06D, 0x00},
+{0xD06E, 0x00},
+{0xD06F, 0x10},
+{0xD070, 0x9C},
+{0xD071, 0x80},
+{0xD072, 0x00},
+{0xD073, 0x03},
+{0xD074, 0x18},
+{0xD075, 0x60},
+{0xD076, 0x00},
+{0xD077, 0x01},
+{0xD078, 0xA8},
+{0xD079, 0x63},
+{0xD07A, 0x07},
+{0xD07B, 0x80},
+{0xD07C, 0x07},
+{0xD07D, 0xFF},
+{0xD07E, 0xF9},
+{0xD07F, 0x03},
+{0xD080, 0x8C},
+{0xD081, 0x63},
+{0xD082, 0x00},
+{0xD083, 0x00},
+{0xD084, 0xA5},
+{0xD085, 0x6B},
+{0xD086, 0x00},
+{0xD087, 0xFF},
+{0xD088, 0x18},
+{0xD089, 0x80},
+{0xD08A, 0x00},
+{0xD08B, 0x01},
+{0xD08C, 0xA8},
+{0xD08D, 0x84},
+{0xD08E, 0x01},
+{0xD08F, 0x04},
+{0xD090, 0xE1},
+{0xD091, 0x6B},
+{0xD092, 0x58},
+{0xD093, 0x00},
+{0xD094, 0x94},
+{0xD095, 0x6A},
+{0xD096, 0x00},
+{0xD097, 0x70},
+{0xD098, 0xE1},
+{0xD099, 0x6B},
+{0xD09A, 0x20},
+{0xD09B, 0x00},
+{0xD09C, 0x95},
+{0xD09D, 0x6B},
+{0xD09E, 0x00},
+{0xD09F, 0x00},
+{0xD0A0, 0xE4},
+{0xD0A1, 0x8B},
+{0xD0A2, 0x18},
+{0xD0A3, 0x00},
+{0xD0A4, 0x0C},
+{0xD0A5, 0x00},
+{0xD0A6, 0x00},
+{0xD0A7, 0x23},
+{0xD0A8, 0x15},
+{0xD0A9, 0x00},
+{0xD0AA, 0x00},
+{0xD0AB, 0x00},
+{0xD0AC, 0x18},
+{0xD0AD, 0x60},
+{0xD0AE, 0x80},
+{0xD0AF, 0x06},
+{0xD0B0, 0xA8},
+{0xD0B1, 0x83},
+{0xD0B2, 0x40},
+{0xD0B3, 0x08},
+{0xD0B4, 0xA8},
+{0xD0B5, 0xE3},
+{0xD0B6, 0x38},
+{0xD0B7, 0x2A},
+{0xD0B8, 0xA8},
+{0xD0B9, 0xC3},
+{0xD0BA, 0x40},
+{0xD0BB, 0x09},
+{0xD0BC, 0xA8},
+{0xD0BD, 0xA3},
+{0xD0BE, 0x38},
+{0xD0BF, 0x29},
+{0xD0C0, 0x8C},
+{0xD0C1, 0x65},
+{0xD0C2, 0x00},
+{0xD0C3, 0x00},
+{0xD0C4, 0xD8},
+{0xD0C5, 0x04},
+{0xD0C6, 0x18},
+{0xD0C7, 0x00},
+{0xD0C8, 0x8C},
+{0xD0C9, 0x67},
+{0xD0CA, 0x00},
+{0xD0CB, 0x00},
+{0xD0CC, 0xD8},
+{0xD0CD, 0x06},
+{0xD0CE, 0x18},
+{0xD0CF, 0x00},
+{0xD0D0, 0x18},
+{0xD0D1, 0x60},
+{0xD0D2, 0x80},
+{0xD0D3, 0x06},
+{0xD0D4, 0xA8},
+{0xD0D5, 0xE3},
+{0xD0D6, 0x67},
+{0xD0D7, 0x02},
+{0xD0D8, 0xA9},
+{0xD0D9, 0x03},
+{0xD0DA, 0x67},
+{0xD0DB, 0x03},
+{0xD0DC, 0xA8},
+{0xD0DD, 0xC3},
+{0xD0DE, 0x3D},
+{0xD0DF, 0x05},
+{0xD0E0, 0x8C},
+{0xD0E1, 0x66},
+{0xD0E2, 0x00},
+{0xD0E3, 0x00},
+{0xD0E4, 0xB8},
+{0xD0E5, 0x63},
+{0xD0E6, 0x00},
+{0xD0E7, 0x18},
+{0xD0E8, 0xB8},
+{0xD0E9, 0x63},
+{0xD0EA, 0x00},
+{0xD0EB, 0x98},
+{0xD0EC, 0xBC},
+{0xD0ED, 0x03},
+{0xD0EE, 0x00},
+{0xD0EF, 0x00},
+{0xD0F0, 0x10},
+{0xD0F1, 0x00},
+{0xD0F2, 0x00},
+{0xD0F3, 0x16},
+{0xD0F4, 0xB8},
+{0xD0F5, 0x83},
+{0xD0F6, 0x00},
+{0xD0F7, 0x19},
+{0xD0F8, 0x8C},
+{0xD0F9, 0x67},
+{0xD0FA, 0x00},
+{0xD0FB, 0x00},
+{0xD0FC, 0xB8},
+{0xD0FD, 0xA4},
+{0xD0FE, 0x00},
+{0xD0FF, 0x98},
+{0xD100, 0xB8},
+{0xD101, 0x83},
+{0xD102, 0x00},
+{0xD103, 0x08},
+{0xD104, 0x8C},
+{0xD105, 0x68},
+{0xD106, 0x00},
+{0xD107, 0x00},
+{0xD108, 0xE0},
+{0xD109, 0x63},
+{0xD10A, 0x20},
+{0xD10B, 0x04},
+{0xD10C, 0xE0},
+{0xD10D, 0x65},
+{0xD10E, 0x18},
+{0xD10F, 0x00},
+{0xD110, 0xA4},
+{0xD111, 0x83},
+{0xD112, 0xFF},
+{0xD113, 0xFF},
+{0xD114, 0xB8},
+{0xD115, 0x64},
+{0xD116, 0x00},
+{0xD117, 0x48},
+{0xD118, 0xD8},
+{0xD119, 0x07},
+{0xD11A, 0x18},
+{0xD11B, 0x00},
+{0xD11C, 0xD8},
+{0xD11D, 0x08},
+{0xD11E, 0x20},
+{0xD11F, 0x00},
+{0xD120, 0x9C},
+{0xD121, 0x60},
+{0xD122, 0x00},
+{0xD123, 0x00},
+{0xD124, 0xD8},
+{0xD125, 0x06},
+{0xD126, 0x18},
+{0xD127, 0x00},
+{0xD128, 0x00},
+{0xD129, 0x00},
+{0xD12A, 0x00},
+{0xD12B, 0x08},
+{0xD12C, 0x15},
+{0xD12D, 0x00},
+{0xD12E, 0x00},
+{0xD12F, 0x00},
+{0xD130, 0x8C},
+{0xD131, 0x6A},
+{0xD132, 0x00},
+{0xD133, 0x76},
+{0xD134, 0xBC},
+{0xD135, 0x23},
+{0xD136, 0x00},
+{0xD137, 0x00},
+{0xD138, 0x13},
+{0xD139, 0xFF},
+{0xD13A, 0xFF},
+{0xD13B, 0xE6},
+{0xD13C, 0x18},
+{0xD13D, 0x60},
+{0xD13E, 0x80},
+{0xD13F, 0x06},
+{0xD140, 0x03},
+{0xD141, 0xFF},
+{0xD142, 0xFF},
+{0xD143, 0xDD},
+{0xD144, 0xA8},
+{0xD145, 0x83},
+{0xD146, 0x40},
+{0xD147, 0x08},
+{0xD148, 0x85},
+{0xD149, 0x21},
+{0xD14A, 0x00},
+{0xD14B, 0x00},
+{0xD14C, 0x85},
+{0xD14D, 0x41},
+{0xD14E, 0x00},
+{0xD14F, 0x04},
+{0xD150, 0x44},
+{0xD151, 0x00},
+{0xD152, 0x48},
+{0xD153, 0x00},
+{0xD154, 0x9C},
+{0xD155, 0x21},
+{0xD156, 0x00},
+{0xD157, 0x08},
+{0x6F0E, 0x03},
+{0x6F0F, 0x00},
+{0x460E, 0x08},
+{0x460F, 0x01},
+{0x4610, 0x00},
+{0x4611, 0x01},
+{0x4612, 0x00},
+{0x4613, 0x01},
+{0x4605, 0x08}, // 8bit
+//{0x4709, 0x10}, // swap data bits order [9:0] -> [0:9]
+{0x4608, 0x00},
+{0x4609, 0x08},
+{0x6804, 0x00},
+{0x6805, 0x06},
+{0x6806, 0x00},
+{0x5120, 0x00},
+{0x3510, 0x00},
+{0x3504, 0x00},
+{0x6800, 0x00},
+{0x6F0D, 0x01},
+{0x4708, 0x01}, // PCLK rising edge
+{0x5000, 0xFF},
+{0x5001, 0xBF},
+{0x5002, 0x7E},
+#ifdef OV10635_DISPLAY_PATTERN
+{0x503d, 0x80},
+#else
+{0x503D, 0x00},
+#endif
+{0xC450, 0x01}, /* AA mode */
+{0xC452, 0x04},
+{0xC453, 0x00},
+{0xC454, 0x00},
+{0xC455, 0x01},
+{0xC456, 0x01},
+{0xC457, 0x00},
+{0xC458, 0x00},
+{0xC459, 0x00},
+{0xC45B, 0x00},
+{0xC45C, 0x01},
+{0xC45D, 0x00},
+{0xC45E, 0x00},
+{0xC45F, 0x00},
+{0xC460, 0x00},
+{0xC461, 0x01},
+{0xC462, 0x01},
+{0xC464, 0x03},
+{0xC465, 0x00},
+{0xC466, 0x8A},
+{0xC467, 0x00},
+{0xC468, 0x86},
+{0xC469, 0x00},
+{0xC46A, 0x30},
+{0xC46B, 0x50},
+{0xC46C, 0x30},
+{0xC46D, 0x28},
+{0xC46E, 0x60},
+{0xC46F, 0x40},
+{0xC47C, 0x01},
+{0xC47D, 0x38},
+{0xC47E, 0x00},
+{0xC47F, 0x00},
+{0xC480, 0x00},
+{0xC481, 0xFF},
+{0xC482, 0x00},
+{0xC483, 0x40},
+{0xC484, 0x00},
+{0xC485, 0x18},
+{0xC486, 0x00},
+{0xC487, 0x18},
+{0xC488, (OV10635_VTS-8)*16 >> 8},
+{0xC489, (OV10635_VTS-8)*16 & 0xff},
+{0xC48A, (OV10635_VTS-8)*16 >> 8},
+{0xC48B, (OV10635_VTS-8)*16 & 0xff},
+{0xC48C, 0x00},
+{0xC48D, 0x04},
+{0xC48E, 0x00},
+{0xC48F, 0x04},
+{0xC490, 0x03},
+{0xC492, 0x20},
+{0xC493, 0x08},
+{0xC498, 0x02},
+{0xC499, 0x00},
+{0xC49A, 0x02},
+{0xC49B, 0x00},
+{0xC49C, 0x02},
+{0xC49D, 0x00},
+{0xC49E, 0x02},
+{0xC49F, 0x60},
+{0xC4A0, 0x03},
+{0xC4A1, 0x00},
+{0xC4A2, 0x04},
+{0xC4A3, 0x00},
+{0xC4A4, 0x00},
+{0xC4A5, 0x10},
+{0xC4A6, 0x00},
+{0xC4A7, 0x40},
+{0xC4A8, 0x00},
+{0xC4A9, 0x80},
+{0xC4AA, 0x0D},
+{0xC4AB, 0x00},
+{0xC4AC, 0x0F},
+{0xC4AD, 0xC0},
+{0xC4B4, 0x01},
+{0xC4B5, 0x01},
+{0xC4B6, 0x00},
+{0xC4B7, 0x01},
+{0xC4B8, 0x00},
+{0xC4B9, 0x01},
+{0xC4BA, 0x01},
+{0xC4BB, 0x00},
+{0xC4BC, 0x01},
+{0xC4BD, 0x60},
+{0xC4BE, 0x02},
+{0xC4BF, 0x33},
+{0xC4C8, 0x03},
+{0xC4C9, 0xD0},
+{0xC4CA, 0x0E},
+{0xC4CB, 0x00},
+{0xC4CC, 0x0E},
+{0xC4CD, 0x51},
+{0xC4CE, 0x0E},
+{0xC4CF, 0x51},
+{0xC4D0, 0x04},
+{0xC4D1, 0x80},
+{0xC4E0, 0x04},
+{0xC4E1, 0x02},
+{0xC4E2, 0x01},
+{0xC4E4, 0x10},
+{0xC4E5, 0x20},
+{0xC4E6, 0x30},
+{0xC4E7, 0x40},
+{0xC4E8, 0x50},
+{0xC4E9, 0x60},
+{0xC4EA, 0x70},
+{0xC4EB, 0x80},
+{0xC4EC, 0x90},
+{0xC4ED, 0xA0},
+{0xC4EE, 0xB0},
+{0xC4EF, 0xC0},
+{0xC4F0, 0xD0},
+{0xC4F1, 0xE0},
+{0xC4F2, 0xF0},
+{0xC4F3, 0x80},
+{0xC4F4, 0x00},
+{0xC4F5, 0x20},
+{0xC4F6, 0x02},
+{0xC4F7, 0x00},
+{0xC4F8, 0x00},
+{0xC4F9, 0x00},
+{0xC4FA, 0x00},
+{0xC4FB, 0x01},
+{0xC4FC, 0x01},
+{0xC4FD, 0x00},
+{0xC4FE, 0x04},
+{0xC4FF, 0x02},
+{0xC500, 0x48},
+{0xC501, 0x74},
+{0xC502, 0x58},
+{0xC503, 0x80},
+{0xC504, 0x05},
+{0xC505, 0x80},
+{0xC506, 0x03},
+{0xC507, 0x80},
+{0xC508, 0x01},
+{0xC509, 0xC0},
+{0xC50A, 0x01},
+{0xC50B, 0xA0},
+{0xC50C, 0x01},
+{0xC50D, 0x2C},
+{0xC50E, 0x01},
+{0xC50F, 0x0A},
+{0xC510, 0x00},
+{0xC511, 0x00},
+{0xC512, 0xE5},
+{0xC513, 0x14},
+{0xC514, 0x04},
+{0xC515, 0x00},
+{0xC518, OV10635_VTS >> 8},
+{0xC519, OV10635_VTS & 0xff},
+{0xC51A, OV10635_HTS >> 8},
+{0xC51B, OV10635_HTS & 0xff},
+{0xC2E0, 0x00},
+{0xC2E1, 0x51},
+{0xC2E2, 0x00},
+{0xC2E3, 0xD6},
+{0xC2E4, 0x01},
+{0xC2E5, 0x5E},
+{0xC2E9, 0x01},
+{0xC2EA, 0x7A},
+{0xC2EB, 0x90},
+{0xC2ED, 0x00},
+{0xC2EE, 0x7A},
+{0xC2EF, 0x64},
+{0xC308, 0x00},
+{0xC309, 0x00},
+{0xC30A, 0x00},
+{0xC30C, 0x00},
+{0xC30D, 0x01},
+{0xC30E, 0x00},
+{0xC30F, 0x00},
+{0xC310, 0x01},
+{0xC311, 0x60},
+{0xC312, 0xFF},
+{0xC313, 0x08},
+{0xC314, 0x01},
+{0xC315, 0x00}, /* min saturation gain */
+{0xC316, 0xFF}, /* max saturation gain */
+{0xC317, 0x0B},
+{0xC318, 0x00},
+{0xC319, 0x0C},
+{0xC31A, 0x00},
+{0xC31B, 0xE0},
+{0xC31C, 0x00},
+{0xC31D, 0x14},
+{0xC31E, 0x00},
+{0xC31F, 0xC5},
+{0xC320, 0xFF},
+{0xC321, 0x4B},
+{0xC322, 0xFF},
+{0xC323, 0xF0},
+{0xC324, 0xFF},
+{0xC325, 0xE8},
+{0xC326, 0x00},
+{0xC327, 0x46},
+{0xC328, 0xFF},
+{0xC329, 0xD2},
+{0xC32A, 0xFF},
+{0xC32B, 0xE4},
+{0xC32C, 0xFF},
+{0xC32D, 0xBB},
+{0xC32E, 0x00},
+{0xC32F, 0x61},
+{0xC330, 0xFF},
+{0xC331, 0xF9},
+{0xC332, 0x00},
+{0xC333, 0xD9},
+{0xC334, 0x00},
+{0xC335, 0x2E},
+{0xC336, 0x00},
+{0xC337, 0xB1},
+{0xC338, 0xFF},
+{0xC339, 0x64},
+{0xC33A, 0xFF},
+{0xC33B, 0xEB},
+{0xC33C, 0xFF},
+{0xC33D, 0xE8},
+{0xC33E, 0x00},
+{0xC33F, 0x48},
+{0xC340, 0xFF},
+{0xC341, 0xD0},
+{0xC342, 0xFF},
+{0xC343, 0xED},
+{0xC344, 0xFF},
+{0xC345, 0xAD},
+{0xC346, 0x00},
+{0xC347, 0x66},
+{0xC348, 0x01},
+{0xC349, 0x00},
+{0x6700, 0x04},
+{0x6701, 0x7B},
+{0x6702, 0xFD},
+{0x6703, 0xF9},
+{0x6704, 0x3D},
+{0x6705, 0x71},
+{0x6706, 0x78},
+{0x6708, 0x05},
+{0x6F06, 0x6F},
+{0x6F07, 0x00},
+{0x6F0A, 0x6F},
+{0x6F0B, 0x00},
+{0x6F00, 0x03},
+{0xC34C, 0x01},
+{0xC34D, 0x00},
+{0xC34E, 0x46},
+{0xC34F, 0x55},
+{0xC350, 0x00},
+{0xC351, 0x40},
+{0xC352, 0x00},
+{0xC353, 0xFF},
+{0xC354, 0x04},
+{0xC355, 0x08},
+{0xC356, 0x01},
+{0xC357, 0xEF},
+{0xC358, 0x30},
+{0xC359, 0x01},
+{0xC35A, 0x64},
+{0xC35B, 0x46},
+{0xC35C, 0x00},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0x3042, 0xF0},
+{0xC261, 0x01},
+{0x301B, 0xF0},
+{0x301C, 0xF0},
+{0x301A, 0xF0},
+{0x6F00, 0xC3},
+{0xC46A, 0x30},
+{0xC46D, 0x20},
+{0xC464, 0x84},
+{0xC465, 0x00},
+{0x6F00, 0x03},
+{0x6F00, 0x43},
+{0x381C, 0x00},
+{0x381D, 0x40},
+{0xC454, 0x01},
+{0x6F00, 0xC3},
+{0xC454, 0x00},
+{0xC4B1, 0x02},
+{0xC4B2, 0x01},
+{0xC4B3, 0x03},
+{0x6F00, 0x03},
+{0x6F00, 0x43},
+/* enable FSIN (FRAMESYNC input) functionality */
+{0x3832, (0x0d+2*0x20+0x15+38) >> 8},
+{0x3833, (0x0d+2*0x20+0x15+38) & 0xff},
+{0x3834, OV10635_VTS >> 8},
+{0x3835, OV10635_VTS & 0xff},
+{0x302E, 0x01},
+};
+
+static const struct ov10635_reg ov10635_regs_30fps[] = {
+/* disable clocks */
+{0x301b, 0xff},
+{0x301c, 0xff},
+{0x301a, 0xff},
+/* clk = 24Mhz/2*32/2(1+1)=96Mhz, 30fps */
+{0x3003, 0x20},
+{0x3004, 0x21},
+/* enable clocks */
+{0x301b, 0xf0},
+{0x301c, 0xf0},
+{0x301a, 0xf0},
+};
+
+static const struct ov10635_reg ov10635_regs_15fps[] = {
+/* disable clocks */
+{0x301b, 0xff},
+{0x301c, 0xff},
+{0x301a, 0xff},
+/* clk = 24Mhz/2*32/2(1+3)=48Mhz, 15fps */
+{0x3003, 0x20},
+{0x3004, 0x23},
+/* enable clocks */
+{0x301b, 0xf0},
+{0x301c, 0xf0},
+{0x301a, 0xf0},
+};
+
+static const struct ov10635_reg ov10635_regs_10fps[] = {
+/* disable clocks */
+{0x301b, 0xff},
+{0x301c, 0xff},
+{0x301a, 0xff},
+/* clk = 24Mhz/2*32/2(1+5)=32Mhz, 10fps */
+{0x3003, 0x20},
+{0x3004, 0x25},
+/* enable clocks */
+{0x301b, 0xf0},
+{0x301c, 0xf0},
+{0x301a, 0xf0},
+};
+
+static const struct ov10635_reg ov10635_regs_5fps[] = {
+/* disable clocks */
+{0x301b, 0xff},
+{0x301c, 0xff},
+{0x301a, 0xff},
+/* clk = 24Mhz/4*32/2(1+5)=96Mhz, 5fps */
+{0x3003, 0x20},
+{0x3004, 0x45},
+/* enable clocks */
+{0x301b, 0xf0},
+{0x301c, 0xf0},
+{0x301a, 0xf0},
+};
+
+static const struct ov10635_reg ov10635_regs_contrast[5][18] = {
+{
+	{0x6f00, 0xc3},
+	{0xc4e4, 0x20},
+	{0xc4e5, 0x40},
+	{0xc4e6, 0x60},
+	{0xc4e7, 0x80},
+	{0xc4e8, 0xa0},
+	{0xc4e9, 0xb4},
+	{0xc4ea, 0xc0},
+	{0xc4eb, 0xcb},
+	{0xc4ec, 0xd5},
+	{0xc4ed, 0xde},
+	{0xc4ee, 0xe6},
+	{0xc4ef, 0xed},
+	{0xc4f0, 0xf3},
+	{0xc4f1, 0xf8},
+	{0xc4f2, 0xfc},
+	{0x6f00, 0x03},
+	{0x6f00, 0x43},
+}, {
+	{0x6f00, 0xc3},
+	{0xc4e4, 0x18},
+	{0xc4e5, 0x30},
+	{0xc4e6, 0x48},
+	{0xc4e7, 0x60},
+	{0xc4e8, 0x78},
+	{0xc4e9, 0x90},
+	{0xc4ea, 0xa4},
+	{0xc4eb, 0xb4},
+	{0xc4ec, 0xc2},
+	{0xc4ed, 0xcf},
+	{0xc4ee, 0xdb},
+	{0xc4ef, 0xe5},
+	{0xc4f0, 0xee},
+	{0xc4f1, 0xf6},
+	{0xc4f2, 0xfc},
+	{0x6f00, 0x03},
+	{0x6f00, 0x43},
+}, {
+	{0x6f00, 0xc3},
+	{0xc4e4, 0x10},
+	{0xc4e5, 0x20},
+	{0xc4e6, 0x30},
+	{0xc4e7, 0x40},
+	{0xc4e8, 0x50},
+	{0xc4e9, 0x60},
+	{0xc4ea, 0x70},
+	{0xc4eb, 0x80},
+	{0xc4ec, 0x90},
+	{0xc4ed, 0xa0},
+	{0xc4ee, 0xb0},
+	{0xc4ef, 0xc0},
+	{0xc4f0, 0xd0},
+	{0xc4f1, 0xe0},
+	{0xc4f2, 0xf0},
+	{0x6f00, 0x03},
+	{0x6f00, 0x43},
+}, {
+	{0x6f00, 0xc3},
+	{0xc4e4, 0x0c},
+	{0xc4e5, 0x18},
+	{0xc4e6, 0x24},
+	{0xc4e7, 0x30},
+	{0xc4e8, 0x3c},
+	{0xc4e9, 0x48},
+	{0xc4ea, 0x54},
+	{0xc4eb, 0x62},
+	{0xc4ec, 0x72},
+	{0xc4ed, 0x84},
+	{0xc4ee, 0x94},
+	{0xc4ef, 0xa6},
+	{0xc4f0, 0xb9},
+	{0xc4f1, 0xcd},
+	{0xc4f2, 0xe2},
+	{0x6f00, 0x03},
+	{0x6f00, 0x43},
+}, {
+	{0x6f00, 0xc3},
+	{0xc4e4, 0x06},
+	{0xc4e5, 0x0d},
+	{0xc4e6, 0x15},
+	{0xc4e7, 0x1e},
+	{0xc4e8, 0x28},
+	{0xc4e9, 0x32},
+	{0xc4ea, 0x3c},
+	{0xc4eb, 0x48},
+	{0xc4ec, 0x56},
+	{0xc4ed, 0x66},
+	{0xc4ee, 0x78},
+	{0xc4ef, 0x8c},
+	{0xc4f0, 0xa2},
+	{0xc4f1, 0xba},
+	{0xc4f2, 0xd4},
+	{0x6f00, 0x03},
+	{0x6f00, 0x43},
+}
+};
diff --git a/drivers/media/i2c/soc_camera/ov10635_debug.h b/drivers/media/i2c/soc_camera/ov10635_debug.h
new file mode 100644
index 0000000..4c3515a
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov10635_debug.h
@@ -0,0 +1,54 @@
+
+#if 0
+{0x4700, 0x02}, // BT656
+{0x381d, 0x40}, // mirror off
+{0x381c, 0x00}, // flip off
+{0x4300, 0x3a}, // YUV: UYVY
+{0x4708, 0x00}, // PCLK rising edge
+
+// clk = 24Mhz/3*22/2= 88Mhz
+{0x3003, 0x16},
+{0x3004, 0x30},
+#endif
+
+#define WIDTH 1280
+#define HEIGHT 720
+
+// DVP frame size
+{0x3808, WIDTH >> 8},
+{0x3809, WIDTH & 0xff},
+{0x380a, HEIGHT >> 8},
+{0x380b, HEIGHT & 0xff},
+
+{0x3802, ((814 - HEIGHT)/2) >> 8}, // vert crop start
+{0x3803, ((814 - HEIGHT)/2) & 0xff},
+{0x3806, ((814 - HEIGHT)/2 + HEIGHT + 1) >> 8}, // vert crop end
+{0x3807, ((814 - HEIGHT)/2 + HEIGHT + 1) & 0xff},
+
+#if 0
+#define HTS 0x6f6 // got from above table 1782
+#define VTS (0x2ec+80) // got from above table 748 + 80
+
+{0x380c, HTS >> 8}, // hts
+{0x380d, HTS & 0xff},
+{0x380e, VTS >> 8}, // vts
+{0x380f, VTS & 0xff},
+
+// fifo
+{0x4606, (2*HTS) >> 8}, // fifo_line_length = 2*hts
+{0x4607, (2*HTS) & 0xff},
+{0x460a, (2*(HTS-1280)) >> 8}, // fifo_hsync_start = 2*(hts - xres)
+{0x460b, (2*(HTS-1280)) & 0xff },
+
+// exposure
+{0xC488, (VTS-8)*16 >> 8},
+{0xC489, (VTS-8)*16 & 0xff},
+{0xC48A, (VTS-8)*16 >> 8},
+{0xC48B, (VTS-8)*16 & 0xff},
+
+// vts/hts
+{0xC518, VTS >> 8},
+{0xC519, VTS & 0xff},
+{0xC51A, HTS >> 8},
+{0xC51B, HTS & 0xff},
+#endif
diff --git a/drivers/media/i2c/soc_camera/ov106xx.c b/drivers/media/i2c/soc_camera/ov106xx.c
new file mode 100644
index 0000000..fa775ae
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov106xx.c
@@ -0,0 +1,139 @@
+/*
+ * OmniVision ov10635/ov490-ov10640/ov495-ov2775 sensor camera driver
+ *
+ * Copyright (C) 2016-2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include "ov10635.c"
+#include "ov490_ov10640.c"
+#include "ov495_ov2775.c"
+#include "ar0132.c"
+#include "ar0220.c"
+#include "ap0101_ar014x.c"
+#include "ov2775.c"
+
+static enum {
+	ID_OV10635,
+	ID_OV490_OV10640,
+	ID_OV495_OV2775,
+	ID_AR0132,
+	ID_AR0220,
+	ID_AP0101_AR014X,
+	ID_OV2775,
+} chip_id;
+
+static int ov106xx_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	int ret;
+	chip_id = -EINVAL;
+
+	ret = ov10635_probe(client, did);
+	if (!ret) {
+		chip_id = ID_OV10635;
+		goto out;
+	}
+
+	ret = ov490_probe(client, did);
+	if (!ret) {
+		chip_id = ID_OV490_OV10640;
+		goto out;
+	}
+
+	ret = ov495_probe(client, did);
+	if (!ret) {
+		chip_id = ID_OV495_OV2775;
+		goto out;
+	}
+
+	ret = ar0132_probe(client, did);
+	if (!ret) {
+		chip_id = ID_AR0132;
+		goto out;
+	}
+
+	ret = ar0220_probe(client, did);
+	if (!ret) {
+		chip_id = ID_AR0220;
+		goto out;
+	}
+
+	ret = ap0101_probe(client, did);
+	if (!ret) {
+		chip_id = ID_AP0101_AR014X;
+		goto out;
+	}
+
+	ret = ov2775_probe(client, did);
+	if (!ret) {
+		chip_id = ID_OV2775;
+		goto out;
+	}
+
+	v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
+			client->addr, client->adapter->name);
+out:
+	return ret;
+}
+
+static int ov106xx_remove(struct i2c_client *client)
+{
+	switch (chip_id) {
+	case ID_OV10635:
+		ov10635_remove(client);
+		break;
+	case ID_OV490_OV10640:
+		ov490_remove(client);
+		break;
+	case ID_OV495_OV2775:
+		ov495_remove(client);
+		break;
+	case ID_AR0132:
+		ar0132_remove(client);
+		break;
+	case ID_AR0220:
+		ar0220_remove(client);
+		break;
+	case ID_AP0101_AR014X:
+		ap0101_remove(client);
+		break;
+	case ID_OV2775:
+		ov2775_remove(client);
+		break;
+	};
+
+	return 0;
+}
+
+static const struct i2c_device_id ov106xx_id[] = {
+	{ "ov106xx", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov106xx_id);
+
+static const struct of_device_id ov106xx_of_ids[] = {
+	{ .compatible = "ovti,ov106xx", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ov106xx_of_ids);
+
+static struct i2c_driver ov106xx_i2c_driver = {
+	.driver	= {
+		.name		= "ov106xx",
+		.of_match_table	= ov106xx_of_ids,
+	},
+	.probe		= ov106xx_probe,
+	.remove		= ov106xx_remove,
+	.id_table	= ov106xx_id,
+};
+
+module_i2c_driver(ov106xx_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OV10635, OV490/OV10640, OV495/OV2775, AR0132, AR0220, AP0101/AR014X");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/soc_camera/ov2775.c b/drivers/media/i2c/soc_camera/ov2775.c
new file mode 100644
index 0000000..feb547f
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov2775.c
@@ -0,0 +1,528 @@
+/*
+ * OmniVision OV2775 sensor camera driver
+ *
+ * Copyright (C) 2018 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+
+#include "ov2775.h"
+
+#define OV2775_I2C_ADDR		0x36
+
+#define OV2775_PID		0x300a
+#define OV2775_VER		0x300b
+#define OV2775_VERSION_REG	0x2770
+
+#define OV2775_MEDIA_BUS_FMT	MEDIA_BUS_FMT_SBGGR8_1X8
+
+struct ov2775_priv {
+	struct v4l2_subdev		sd;
+	struct v4l2_ctrl_handler	hdl;
+	struct media_pad		pad;
+	struct v4l2_rect		rect;
+	int				init_complete;
+	u8				id[6];
+	int				exposure;
+	int				gain;
+	int				autogain;
+	/* serializers */
+	int				ti9x4_addr;
+	int				ti9x3_addr;
+	int				port;
+	int				gpio_resetb;
+	int				gpio_fsin;
+
+};
+
+static inline struct ov2775_priv *to_ov2775(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ov2775_priv, sd);
+}
+
+static int ov2775_set_regs(struct i2c_client *client,
+			  const struct ov2775_reg *regs, int nr_regs)
+{
+	int i;
+
+	for (i = 0; i < nr_regs; i++) {
+		if (regs[i].reg == OV2775_DELAY) {
+			mdelay(regs[i].val);
+			continue;
+		}
+
+		reg16_write(client, regs[i].reg, regs[i].val);
+	}
+
+	return 0;
+}
+
+static int ov2775_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static int ov2775_get_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov2775_priv *priv = to_ov2775(client);
+
+	if (format->pad)
+		return -EINVAL;
+
+	mf->width = priv->rect.width;
+	mf->height = priv->rect.height;
+	mf->code = OV2775_MEDIA_BUS_FMT;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int ov2775_set_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+
+	mf->code = OV2775_MEDIA_BUS_FMT;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		cfg->try_fmt = *mf;
+
+	return 0;
+}
+
+static int ov2775_enum_mbus_code(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->pad || code->index > 0)
+		return -EINVAL;
+
+	code->code = OV2775_MEDIA_BUS_FMT;
+
+	return 0;
+}
+
+static int ov2775_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov2775_priv *priv = to_ov2775(client);
+
+	memcpy(edid->edid, priv->id, 6);
+
+	edid->edid[6] = 0xff;
+	edid->edid[7] = client->addr;
+	edid->edid[8] = OV2775_VERSION_REG >> 8;
+	edid->edid[9] = OV2775_VERSION_REG & 0xff;
+
+	return 0;
+}
+
+static int ov2775_set_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct v4l2_rect *rect = &sel->r;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov2775_priv *priv = to_ov2775(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
+	    sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	rect->left = ALIGN(rect->left, 2);
+	rect->top = ALIGN(rect->top, 2);
+	rect->width = ALIGN(rect->width, 2);
+	rect->height = ALIGN(rect->height, 2);
+
+	if ((rect->left + rect->width > OV2775_MAX_WIDTH) ||
+	    (rect->top + rect->height > OV2775_MAX_HEIGHT))
+		*rect = priv->rect;
+
+	priv->rect.left = rect->left;
+	priv->rect.top = rect->top;
+	priv->rect.width = rect->width;
+	priv->rect.height = rect->height;
+
+	return 0;
+}
+
+static int ov2775_get_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov2775_priv *priv = to_ov2775(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = OV2775_MAX_WIDTH;
+		sel->r.height = OV2775_MAX_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = OV2775_MAX_WIDTH;
+		sel->r.height = OV2775_MAX_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP:
+		sel->r = priv->rect;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ov2775_g_mbus_config(struct v4l2_subdev *sd,
+			       struct v4l2_mbus_config *cfg)
+{
+	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	cfg->type = V4L2_MBUS_CSI2;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov2775_g_register(struct v4l2_subdev *sd,
+			    struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+	u8 val = 0;
+
+	ret = reg16_read(client, (u16)reg->reg, &val);
+	if (ret < 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = sizeof(u8);
+
+	return 0;
+}
+
+static int ov2775_s_register(struct v4l2_subdev *sd,
+			    const struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	return reg16_write(client, (u16)reg->reg, (u8)reg->val);
+}
+#endif
+
+static struct v4l2_subdev_core_ops ov2775_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = ov2775_g_register,
+	.s_register = ov2775_s_register,
+#endif
+};
+
+static int ov2775_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov2775_priv *priv = to_ov2775(client);
+	int ret = -EINVAL;
+
+	if (!priv->init_complete)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+	case V4L2_CID_CONTRAST:
+	case V4L2_CID_SATURATION:
+	case V4L2_CID_HUE:
+	case V4L2_CID_GAMMA:
+	case V4L2_CID_SHARPNESS:
+	case V4L2_CID_AUTOGAIN:
+	case V4L2_CID_GAIN:
+	case V4L2_CID_EXPOSURE:
+	case V4L2_CID_HFLIP:
+	case V4L2_CID_VFLIP:
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov2775_ctrl_ops = {
+	.s_ctrl = ov2775_s_ctrl,
+};
+
+static struct v4l2_subdev_video_ops ov2775_video_ops = {
+	.s_stream	= ov2775_s_stream,
+	.g_mbus_config	= ov2775_g_mbus_config,
+};
+
+static const struct v4l2_subdev_pad_ops ov2775_subdev_pad_ops = {
+	.get_edid	= ov2775_get_edid,
+	.enum_mbus_code	= ov2775_enum_mbus_code,
+	.get_selection	= ov2775_get_selection,
+	.set_selection	= ov2775_set_selection,
+	.get_fmt	= ov2775_get_fmt,
+	.set_fmt	= ov2775_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov2775_subdev_ops = {
+	.core	= &ov2775_core_ops,
+	.video	= &ov2775_video_ops,
+	.pad	= &ov2775_subdev_pad_ops,
+};
+
+static void ov2775_otp_id_read(struct i2c_client *client)
+{
+}
+
+static ssize_t ov2775_otp_id_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev));
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov2775_priv *priv = to_ov2775(client);
+
+	return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+			priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+}
+
+static DEVICE_ATTR(otp_id_ov2775, S_IRUGO, ov2775_otp_id_show, NULL);
+
+static int ov2775_initialize(struct i2c_client *client)
+{
+	struct ov2775_priv *priv = to_ov2775(client);
+	u8 val = 0;
+	u16 pid;
+	int ret = 0;
+
+	/* check and show model ID */
+	reg16_read(client, OV2775_PID, &val);
+	pid = val;
+	reg16_read(client, OV2775_VER, &val);
+	pid = (pid << 8) | val;
+
+	if (pid != OV2775_VERSION_REG) {
+		dev_dbg(&client->dev, "Product ID error %x\n", pid);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	/* Program wizard registers */
+	ov2775_set_regs(client, ov2775_regs_wizard, ARRAY_SIZE(ov2775_regs_wizard));
+	/* Read OTP IDs */
+	ov2775_otp_id_read(client);
+
+	dev_info(&client->dev, "ov2775 PID %x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n",
+		 pid, OV2775_MAX_WIDTH, OV2775_MAX_HEIGHT, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+err:
+	return ret;
+}
+
+static int ov2775_parse_dt(struct device_node *np, struct ov2775_priv *priv)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+	int i;
+	struct device_node *endpoint = NULL, *rendpoint = NULL;
+	int tmp_addr = 0;
+
+	for (i = 0; ; i++) {
+		endpoint = of_graph_get_next_endpoint(np, endpoint);
+		if (!endpoint)
+			break;
+
+		of_node_put(endpoint);
+
+		rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0);
+		if (!rendpoint)
+			continue;
+
+		if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) &&
+		    !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti9x4") &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti9x4_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port))
+			break;
+	}
+
+	if (!priv->ti9x4_addr) {
+		dev_err(&client->dev, "deserializer does not present\n");
+		return -EINVAL;
+	}
+
+	/* setup I2C translator address */
+	tmp_addr = client->addr;
+	if (priv->ti9x4_addr) {
+		client->addr = priv->ti9x4_addr;			/* Deserializer I2C address */
+		reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */
+		usleep_range(2000, 2500);				/* wait 2ms */
+		reg8_write(client, 0x65, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x5d, OV2775_I2C_ADDR << 1);		/* Sensor native I2C address */
+//		reg8_write(client, 0x6e, 0xa9);				/* GPIO0 - reset, GPIO1 - fsin */
+
+		client->addr = priv->ti9x3_addr;			/* Serializer I2C address */
+		reg8_write(client, 0x0d, 0x03);				/* unreset gpios */
+		reg8_write(client, 0x0e, 0xf0);				/* unreset gpios */
+	}
+	client->addr = tmp_addr;
+
+	mdelay(10);
+
+	return 0;
+}
+
+static int ov2775_probe(struct i2c_client *client,
+		       const struct i2c_device_id *did)
+{
+	struct ov2775_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&priv->sd, client, &ov2775_subdev_ops);
+	priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	priv->exposure = 0x100;
+	priv->gain = 0x100;
+	priv->autogain = 1;
+	v4l2_ctrl_handler_init(&priv->hdl, 4);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_BRIGHTNESS, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_CONTRAST, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_SATURATION, 0, 7, 1, 2);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_HUE, 0, 23, 1, 12);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_GAMMA, -128, 128, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_SHARPNESS, 0, 10, 1, 3);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_AUTOGAIN, 0, 1, 1, priv->autogain);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_GAIN, 0, 0xffff, 1, priv->gain);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_EXPOSURE, 0, 0xffff, 1, priv->exposure);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_HFLIP, 0, 1, 1, 1);
+	v4l2_ctrl_new_std(&priv->hdl, &ov2775_ctrl_ops,
+			  V4L2_CID_VFLIP, 0, 1, 1, 0);
+	priv->sd.ctrl_handler = &priv->hdl;
+
+	ret = priv->hdl.error;
+	if (ret)
+		goto cleanup;
+
+	v4l2_ctrl_handler_setup(&priv->hdl);
+
+	priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+	priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad);
+	if (ret < 0)
+		goto cleanup;
+
+	ret = ov2775_parse_dt(client->dev.of_node, priv);
+	if (ret)
+		goto cleanup;
+
+	ret = ov2775_initialize(client);
+	if (ret < 0)
+		goto cleanup;
+
+	priv->rect.left = 0;
+	priv->rect.top = 0;
+	priv->rect.width = OV2775_MAX_WIDTH;
+	priv->rect.height = OV2775_MAX_HEIGHT;
+
+	ret = v4l2_async_register_subdev(&priv->sd);
+	if (ret)
+		goto cleanup;
+
+	if (device_create_file(&client->dev, &dev_attr_otp_id_ov2775) != 0) {
+		dev_err(&client->dev, "sysfs otp_id entry creation failed\n");
+		goto cleanup;
+	}
+
+	priv->init_complete = 1;
+
+	return 0;
+
+cleanup:
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+#ifdef CONFIG_SOC_CAMERA_OV2775
+	v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
+		client->addr, client->adapter->name);
+#endif
+	return ret;
+}
+
+static int ov2775_remove(struct i2c_client *client)
+{
+	struct ov2775_priv *priv = i2c_get_clientdata(client);
+
+	device_remove_file(&client->dev, &dev_attr_otp_id_ov2775);
+	v4l2_async_unregister_subdev(&priv->sd);
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+
+	return 0;
+}
+
+#ifdef CONFIG_SOC_CAMERA_OV2775
+static const struct i2c_device_id ov2775_id[] = {
+	{ "ov2775", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov2775_id);
+
+static const struct of_device_id ov2775_of_ids[] = {
+	{ .compatible = "ovti,ov2775", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ov2775_of_ids);
+
+static struct i2c_driver ov2775_i2c_driver = {
+	.driver	= {
+		.name		= "ov2775",
+		.of_match_table	= ov2775_of_ids,
+	},
+	.probe		= ov2775_probe,
+	.remove		= ov2775_remove,
+	.id_table	= ov2775_id,
+};
+
+module_i2c_driver(ov2775_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OV2775");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/i2c/soc_camera/ov2775.h b/drivers/media/i2c/soc_camera/ov2775.h
new file mode 100644
index 0000000..1cdfb50
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov2775.h
@@ -0,0 +1,1841 @@
+/*
+ * OmniVision OV2775 sensor camera wizard 1928x1088@30/RGGB/MIPI
+ *
+ * Copyright (C) 2018 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+//#define OV2775_DISPLAY_PATTERN_COLOR_BAR
+
+#define OV2775_MAX_WIDTH	2880 // (1928*1.5=2892) <- must be multiple of 16 - requred by R-CAR VIN
+#define OV2775_MAX_HEIGHT	1088
+
+#define OV2775_DELAY		0xffff
+#define OV2775_DT		0x2c // MIPI Data Type
+
+struct ov2775_reg {
+	u16	reg;
+	u8	val;
+};
+
+/* wizard: MIPI 1928x1088 RAW12 Linear 30fps 960Mbps */
+static const struct ov2775_reg ov2775_regs_wizard[] = {
+{0x3013, 0x01}, // s/w reset
+{OV2775_DELAY, 10}, // Wait 10ms
+{0x3000, 0x02},
+{0x3001, 0x28},
+{0x3002, 0x03},
+{0x3003, 0x01},
+{0x3004, 0x02},
+{0x3005, 0x26},
+{0x3006, 0x00},
+{0x3007, 0x07},
+{0x3008, 0x01},
+{0x3009, 0x00},
+{0x300c, 0x6c},
+{0x300e, 0x80},
+{0x300f, 0x00},
+{0x3012, 0x00},
+{0x3013, 0x00},
+{0x3014, 0xc4},
+{0x3015, 0x00},
+{0x3017, 0x00},
+{0x3018, 0x00},
+{0x3019, 0x00},
+{0x301a, 0x00},
+{0x301b, 0x01},
+{0x301e, 0x17},
+{0x301f, 0xe1},
+{0x3030, 0x02},
+{0x3031, 0x72},
+{0x3032, 0xf0},
+{0x3033, 0x30},
+{0x3034, 0x3f},
+{0x3035, 0x5f},
+{0x3036, 0x02},
+{0x3037, 0x9f},
+{0x3038, 0x04},
+{0x3039, 0xb7},
+{0x303a, 0x04},
+{0x303b, 0x07},
+{0x303c, 0xf0},
+{0x303d, 0x00},
+{0x303e, 0x0b},
+{0x303f, 0xe3},
+{0x3040, 0xf3},
+{0x3041, 0x29},
+{0x3042, 0xf6},
+{0x3043, 0x65},
+{0x3044, 0x06},
+{0x3045, 0x0f},
+{0x3046, 0x59},
+{0x3047, 0x07},
+{0x3048, 0x82},
+{0x3049, 0xcf},
+{0x304a, 0x12},
+{0x304b, 0x40},
+{0x304c, 0x33},
+{0x304d, 0xa4},
+{0x304e, 0x0b},
+{0x304f, 0x3d},
+{0x3050, 0x10},
+{0x3060, 0x00},
+{0x3061, 0x64},
+{0x3062, 0x00},
+{0x3063, 0xe4},
+{0x3066, 0x80},
+{0x3080, 0x00},
+{0x3081, 0x00},
+{0x3082, 0x01},
+{0x3083, 0xe3},
+{0x3084, 0x06},
+{0x3085, 0x00},
+{0x3086, 0x10},
+{0x3087, 0x10},
+{0x3089, 0x00},
+{0x308a, 0x01},
+{0x3093, 0x00},
+{0x30a0, 0x00},
+{0x30a1, 0x00},
+{0x30a2, 0x00},
+{0x30a3, 0x00},
+{0x30a4, 0x07},
+{0x30a5, 0x8f},
+{0x30a6, 0x04},
+{0x30a7, 0x47},
+{0x30a8, 0x00},
+{0x30a9, 0x00},
+{0x30aa, 0x00},
+{0x30ab, 0x00},
+{0x30ac, 0x07},
+{0x30ad, 0x90},
+{0x30ae, 0x04},
+{0x30af, 0x48},
+{0x30b0, 0x04},
+{0x30b1, 0x7e},
+{0x30b2, 0x04},
+{0x30b3, 0x65},
+{0x30b4, 0x00},
+{0x30b5, 0x00},
+{0x30b6, 0x00},
+{0x30b7, 0x10},
+{0x30b8, 0x00},
+{0x30b9, 0x02},
+{0x30ba, 0x10},
+{0x30bb, 0x00},
+{0x30bc, 0x00},
+{0x30bd, 0x03},
+{0x30be, 0x5c},
+{0x30bf, 0x00},
+{0x30c0, 0x01},
+{0x30c1, 0x00},
+{0x30c2, 0x20},
+{0x30c3, 0x00},
+{0x30c4, 0x4a},
+{0x30c5, 0x00},
+{0x30c7, 0x00},
+{0x30c8, 0x00},
+{0x30d1, 0x00},
+{0x30d2, 0x00},
+{0x30d3, 0x80},
+{0x30d4, 0x00},
+{0x30d9, 0x09},
+{0x30da, 0x64},
+{0x30dd, 0x00},
+{0x30de, 0x16},
+{0x30df, 0x00},
+{0x30e0, 0x17},
+{0x30e1, 0x00},
+{0x30e2, 0x18},
+{0x30e3, 0x10},
+{0x30e4, 0x04},
+{0x30e5, 0x00},
+{0x30e6, 0x00},
+{0x30e7, 0x00},
+{0x30e8, 0x00},
+{0x30e9, 0x00},
+{0x30ea, 0x00},
+{0x30eb, 0x00},
+{0x30ec, 0x00},
+{0x30ed, 0x00},
+{0x3101, 0x00},
+{0x3102, 0x00},
+{0x3103, 0x00},
+{0x3104, 0x00},
+{0x3105, 0x8c},
+{0x3106, 0x87},
+{0x3107, 0xc0},
+{0x3108, 0x9d},
+{0x3109, 0x8d},
+{0x310a, 0x8d},
+{0x310b, 0x6a},
+{0x310c, 0x3a},
+{0x310d, 0x5a},
+{0x310e, 0x00},
+{0x3120, 0x00},
+{0x3121, 0x00},
+{0x3122, 0x00},
+{0x3123, 0xf0},
+{0x3124, 0x00},
+{0x3125, 0x70},
+{0x3126, 0x1f},
+{0x3127, 0x0f},
+{0x3128, 0x00},
+{0x3129, 0x3a},
+{0x312a, 0x02},
+{0x312b, 0x0f},
+{0x312c, 0x00},
+{0x312d, 0x0f},
+{0x312e, 0x1d},
+{0x312f, 0x00},
+{0x3130, 0x00},
+{0x3131, 0x00},
+{0x3132, 0x00},
+{0x3140, 0x0a},
+{0x3141, 0x03},
+{0x3142, 0x00},
+{0x3143, 0x00},
+{0x3144, 0x00},
+{0x3145, 0x00},
+{0x3146, 0x00},
+{0x3147, 0x00},
+{0x3148, 0x00},
+{0x3149, 0x00},
+{0x314a, 0x00},
+{0x314b, 0x00},
+{0x314c, 0x00},
+{0x314d, 0x00},
+{0x314e, 0x1c},
+{0x314f, 0xff},
+{0x3150, 0xff},
+{0x3151, 0xff},
+{0x3152, 0x10},
+{0x3153, 0x10},
+{0x3154, 0x10},
+{0x3155, 0x00},
+{0x3156, 0x03},
+{0x3157, 0x00},
+{0x3158, 0x0f},
+{0x3159, 0xff},
+{0x315a, 0x01},
+{0x315b, 0x00},
+{0x315c, 0x01},
+{0x315d, 0x00},
+{0x315e, 0x01},
+{0x315f, 0x00},
+{0x3160, 0x00},
+{0x3161, 0x40},
+{0x3162, 0x00},
+{0x3163, 0x40},
+{0x3164, 0x00},
+{0x3165, 0x40},
+{0x3190, 0x08},
+{0x3191, 0x99},
+{0x3193, 0x08},
+{0x3194, 0x13},
+{0x3195, 0x33},
+{0x3196, 0x00},
+{0x3197, 0x10},
+{0x3198, 0x00},
+{0x3199, 0x7f},
+{0x319a, 0x80},
+{0x319b, 0xff},
+{0x319c, 0x80},
+{0x319d, 0xbf},
+{0x319e, 0xc0},
+{0x319f, 0xff},
+{0x31a0, 0x24},
+{0x31a1, 0x55},
+{0x31a2, 0x00},
+{0x31a3, 0x00},
+{0x31a6, 0x00},
+{0x31a7, 0x00},
+{0x31b0, 0x00},
+{0x31b1, 0x00},
+{0x31b2, 0x02},
+{0x31b3, 0x00},
+{0x31b4, 0x00},
+{0x31b5, 0x01},
+{0x31b6, 0x00},
+{0x31b7, 0x00},
+{0x31b8, 0x00},
+{0x31b9, 0x00},
+{0x31ba, 0x00},
+{0x31d0, 0x3c},
+{0x31d1, 0x34},
+{0x31d2, 0x3c},
+{0x31d3, 0x00},
+{0x31d4, 0x2d},
+{0x31d5, 0x00},
+{0x31d6, 0x01},
+{0x31d7, 0x06},
+{0x31d8, 0x00},
+{0x31d9, 0x64},
+{0x31da, 0x00},
+{0x31db, 0x30},
+{0x31dc, 0x04},
+{0x31dd, 0x69},
+{0x31de, 0x0a},
+{0x31df, 0x3c},
+{0x31e0, 0x04},
+{0x31e1, 0x32},
+{0x31e2, 0x00},
+{0x31e3, 0x00},
+{0x31e4, 0x08},
+{0x31e5, 0x80},
+{0x31e6, 0x00},
+{0x31e7, OV2775_DT},
+{0x31e8, 0x6c},
+{0x31e9, 0xac},
+{0x31ea, 0xec},
+{0x31eb, 0x3f},
+{0x31ec, 0x0f},
+{0x31ed, 0x20},
+{0x31ee, 0x04},
+{0x31ef, 0x48},
+{0x31f0, 0x07},
+{0x31f1, 0x90},
+{0x31f2, 0x04},
+{0x31f3, 0x48},
+{0x31f4, 0x07},
+{0x31f5, 0x90},
+{0x31f6, 0x04},
+{0x31f7, 0x48},
+{0x31f8, 0x07},
+{0x31f9, 0x90},
+{0x31fa, 0x04},
+{0x31fb, 0x48},
+{0x31fd, 0xcb},
+{0x31fe, 0x0f},
+{0x31ff, 0x03},
+{0x3200, 0x00},
+{0x3201, 0xff},
+{0x3202, 0x00},
+{0x3203, 0xff},
+{0x3204, 0xff},
+{0x3205, 0xff},
+{0x3206, 0xff},
+{0x3207, 0xff},
+{0x3208, 0xff},
+{0x3209, 0xff},
+{0x320a, 0xff},
+{0x320b, 0x1b},
+{0x320c, 0x1f},
+{0x320d, 0x1e},
+{0x320e, 0x30},
+{0x320f, 0x2d},
+{0x3210, OV2775_DT},
+{0x3211, 0x2b},
+{0x3212, 0x2a},
+{0x3213, 0x24},
+{0x3214, 0x22},
+{0x3215, 0x00},
+{0x3216, 0x04},
+{0x3217, OV2775_DT},
+{0x3218, 0x6c},
+{0x3219, 0xac},
+{0x321a, 0xec},
+{0x321b, 0x00},
+{0x3230, 0x3a},
+{0x3231, 0x00},
+{0x3232, 0x80},
+{0x3233, 0x00},
+{0x3234, 0x10},
+{0x3235, 0xaa},
+{0x3236, 0x55},
+{0x3237, 0x99},
+{0x3238, 0x66},
+{0x3239, 0x08},
+{0x323a, 0x88},
+{0x323b, 0x00},
+{0x323c, 0x00},
+{0x323d, 0x03},
+{0x3250, 0x33},
+{0x3251, 0x00},
+{0x3252, 0x20},
+#ifdef OV2775_DISPLAY_PATTERN_COLOR_BAR
+{0x3253, 0x80},
+#else
+{0x3253, 0x00},
+#endif
+{0x3254, 0x00},
+{0x3255, 0x01},
+{0x3256, 0x00},
+{0x3257, 0x00},
+{0x3258, 0x00},
+{0x3270, 0x01},
+{0x3271, 0x60},
+{0x3272, 0xc0},
+{0x3273, 0x00},
+{0x3274, 0x80},
+{0x3275, 0x40},
+{0x3276, 0x02},
+{0x3277, 0x08},
+{0x3278, 0x10},
+{0x3279, 0x04},
+{0x327a, 0x00},
+{0x327b, 0x03},
+{0x327c, 0x10},
+{0x327d, 0x60},
+{0x327e, 0xc0},
+{0x327f, 0x06},
+{0x3288, 0x10},
+{0x3289, 0x00},
+{0x328a, 0x08},
+{0x328b, 0x00},
+{0x328c, 0x04},
+{0x328d, 0x00},
+{0x328e, 0x02},
+{0x328f, 0x00},
+{0x3290, 0x20},
+{0x3291, 0x00},
+{0x3292, 0x10},
+{0x3293, 0x00},
+{0x3294, 0x08},
+{0x3295, 0x00},
+{0x3296, 0x04},
+{0x3297, 0x00},
+{0x3298, 0x40},
+{0x3299, 0x00},
+{0x329a, 0x20},
+{0x329b, 0x00},
+{0x329c, 0x10},
+{0x329d, 0x00},
+{0x329e, 0x08},
+{0x329f, 0x00},
+{0x32a0, 0x7f},
+{0x32a1, 0xff},
+{0x32a2, 0x40},
+{0x32a3, 0x00},
+{0x32a4, 0x20},
+{0x32a5, 0x00},
+{0x32a6, 0x10},
+{0x32a7, 0x00},
+{0x32a8, 0x00},
+{0x32a9, 0x00},
+{0x32aa, 0x00},
+{0x32ab, 0x00},
+{0x32ac, 0x00},
+{0x32ad, 0x00},
+{0x32ae, 0x00},
+{0x32af, 0x00},
+{0x32b0, 0x00},
+{0x32b1, 0x00},
+{0x32b2, 0x00},
+{0x32b3, 0x00},
+{0x32b4, 0x00},
+{0x32b5, 0x00},
+{0x32b6, 0x00},
+{0x32b7, 0x00},
+{0x32b8, 0x00},
+{0x32b9, 0x00},
+{0x32ba, 0x00},
+{0x32bb, 0x00},
+{0x32bc, 0x00},
+{0x32bd, 0x00},
+{0x32be, 0x00},
+{0x32bf, 0x00},
+{0x32c0, 0x00},
+{0x32c1, 0x00},
+{0x32c2, 0x00},
+{0x32c3, 0x00},
+{0x32c4, 0x00},
+{0x32c5, 0x00},
+{0x32c6, 0x00},
+{0x32c7, 0x00},
+{0x32c8, 0x87},
+{0x32c9, 0x00},
+{0x3330, 0x03},
+{0x3331, 0xc8},
+{0x3332, 0x02},
+{0x3333, 0x24},
+{0x3334, 0x00},
+{0x3335, 0x00},
+{0x3336, 0x00},
+{0x3337, 0x00},
+{0x3338, 0x03},
+{0x3339, 0xc8},
+{0x333a, 0x02},
+{0x333b, 0x24},
+{0x333c, 0x00},
+{0x333d, 0x00},
+{0x333e, 0x00},
+{0x333f, 0x00},
+{0x3340, 0x03},
+{0x3341, 0xc8},
+{0x3342, 0x02},
+{0x3343, 0x24},
+{0x3344, 0x00},
+{0x3345, 0x00},
+{0x3346, 0x00},
+{0x3347, 0x00},
+{0x3348, 0x40},
+{0x3349, 0x00},
+{0x334a, 0x00},
+{0x334b, 0x00},
+{0x334c, 0x00},
+{0x334d, 0x00},
+{0x334e, 0x80},
+{0x3360, 0x01},
+{0x3361, 0x00},
+{0x3362, 0x01},
+{0x3363, 0x00},
+{0x3364, 0x01},
+{0x3365, 0x00},
+{0x3366, 0x01},
+{0x3367, 0x00},
+{0x3368, 0x01},
+{0x3369, 0x00},
+{0x336a, 0x01},
+{0x336b, 0x00},
+{0x336c, 0x01},
+{0x336d, 0x00},
+{0x336e, 0x01},
+{0x336f, 0x00},
+{0x3370, 0x01},
+{0x3371, 0x00},
+{0x3372, 0x01},
+{0x3373, 0x00},
+{0x3374, 0x01},
+{0x3375, 0x00},
+{0x3376, 0x01},
+{0x3377, 0x00},
+{0x3378, 0x00},
+{0x3379, 0x00},
+{0x337a, 0x00},
+{0x337b, 0x00},
+{0x337c, 0x00},
+{0x337d, 0x00},
+{0x337e, 0x00},
+{0x337f, 0x00},
+{0x3380, 0x00},
+{0x3381, 0x00},
+{0x3382, 0x00},
+{0x3383, 0x00},
+{0x3384, 0x00},
+{0x3385, 0x00},
+{0x3386, 0x00},
+{0x3387, 0x00},
+{0x3388, 0x00},
+{0x3389, 0x00},
+{0x338a, 0x00},
+{0x338b, 0x00},
+{0x338c, 0x00},
+{0x338d, 0x00},
+{0x338e, 0x00},
+{0x338f, 0x00},
+{0x3390, 0x00},
+{0x3391, 0x00},
+{0x3392, 0x00},
+{0x3393, 0x00},
+{0x3394, 0x00},
+{0x3395, 0x00},
+{0x3396, 0x00},
+{0x3397, 0x00},
+{0x3398, 0x00},
+{0x3399, 0x00},
+{0x339a, 0x00},
+{0x339b, 0x00},
+{0x33b0, 0x00},
+{0x33b1, 0x50},
+{0x33b2, 0x01},
+{0x33b3, 0xff},
+{0x33b4, 0xe0},
+{0x33b5, 0x6b},
+{0x33b6, 0x00},
+{0x33b7, 0x00},
+{0x33b8, 0x00},
+{0x33b9, 0x00},
+{0x33ba, 0x00},
+{0x33bb, 0x1f},
+{0x33bc, 0x01},
+{0x33bd, 0x01},
+{0x33be, 0x01},
+{0x33bf, 0x01},
+{0x33c0, 0x00},
+{0x33c1, 0x00},
+{0x33c2, 0x00},
+{0x33c3, 0x00},
+{0x33e0, 0x14},
+{0x33e1, 0x0f},
+{0x33e2, 0x02},
+{0x33e3, 0x01},
+{0x33e4, 0x01},
+{0x33e5, 0x01},
+{0x33e6, 0x00},
+{0x33e7, 0x04},
+{0x33e8, 0x0c},
+{0x33e9, 0x02},
+{0x33ea, 0x02},
+{0x33eb, 0x02},
+{0x33ec, 0x03},
+{0x33ed, 0x01},
+{0x33ee, 0x02},
+{0x33ef, 0x08},
+{0x33f0, 0x08},
+{0x33f1, 0x04},
+{0x33f2, 0x04},
+{0x33f3, 0x00},
+{0x33f4, 0x03},
+{0x33f5, 0x14},
+{0x33f6, 0x0f},
+{0x33f7, 0x02},
+{0x33f8, 0x01},
+{0x33f9, 0x01},
+{0x33fa, 0x01},
+{0x33fb, 0x00},
+{0x33fc, 0x04},
+{0x33fd, 0x0c},
+{0x33fe, 0x02},
+{0x33ff, 0x02},
+{0x3400, 0x02},
+{0x3401, 0x03},
+{0x3402, 0x01},
+{0x3403, 0x02},
+{0x3404, 0x08},
+{0x3405, 0x08},
+{0x3406, 0x04},
+{0x3407, 0x04},
+{0x3408, 0x00},
+{0x3409, 0x03},
+{0x340a, 0x14},
+{0x340b, 0x0f},
+{0x340c, 0x04},
+{0x340d, 0x02},
+{0x340e, 0x01},
+{0x340f, 0x01},
+{0x3410, 0x00},
+{0x3411, 0x04},
+{0x3412, 0x0c},
+{0x3413, 0x00},
+{0x3414, 0x01},
+{0x3415, 0x02},
+{0x3416, 0x03},
+{0x3417, 0x02},
+{0x3418, 0x05},
+{0x3419, 0x0a},
+{0x341a, 0x08},
+{0x341b, 0x04},
+{0x341c, 0x04},
+{0x341d, 0x00},
+{0x341e, 0x03},
+{0x3440, 0x00},
+{0x3441, 0x00},
+{0x3442, 0x00},
+{0x3443, 0x00},
+{0x3444, 0x02},
+{0x3445, 0xf0},
+{0x3446, 0x02},
+{0x3447, 0x08},
+{0x3448, 0x00},
+{0x3460, 0x40},
+{0x3461, 0x40},
+{0x3462, 0x40},
+{0x3463, 0x40},
+{0x3464, 0x03},
+{0x3465, 0x01},
+{0x3466, 0x01},
+{0x3467, 0x02},
+{0x3468, 0x30},
+{0x3469, 0x00},
+{0x346a, 0x33},
+{0x346b, 0xbf},
+{0x3480, 0x40},
+{0x3481, 0x00},
+{0x3482, 0x00},
+{0x3483, 0x00},
+{0x3484, 0x0d},
+{0x3485, 0x00},
+{0x3486, 0x00},
+{0x3487, 0x00},
+{0x3488, 0x00},
+{0x3489, 0x00},
+{0x348a, 0x00},
+{0x348b, 0x04},
+{0x348c, 0x00},
+{0x348d, 0x01},
+{0x348f, 0x01},
+{0x3030, 0x0a},
+{0x3030, 0x02},
+{0x7000, 0x58},
+{0x7001, 0x7a},
+{0x7002, 0x1a},
+{0x7003, 0xc1},
+{0x7004, 0x03},
+{0x7005, 0xda},
+{0x7006, 0xbd},
+{0x7007, 0x03},
+{0x7008, 0xbd},
+{0x7009, 0x06},
+{0x700a, 0xe6},
+{0x700b, 0xec},
+{0x700c, 0xbc},
+{0x700d, 0xff},
+{0x700e, 0xbc},
+{0x700f, 0x73},
+{0x7010, 0xda},
+{0x7011, 0x72},
+{0x7012, 0x76},
+{0x7013, 0xb6},
+{0x7014, 0xee},
+{0x7015, 0xcf},
+{0x7016, 0xac},
+{0x7017, 0xd0},
+{0x7018, 0xac},
+{0x7019, 0xd1},
+{0x701a, 0x50},
+{0x701b, 0xac},
+{0x701c, 0xd2},
+{0x701d, 0xbc},
+{0x701e, 0x2e},
+{0x701f, 0xb4},
+{0x7020, 0x00},
+{0x7021, 0xdc},
+{0x7022, 0xdf},
+{0x7023, 0xb0},
+{0x7024, 0x6e},
+{0x7025, 0xbd},
+{0x7026, 0x01},
+{0x7027, 0xd7},
+{0x7028, 0xed},
+{0x7029, 0xe1},
+{0x702a, 0x36},
+{0x702b, 0x30},
+{0x702c, 0xd3},
+{0x702d, 0x2e},
+{0x702e, 0x54},
+{0x702f, 0x46},
+{0x7030, 0xbc},
+{0x7031, 0x22},
+{0x7032, 0x66},
+{0x7033, 0xbc},
+{0x7034, 0x24},
+{0x7035, 0x2c},
+{0x7036, 0x28},
+{0x7037, 0xbc},
+{0x7038, 0x3c},
+{0x7039, 0xa1},
+{0x703a, 0xac},
+{0x703b, 0xd8},
+{0x703c, 0xd6},
+{0x703d, 0xb4},
+{0x703e, 0x04},
+{0x703f, 0x46},
+{0x7040, 0xb7},
+{0x7041, 0x04},
+{0x7042, 0xbe},
+{0x7043, 0x08},
+{0x7044, 0xc3},
+{0x7045, 0xd9},
+{0x7046, 0xad},
+{0x7047, 0xc3},
+{0x7048, 0xbc},
+{0x7049, 0x19},
+{0x704a, 0xc1},
+{0x704b, 0x27},
+{0x704c, 0xe7},
+{0x704d, 0x00},
+{0x704e, 0x50},
+{0x704f, 0x20},
+{0x7050, 0xb8},
+{0x7051, 0x02},
+{0x7052, 0xbc},
+{0x7053, 0x17},
+{0x7054, 0xdb},
+{0x7055, 0xc7},
+{0x7056, 0xb8},
+{0x7057, 0x00},
+{0x7058, 0x28},
+{0x7059, 0x54},
+{0x705a, 0xb4},
+{0x705b, 0x14},
+{0x705c, 0xab},
+{0x705d, 0xbe},
+{0x705e, 0x06},
+{0x705f, 0xd8},
+{0x7060, 0xd6},
+{0x7061, 0x00},
+{0x7062, 0xb4},
+{0x7063, 0xc7},
+{0x7064, 0x07},
+{0x7065, 0xb9},
+{0x7066, 0x05},
+{0x7067, 0xee},
+{0x7068, 0xe6},
+{0x7069, 0xad},
+{0x706a, 0xb4},
+{0x706b, 0x26},
+{0x706c, 0x19},
+{0x706d, 0xc1},
+{0x706e, 0x3a},
+{0x706f, 0xc3},
+{0x7070, 0xaf},
+{0x7071, 0x00},
+{0x7072, 0xc0},
+{0x7073, 0x3c},
+{0x7074, 0xc3},
+{0x7075, 0xbe},
+{0x7076, 0xe7},
+{0x7077, 0x00},
+{0x7078, 0x15},
+{0x7079, 0xc2},
+{0x707a, 0x40},
+{0x707b, 0xc3},
+{0x707c, 0xa4},
+{0x707d, 0xc0},
+{0x707e, 0x3c},
+{0x707f, 0x00},
+{0x7080, 0xb9},
+{0x7081, 0x64},
+{0x7082, 0x29},
+{0x7083, 0x00},
+{0x7084, 0xb8},
+{0x7085, 0x12},
+{0x7086, 0xbe},
+{0x7087, 0x01},
+{0x7088, 0xd0},
+{0x7089, 0xbc},
+{0x708a, 0x01},
+{0x708b, 0xac},
+{0x708c, 0x37},
+{0x708d, 0xd2},
+{0x708e, 0xac},
+{0x708f, 0x45},
+{0x7090, 0xad},
+{0x7091, 0x28},
+{0x7092, 0x00},
+{0x7093, 0xb8},
+{0x7094, 0x00},
+{0x7095, 0xbc},
+{0x7096, 0x01},
+{0x7097, 0x36},
+{0x7098, 0xd3},
+{0x7099, 0x30},
+{0x709a, 0x04},
+{0x709b, 0xe0},
+{0x709c, 0xd8},
+{0x709d, 0xb4},
+{0x709e, 0xe9},
+{0x709f, 0x00},
+{0x70a0, 0xbe},
+{0x70a1, 0x05},
+{0x70a2, 0x62},
+{0x70a3, 0x07},
+{0x70a4, 0xb9},
+{0x70a5, 0x05},
+{0x70a6, 0xad},
+{0x70a7, 0xc3},
+{0x70a8, 0xcf},
+{0x70a9, 0x00},
+{0x70aa, 0x15},
+{0x70ab, 0xc2},
+{0x70ac, 0x59},
+{0x70ad, 0xc3},
+{0x70ae, 0xc9},
+{0x70af, 0xc0},
+{0x70b0, 0x55},
+{0x70b1, 0x00},
+{0x70b2, 0x46},
+{0x70b3, 0xa1},
+{0x70b4, 0xb9},
+{0x70b5, 0x64},
+{0x70b6, 0x29},
+{0x70b7, 0x00},
+{0x70b8, 0xb8},
+{0x70b9, 0x02},
+{0x70ba, 0xbe},
+{0x70bb, 0x02},
+{0x70bc, 0xd0},
+{0x70bd, 0xdc},
+{0x70be, 0xac},
+{0x70bf, 0xbc},
+{0x70c0, 0x01},
+{0x70c1, 0x37},
+{0x70c2, 0xac},
+{0x70c3, 0xd2},
+{0x70c4, 0x45},
+{0x70c5, 0xad},
+{0x70c6, 0x28},
+{0x70c7, 0x00},
+{0x70c8, 0xb8},
+{0x70c9, 0x00},
+{0x70ca, 0xbc},
+{0x70cb, 0x01},
+{0x70cc, 0x36},
+{0x70cd, 0x30},
+{0x70ce, 0xe0},
+{0x70cf, 0xd8},
+{0x70d0, 0xb5},
+{0x70d1, 0x0b},
+{0x70d2, 0xd6},
+{0x70d3, 0xbe},
+{0x70d4, 0x07},
+{0x70d5, 0x00},
+{0x70d6, 0x62},
+{0x70d7, 0x07},
+{0x70d8, 0xb9},
+{0x70d9, 0x05},
+{0x70da, 0xad},
+{0x70db, 0xc3},
+{0x70dc, 0xcf},
+{0x70dd, 0x46},
+{0x70de, 0xcd},
+{0x70df, 0x07},
+{0x70e0, 0xcd},
+{0x70e1, 0x00},
+{0x70e2, 0xe3},
+{0x70e3, 0x18},
+{0x70e4, 0xc2},
+{0x70e5, 0xa2},
+{0x70e6, 0xb9},
+{0x70e7, 0x64},
+{0x70e8, 0xd1},
+{0x70e9, 0xdd},
+{0x70ea, 0xac},
+{0x70eb, 0xcf},
+{0x70ec, 0xdf},
+{0x70ed, 0xb5},
+{0x70ee, 0x19},
+{0x70ef, 0x46},
+{0x70f0, 0x50},
+{0x70f1, 0xb6},
+{0x70f2, 0xee},
+{0x70f3, 0xe8},
+{0x70f4, 0xe6},
+{0x70f5, 0xbc},
+{0x70f6, 0x31},
+{0x70f7, 0xe1},
+{0x70f8, 0x36},
+{0x70f9, 0x30},
+{0x70fa, 0xd3},
+{0x70fb, 0x2e},
+{0x70fc, 0x54},
+{0x70fd, 0xbd},
+{0x70fe, 0x03},
+{0x70ff, 0xec},
+{0x7100, 0x2c},
+{0x7101, 0x50},
+{0x7102, 0x20},
+{0x7103, 0x04},
+{0x7104, 0xb8},
+{0x7105, 0x02},
+{0x7106, 0xbc},
+{0x7107, 0x18},
+{0x7108, 0xc7},
+{0x7109, 0xb8},
+{0x710a, 0x00},
+{0x710b, 0x28},
+{0x710c, 0x54},
+{0x710d, 0xbc},
+{0x710e, 0x02},
+{0x710f, 0xb4},
+{0x7110, 0xda},
+{0x7111, 0xbe},
+{0x7112, 0x04},
+{0x7113, 0xd6},
+{0x7114, 0xd8},
+{0x7115, 0xab},
+{0x7116, 0x00},
+{0x7117, 0x62},
+{0x7118, 0x07},
+{0x7119, 0xb9},
+{0x711a, 0x05},
+{0x711b, 0xad},
+{0x711c, 0xc3},
+{0x711d, 0xbc},
+{0x711e, 0xe7},
+{0x711f, 0xb9},
+{0x7120, 0x64},
+{0x7121, 0x29},
+{0x7122, 0x00},
+{0x7123, 0xb8},
+{0x7124, 0x02},
+{0x7125, 0xbe},
+{0x7126, 0x00},
+{0x7127, 0x45},
+{0x7128, 0xad},
+{0x7129, 0xe2},
+{0x712a, 0x28},
+{0x712b, 0x00},
+{0x712c, 0xb8},
+{0x712d, 0x00},
+{0x712e, 0xe0},
+{0x712f, 0xd8},
+{0x7130, 0xb4},
+{0x7131, 0xe9},
+{0x7132, 0xbe},
+{0x7133, 0x03},
+{0x7134, 0x00},
+{0x7135, 0x30},
+{0x7136, 0x62},
+{0x7137, 0x07},
+{0x7138, 0xb9},
+{0x7139, 0x05},
+{0x713a, 0xad},
+{0x713b, 0xc3},
+{0x713c, 0xcf},
+{0x713d, 0x42},
+{0x713e, 0xe4},
+{0x713f, 0xcd},
+{0x7140, 0x07},
+{0x7141, 0xcd},
+{0x7142, 0x00},
+{0x7143, 0x00},
+{0x7144, 0x17},
+{0x7145, 0xc2},
+{0x7146, 0xbb},
+{0x7147, 0xde},
+{0x7148, 0xcf},
+{0x7149, 0xdf},
+{0x714a, 0xac},
+{0x714b, 0xd1},
+{0x714c, 0x44},
+{0x714d, 0xac},
+{0x714e, 0xb9},
+{0x714f, 0x76},
+{0x7150, 0xb8},
+{0x7151, 0x08},
+{0x7152, 0xb6},
+{0x7153, 0xfe},
+{0x7154, 0xb4},
+{0x7155, 0xca},
+{0x7156, 0xd6},
+{0x7157, 0xd8},
+{0x7158, 0xab},
+{0x7159, 0x00},
+{0x715a, 0xe1},
+{0x715b, 0x36},
+{0x715c, 0x30},
+{0x715d, 0xd3},
+{0x715e, 0xbc},
+{0x715f, 0x29},
+{0x7160, 0xb4},
+{0x7161, 0x1f},
+{0x7162, 0xaa},
+{0x7163, 0xbd},
+{0x7164, 0x01},
+{0x7165, 0xb8},
+{0x7166, 0x0c},
+{0x7167, 0x45},
+{0x7168, 0xa4},
+{0x7169, 0xbd},
+{0x716a, 0x03},
+{0x716b, 0xec},
+{0x716c, 0xbc},
+{0x716d, 0x3d},
+{0x716e, 0xc3},
+{0x716f, 0xcf},
+{0x7170, 0x42},
+{0x7171, 0xb8},
+{0x7172, 0x00},
+{0x7173, 0xe4},
+{0x7174, 0xd5},
+{0x7175, 0x00},
+{0x7176, 0xb6},
+{0x7177, 0x00},
+{0x7178, 0x74},
+{0x7179, 0xbd},
+{0x717a, 0x03},
+{0x717b, 0x40},
+{0x717c, 0xb5},
+{0x717d, 0x39},
+{0x717e, 0x58},
+{0x717f, 0xdd},
+{0x7180, 0x19},
+{0x7181, 0xc1},
+{0x7182, 0xc8},
+{0x7183, 0xbd},
+{0x7184, 0x06},
+{0x7185, 0x17},
+{0x7186, 0xc1},
+{0x7187, 0xc6},
+{0x7188, 0xe8},
+{0x7189, 0x00},
+{0x718a, 0xc0},
+{0x718b, 0xc8},
+{0x718c, 0xe6},
+{0x718d, 0x95},
+{0x718e, 0x15},
+{0x718f, 0x00},
+{0x7190, 0xbc},
+{0x7191, 0x19},
+{0x7192, 0xb9},
+{0x7193, 0xf6},
+{0x7194, 0x14},
+{0x7195, 0xc1},
+{0x7196, 0xd0},
+{0x7197, 0xd1},
+{0x7198, 0xac},
+{0x7199, 0x37},
+{0x719a, 0xbc},
+{0x719b, 0x35},
+{0x719c, 0x36},
+{0x719d, 0x30},
+{0x719e, 0xe1},
+{0x719f, 0xd3},
+{0x71a0, 0x7a},
+{0x71a1, 0xb6},
+{0x71a2, 0x0c},
+{0x71a3, 0xff},
+{0x71a4, 0xb4},
+{0x71a5, 0xc7},
+{0x71a6, 0xd9},
+{0x71a7, 0x00},
+{0x71a8, 0xbd},
+{0x71a9, 0x01},
+{0x71aa, 0x56},
+{0x71ab, 0xc0},
+{0x71ac, 0xda},
+{0x71ad, 0xb4},
+{0x71ae, 0x1f},
+{0x71af, 0x56},
+{0x71b0, 0xaa},
+{0x71b1, 0xbc},
+{0x71b2, 0x08},
+{0x71b3, 0x00},
+{0x71b4, 0x57},
+{0x71b5, 0xe8},
+{0x71b6, 0xb5},
+{0x71b7, 0x36},
+{0x71b8, 0x00},
+{0x71b9, 0x54},
+{0x71ba, 0xe7},
+{0x71bb, 0xc8},
+{0x71bc, 0xb4},
+{0x71bd, 0x1f},
+{0x71be, 0x56},
+{0x71bf, 0xaa},
+{0x71c0, 0xbc},
+{0x71c1, 0x08},
+{0x71c2, 0x57},
+{0x71c3, 0x00},
+{0x71c4, 0xb5},
+{0x71c5, 0x36},
+{0x71c6, 0x00},
+{0x71c7, 0x54},
+{0x71c8, 0xc8},
+{0x71c9, 0xb5},
+{0x71ca, 0x18},
+{0x71cb, 0xd9},
+{0x71cc, 0x00},
+{0x71cd, 0xbd},
+{0x71ce, 0x01},
+{0x71cf, 0x56},
+{0x71d0, 0x08},
+{0x71d1, 0x57},
+{0x71d2, 0xe8},
+{0x71d3, 0xb4},
+{0x71d4, 0x42},
+{0x71d5, 0x00},
+{0x71d6, 0x54},
+{0x71d7, 0xe7},
+{0x71d8, 0xc8},
+{0x71d9, 0xab},
+{0x71da, 0x00},
+{0x71db, 0x66},
+{0x71dc, 0x62},
+{0x71dd, 0x06},
+{0x71de, 0x74},
+{0x71df, 0xb9},
+{0x71e0, 0x05},
+{0x71e1, 0xb7},
+{0x71e2, 0x14},
+{0x71e3, 0x0e},
+{0x71e4, 0xb7},
+{0x71e5, 0x04},
+{0x71e6, 0xc8},
+{0x7600, 0x04},
+{0x7601, 0x80},
+{0x7602, 0x07},
+{0x7603, 0x44},
+{0x7604, 0x05},
+{0x7605, 0x33},
+{0x7606, 0x0f},
+{0x7607, 0x00},
+{0x7608, 0x07},
+{0x7609, 0x40},
+{0x760a, 0x04},
+{0x760b, 0xe5},
+{0x760c, 0x06},
+{0x760d, 0x50},
+{0x760e, 0x04},
+{0x760f, 0xe4},
+{0x7610, 0x00},
+{0x7611, 0x00},
+{0x7612, 0x06},
+{0x7613, 0x5c},
+{0x7614, 0x00},
+{0x7615, 0x0f},
+{0x7616, 0x06},
+{0x7617, 0x1c},
+{0x7618, 0x00},
+{0x7619, 0x02},
+{0x761a, 0x06},
+{0x761b, 0xa2},
+{0x761c, 0x00},
+{0x761d, 0x01},
+{0x761e, 0x06},
+{0x761f, 0xae},
+{0x7620, 0x00},
+{0x7621, 0x0e},
+{0x7622, 0x05},
+{0x7623, 0x30},
+{0x7624, 0x07},
+{0x7625, 0x00},
+{0x7626, 0x0f},
+{0x7627, 0x00},
+{0x7628, 0x04},
+{0x7629, 0xe5},
+{0x762a, 0x05},
+{0x762b, 0x33},
+{0x762c, 0x06},
+{0x762d, 0x12},
+{0x762e, 0x00},
+{0x762f, 0x01},
+{0x7630, 0x06},
+{0x7631, 0x52},
+{0x7632, 0x00},
+{0x7633, 0x01},
+{0x7634, 0x06},
+{0x7635, 0x5e},
+{0x7636, 0x04},
+{0x7637, 0xe4},
+{0x7638, 0x00},
+{0x7639, 0x01},
+{0x763a, 0x05},
+{0x763b, 0x30},
+{0x763c, 0x0f},
+{0x763d, 0x00},
+{0x763e, 0x06},
+{0x763f, 0xa6},
+{0x7640, 0x00},
+{0x7641, 0x02},
+{0x7642, 0x06},
+{0x7643, 0x26},
+{0x7644, 0x00},
+{0x7645, 0x02},
+{0x7646, 0x05},
+{0x7647, 0x33},
+{0x7648, 0x06},
+{0x7649, 0x20},
+{0x764a, 0x0f},
+{0x764b, 0x00},
+{0x764c, 0x06},
+{0x764d, 0x56},
+{0x764e, 0x00},
+{0x764f, 0x02},
+{0x7650, 0x06},
+{0x7651, 0x16},
+{0x7652, 0x05},
+{0x7653, 0x33},
+{0x7654, 0x06},
+{0x7655, 0x10},
+{0x7656, 0x0f},
+{0x7657, 0x00},
+{0x7658, 0x06},
+{0x7659, 0x10},
+{0x765a, 0x0f},
+{0x765b, 0x00},
+{0x765c, 0x06},
+{0x765d, 0x20},
+{0x765e, 0x0f},
+{0x765f, 0x00},
+{0x7660, 0x00},
+{0x7661, 0x00},
+{0x7662, 0x00},
+{0x7663, 0x02},
+{0x7664, 0x04},
+{0x7665, 0xe5},
+{0x7666, 0x04},
+{0x7667, 0xe4},
+{0x7668, 0x0f},
+{0x7669, 0x00},
+{0x766a, 0x00},
+{0x766b, 0x00},
+{0x766c, 0x00},
+{0x766d, 0x01},
+{0x766e, 0x04},
+{0x766f, 0xe5},
+{0x7670, 0x04},
+{0x7671, 0xe4},
+{0x7672, 0x0f},
+{0x7673, 0x00},
+{0x7674, 0x00},
+{0x7675, 0x02},
+{0x7676, 0x04},
+{0x7677, 0xe4},
+{0x7678, 0x00},
+{0x7679, 0x02},
+{0x767a, 0x04},
+{0x767b, 0xc4},
+{0x767c, 0x00},
+{0x767d, 0x02},
+{0x767e, 0x04},
+{0x767f, 0xc4},
+{0x7680, 0x05},
+{0x7681, 0x83},
+{0x7682, 0x0f},
+{0x7683, 0x00},
+{0x7684, 0x00},
+{0x7685, 0x02},
+{0x7686, 0x04},
+{0x7687, 0xe4},
+{0x7688, 0x00},
+{0x7689, 0x02},
+{0x768a, 0x04},
+{0x768b, 0xc4},
+{0x768c, 0x00},
+{0x768d, 0x02},
+{0x768e, 0x04},
+{0x768f, 0xc4},
+{0x7690, 0x05},
+{0x7691, 0x83},
+{0x7692, 0x03},
+{0x7693, 0x0b},
+{0x7694, 0x05},
+{0x7695, 0x83},
+{0x7696, 0x00},
+{0x7697, 0x07},
+{0x7698, 0x05},
+{0x7699, 0x03},
+{0x769a, 0x00},
+{0x769b, 0x05},
+{0x769c, 0x05},
+{0x769d, 0x32},
+{0x769e, 0x05},
+{0x769f, 0x30},
+{0x76a0, 0x00},
+{0x76a1, 0x02},
+{0x76a2, 0x05},
+{0x76a3, 0x78},
+{0x76a4, 0x00},
+{0x76a5, 0x01},
+{0x76a6, 0x05},
+{0x76a7, 0x7c},
+{0x76a8, 0x03},
+{0x76a9, 0x9a},
+{0x76aa, 0x05},
+{0x76ab, 0x83},
+{0x76ac, 0x00},
+{0x76ad, 0x04},
+{0x76ae, 0x05},
+{0x76af, 0x03},
+{0x76b0, 0x00},
+{0x76b1, 0x03},
+{0x76b2, 0x05},
+{0x76b3, 0x32},
+{0x76b4, 0x05},
+{0x76b5, 0x30},
+{0x76b6, 0x00},
+{0x76b7, 0x02},
+{0x76b8, 0x05},
+{0x76b9, 0x78},
+{0x76ba, 0x00},
+{0x76bb, 0x01},
+{0x76bc, 0x05},
+{0x76bd, 0x7c},
+{0x76be, 0x03},
+{0x76bf, 0x99},
+{0x76c0, 0x05},
+{0x76c1, 0x83},
+{0x76c2, 0x00},
+{0x76c3, 0x03},
+{0x76c4, 0x05},
+{0x76c5, 0x03},
+{0x76c6, 0x00},
+{0x76c7, 0x01},
+{0x76c8, 0x05},
+{0x76c9, 0x32},
+{0x76ca, 0x05},
+{0x76cb, 0x30},
+{0x76cc, 0x00},
+{0x76cd, 0x02},
+{0x76ce, 0x05},
+{0x76cf, 0x78},
+{0x76d0, 0x00},
+{0x76d1, 0x01},
+{0x76d2, 0x05},
+{0x76d3, 0x7c},
+{0x76d4, 0x03},
+{0x76d5, 0x98},
+{0x76d6, 0x05},
+{0x76d7, 0x83},
+{0x76d8, 0x00},
+{0x76d9, 0x00},
+{0x76da, 0x05},
+{0x76db, 0x03},
+{0x76dc, 0x00},
+{0x76dd, 0x01},
+{0x76de, 0x05},
+{0x76df, 0x32},
+{0x76e0, 0x05},
+{0x76e1, 0x30},
+{0x76e2, 0x00},
+{0x76e3, 0x02},
+{0x76e4, 0x05},
+{0x76e5, 0x78},
+{0x76e6, 0x00},
+{0x76e7, 0x01},
+{0x76e8, 0x05},
+{0x76e9, 0x7c},
+{0x76ea, 0x03},
+{0x76eb, 0x97},
+{0x76ec, 0x05},
+{0x76ed, 0x83},
+{0x76ee, 0x00},
+{0x76ef, 0x00},
+{0x76f0, 0x05},
+{0x76f1, 0x03},
+{0x76f2, 0x05},
+{0x76f3, 0x32},
+{0x76f4, 0x05},
+{0x76f5, 0x30},
+{0x76f6, 0x00},
+{0x76f7, 0x02},
+{0x76f8, 0x05},
+{0x76f9, 0x78},
+{0x76fa, 0x00},
+{0x76fb, 0x01},
+{0x76fc, 0x05},
+{0x76fd, 0x7c},
+{0x76fe, 0x03},
+{0x76ff, 0x96},
+{0x7700, 0x05},
+{0x7701, 0x83},
+{0x7702, 0x05},
+{0x7703, 0x03},
+{0x7704, 0x05},
+{0x7705, 0x32},
+{0x7706, 0x05},
+{0x7707, 0x30},
+{0x7708, 0x00},
+{0x7709, 0x02},
+{0x770a, 0x05},
+{0x770b, 0x78},
+{0x770c, 0x00},
+{0x770d, 0x01},
+{0x770e, 0x05},
+{0x770f, 0x7c},
+{0x7710, 0x03},
+{0x7711, 0x95},
+{0x7712, 0x05},
+{0x7713, 0x83},
+{0x7714, 0x05},
+{0x7715, 0x03},
+{0x7716, 0x05},
+{0x7717, 0x32},
+{0x7718, 0x05},
+{0x7719, 0x30},
+{0x771a, 0x00},
+{0x771b, 0x02},
+{0x771c, 0x05},
+{0x771d, 0x78},
+{0x771e, 0x00},
+{0x771f, 0x01},
+{0x7720, 0x05},
+{0x7721, 0x7c},
+{0x7722, 0x03},
+{0x7723, 0x94},
+{0x7724, 0x05},
+{0x7725, 0x83},
+{0x7726, 0x00},
+{0x7727, 0x01},
+{0x7728, 0x05},
+{0x7729, 0x03},
+{0x772a, 0x00},
+{0x772b, 0x01},
+{0x772c, 0x05},
+{0x772d, 0x32},
+{0x772e, 0x05},
+{0x772f, 0x30},
+{0x7730, 0x00},
+{0x7731, 0x02},
+{0x7732, 0x05},
+{0x7733, 0x78},
+{0x7734, 0x00},
+{0x7735, 0x01},
+{0x7736, 0x05},
+{0x7737, 0x7c},
+{0x7738, 0x03},
+{0x7739, 0x93},
+{0x773a, 0x05},
+{0x773b, 0x83},
+{0x773c, 0x00},
+{0x773d, 0x00},
+{0x773e, 0x05},
+{0x773f, 0x03},
+{0x7740, 0x00},
+{0x7741, 0x00},
+{0x7742, 0x05},
+{0x7743, 0x32},
+{0x7744, 0x05},
+{0x7745, 0x30},
+{0x7746, 0x00},
+{0x7747, 0x02},
+{0x7748, 0x05},
+{0x7749, 0x78},
+{0x774a, 0x00},
+{0x774b, 0x01},
+{0x774c, 0x05},
+{0x774d, 0x7c},
+{0x774e, 0x03},
+{0x774f, 0x92},
+{0x7750, 0x05},
+{0x7751, 0x83},
+{0x7752, 0x05},
+{0x7753, 0x03},
+{0x7754, 0x00},
+{0x7755, 0x00},
+{0x7756, 0x05},
+{0x7757, 0x32},
+{0x7758, 0x05},
+{0x7759, 0x30},
+{0x775a, 0x00},
+{0x775b, 0x02},
+{0x775c, 0x05},
+{0x775d, 0x78},
+{0x775e, 0x00},
+{0x775f, 0x01},
+{0x7760, 0x05},
+{0x7761, 0x7c},
+{0x7762, 0x03},
+{0x7763, 0x91},
+{0x7764, 0x05},
+{0x7765, 0x83},
+{0x7766, 0x05},
+{0x7767, 0x03},
+{0x7768, 0x05},
+{0x7769, 0x32},
+{0x776a, 0x05},
+{0x776b, 0x30},
+{0x776c, 0x00},
+{0x776d, 0x02},
+{0x776e, 0x05},
+{0x776f, 0x78},
+{0x7770, 0x00},
+{0x7771, 0x01},
+{0x7772, 0x05},
+{0x7773, 0x7c},
+{0x7774, 0x03},
+{0x7775, 0x90},
+{0x7776, 0x05},
+{0x7777, 0x83},
+{0x7778, 0x05},
+{0x7779, 0x03},
+{0x777a, 0x05},
+{0x777b, 0x32},
+{0x777c, 0x05},
+{0x777d, 0x30},
+{0x777e, 0x00},
+{0x777f, 0x02},
+{0x7780, 0x05},
+{0x7781, 0x78},
+{0x7782, 0x00},
+{0x7783, 0x01},
+{0x7784, 0x05},
+{0x7785, 0x7c},
+{0x7786, 0x02},
+{0x7787, 0x90},
+{0x7788, 0x05},
+{0x7789, 0x03},
+{0x778a, 0x07},
+{0x778b, 0x00},
+{0x778c, 0x0f},
+{0x778d, 0x00},
+{0x778e, 0x08},
+{0x778f, 0x30},
+{0x7790, 0x08},
+{0x7791, 0xee},
+{0x7792, 0x0f},
+{0x7793, 0x00},
+{0x7794, 0x05},
+{0x7795, 0x33},
+{0x7796, 0x04},
+{0x7797, 0xe5},
+{0x7798, 0x06},
+{0x7799, 0x52},
+{0x779a, 0x04},
+{0x779b, 0xe4},
+{0x779c, 0x00},
+{0x779d, 0x00},
+{0x779e, 0x06},
+{0x779f, 0x5e},
+{0x77a0, 0x00},
+{0x77a1, 0x0f},
+{0x77a2, 0x06},
+{0x77a3, 0x1e},
+{0x77a4, 0x00},
+{0x77a5, 0x02},
+{0x77a6, 0x06},
+{0x77a7, 0xa2},
+{0x77a8, 0x00},
+{0x77a9, 0x01},
+{0x77aa, 0x06},
+{0x77ab, 0xae},
+{0x77ac, 0x00},
+{0x77ad, 0x03},
+{0x77ae, 0x05},
+{0x77af, 0x30},
+{0x77b0, 0x09},
+{0x77b1, 0x19},
+{0x77b2, 0x0f},
+{0x77b3, 0x00},
+{0x77b4, 0x05},
+{0x77b5, 0x33},
+{0x77b6, 0x04},
+{0x77b7, 0xe5},
+{0x77b8, 0x06},
+{0x77b9, 0x52},
+{0x77ba, 0x04},
+{0x77bb, 0xe4},
+{0x77bc, 0x00},
+{0x77bd, 0x00},
+{0x77be, 0x06},
+{0x77bf, 0x5e},
+{0x77c0, 0x00},
+{0x77c1, 0x0f},
+{0x77c2, 0x06},
+{0x77c3, 0x1e},
+{0x77c4, 0x00},
+{0x77c5, 0x02},
+{0x77c6, 0x06},
+{0x77c7, 0xa2},
+{0x77c8, 0x00},
+{0x77c9, 0x01},
+{0x77ca, 0x06},
+{0x77cb, 0xae},
+{0x77cc, 0x00},
+{0x77cd, 0x03},
+{0x77ce, 0x05},
+{0x77cf, 0x30},
+{0x77d0, 0x0f},
+{0x77d1, 0x00},
+{0x77d2, 0x00},
+{0x77d3, 0x00},
+{0x77d4, 0x00},
+{0x77d5, 0x02},
+{0x77d6, 0x04},
+{0x77d7, 0xe5},
+{0x77d8, 0x04},
+{0x77d9, 0xe4},
+{0x77da, 0x05},
+{0x77db, 0x33},
+{0x77dc, 0x07},
+{0x77dd, 0x10},
+{0x77de, 0x00},
+{0x77df, 0x00},
+{0x77e0, 0x01},
+{0x77e1, 0xbb},
+{0x77e2, 0x00},
+{0x77e3, 0x00},
+{0x77e4, 0x01},
+{0x77e5, 0xaa},
+{0x77e6, 0x00},
+{0x77e7, 0x00},
+{0x77e8, 0x01},
+{0x77e9, 0x99},
+{0x77ea, 0x00},
+{0x77eb, 0x00},
+{0x77ec, 0x01},
+{0x77ed, 0x88},
+{0x77ee, 0x00},
+{0x77ef, 0x00},
+{0x77f0, 0x01},
+{0x77f1, 0x77},
+{0x77f2, 0x00},
+{0x77f3, 0x00},
+{0x77f4, 0x01},
+{0x77f5, 0x66},
+{0x77f6, 0x00},
+{0x77f7, 0x00},
+{0x77f8, 0x01},
+{0x77f9, 0x55},
+{0x77fa, 0x00},
+{0x77fb, 0x00},
+{0x77fc, 0x01},
+{0x77fd, 0x44},
+{0x77fe, 0x00},
+{0x77ff, 0x00},
+{0x7800, 0x01},
+{0x7801, 0x33},
+{0x7802, 0x00},
+{0x7803, 0x00},
+{0x7804, 0x01},
+{0x7805, 0x22},
+{0x7806, 0x00},
+{0x7807, 0x00},
+{0x7808, 0x01},
+{0x7809, 0x11},
+{0x780a, 0x00},
+{0x780b, 0x00},
+{0x780c, 0x01},
+{0x780d, 0x00},
+{0x780e, 0x01},
+{0x780f, 0xff},
+{0x7810, 0x07},
+{0x7811, 0x00},
+{0x7812, 0x02},
+{0x7813, 0xa0},
+{0x7814, 0x0f},
+{0x7815, 0x00},
+{0x7816, 0x08},
+{0x7817, 0x35},
+{0x7818, 0x06},
+{0x7819, 0x52},
+{0x781a, 0x04},
+{0x781b, 0xe4},
+{0x781c, 0x00},
+{0x781d, 0x00},
+{0x781e, 0x06},
+{0x781f, 0x5e},
+{0x7820, 0x05},
+{0x7821, 0x33},
+{0x7822, 0x09},
+{0x7823, 0x19},
+{0x7824, 0x06},
+{0x7825, 0x1e},
+{0x7826, 0x05},
+{0x7827, 0x33},
+{0x7828, 0x00},
+{0x7829, 0x01},
+{0x782a, 0x06},
+{0x782b, 0x24},
+{0x782c, 0x06},
+{0x782d, 0x20},
+{0x782e, 0x0f},
+{0x782f, 0x00},
+{0x7830, 0x08},
+{0x7831, 0x35},
+{0x7832, 0x07},
+{0x7833, 0x10},
+{0x7834, 0x00},
+{0x7835, 0x00},
+{0x7836, 0x01},
+{0x7837, 0xbb},
+{0x7838, 0x00},
+{0x7839, 0x00},
+{0x783a, 0x01},
+{0x783b, 0xaa},
+{0x783c, 0x00},
+{0x783d, 0x00},
+{0x783e, 0x01},
+{0x783f, 0x99},
+{0x7840, 0x00},
+{0x7841, 0x00},
+{0x7842, 0x01},
+{0x7843, 0x88},
+{0x7844, 0x00},
+{0x7845, 0x00},
+{0x7846, 0x01},
+{0x7847, 0x77},
+{0x7848, 0x00},
+{0x7849, 0x00},
+{0x784a, 0x01},
+{0x784b, 0x66},
+{0x784c, 0x00},
+{0x784d, 0x00},
+{0x784e, 0x01},
+{0x784f, 0x55},
+{0x7850, 0x00},
+{0x7851, 0x00},
+{0x7852, 0x01},
+{0x7853, 0x44},
+{0x7854, 0x00},
+{0x7855, 0x00},
+{0x7856, 0x01},
+{0x7857, 0x33},
+{0x7858, 0x00},
+{0x7859, 0x00},
+{0x785a, 0x01},
+{0x785b, 0x22},
+{0x785c, 0x00},
+{0x785d, 0x00},
+{0x785e, 0x01},
+{0x785f, 0x11},
+{0x7860, 0x00},
+{0x7861, 0x00},
+{0x7862, 0x01},
+{0x7863, 0x00},
+{0x7864, 0x07},
+{0x7865, 0x00},
+{0x7866, 0x01},
+{0x7867, 0xff},
+{0x7868, 0x02},
+{0x7869, 0xa0},
+{0x786a, 0x0f},
+{0x786b, 0x00},
+{0x786c, 0x08},
+{0x786d, 0x3a},
+{0x786e, 0x08},
+{0x786f, 0x6a},
+{0x7870, 0x0f},
+{0x7871, 0x00},
+{0x7872, 0x04},
+{0x7873, 0xc0},
+{0x7874, 0x09},
+{0x7875, 0x19},
+{0x7876, 0x04},
+{0x7877, 0x99},
+{0x7878, 0x07},
+{0x7879, 0x14},
+{0x787a, 0x00},
+{0x787b, 0x01},
+{0x787c, 0x04},
+{0x787d, 0xa4},
+{0x787e, 0x00},
+{0x787f, 0x0f},
+{0x7880, 0x00},
+{0x7881, 0x0f},
+{0x7882, 0x04},
+{0x7883, 0xa6},
+{0x7884, 0x00},
+{0x7885, 0x00},
+{0x7886, 0x04},
+{0x7887, 0xa0},
+{0x7888, 0x04},
+{0x7889, 0x80},
+{0x788a, 0x04},
+{0x788b, 0x00},
+{0x788c, 0x05},
+{0x788d, 0x03},
+{0x788e, 0x06},
+{0x788f, 0x00},
+{0x7890, 0x0f},
+{0x7891, 0x00},
+{0x7892, 0x0f},
+{0x7893, 0x00},
+{0x7894, 0x0f},
+{0x7895, 0x00},
+{0x30a0, 0x00},
+{0x30a1, 0x00},
+{0x30a2, 0x00},
+{0x30a3, 0x00},
+{0x30a4, 0x07},
+{0x30a5, 0x8f},
+{0x30a6, 0x04},
+{0x30a7, 0x47},
+{0x30a8, 0x00},
+{0x30a9, 0x04},
+{0x30aa, 0x00},
+{0x30ab, 0x04},
+{0x30ac, 0x07},
+{0x30ad, 0x88},
+{0x30ae, 0x04},
+{0x30af, 0x40},
+{0x30b0, 0x0d},
+{0x30b1, 0xde},
+{0x30b2, 0x04},
+{0x30b3, 0x66},
+{0x3196, 0x00},
+{0x3197, 0x0a},
+{0x3195, 0x29},
+{0x315a, 0x02},
+{0x315b, 0x00},
+{0x30bb, 0x40},
+{0x3250, 0xf7},
+{0x3012, 0x01},
+};
diff --git a/drivers/media/i2c/soc_camera/ov490_ov10640.c b/drivers/media/i2c/soc_camera/ov490_ov10640.c
new file mode 100644
index 0000000..e698f59
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov490_ov10640.c
@@ -0,0 +1,1134 @@
+/*
+ * OmniVision ov490-ov10640 sensor camera driver
+ *
+ * Copyright (C) 2016-2018 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+
+#include "max9286.h"
+#include "ov490_ov10640.h"
+
+#define OV490_I2C_ADDR		0x24
+
+#define OV490_PID		0x300a
+#define OV490_VER		0x300b
+#define OV490_VERSION_REG	0x0490
+#define OV490_VERSION(pid, ver)	(((pid) << 8) | ((ver) & 0xff))
+
+#define OV490_ISP_HSIZE_LOW	0x60
+#define OV490_ISP_HSIZE_HIGH	0x61
+#define OV490_ISP_VSIZE_LOW	0x62
+#define OV490_ISP_VSIZE_HIGH	0x63
+
+struct ov490_priv {
+	struct v4l2_subdev		sd;
+	struct v4l2_ctrl_handler	hdl;
+	struct media_pad		pad;
+	struct v4l2_rect		rect;
+	int				max_width;
+	int				max_height;
+	char				is_fixed_sensor;
+	int				init_complete;
+	u8				id[6];
+	int				exposure;
+	int				gain;
+	int				autogain;
+	int				red;
+	int				green_r;
+	int				green_b;
+	int				blue;
+	int				awb;
+	int				dvp_order;
+	/* serializers */
+	int				max9286_addr;
+	int				max9271_addr;
+	int				ti9x4_addr;
+	int				ti9x3_addr;
+	int				port;
+	int				gpio_resetb;
+	int				active_low_resetb;
+	int				gpio_fsin;
+};
+
+static int conf_link;
+module_param(conf_link, int, 0644);
+MODULE_PARM_DESC(conf_link, " Force configuration link. Used only if robust firmware flashing required (f.e. recovery)");
+
+static int dvp_order;
+module_param(dvp_order, int, 0644);
+MODULE_PARM_DESC(dvp_order, " DVP bus bits order");
+
+static int max_width;
+module_param(max_width, int, 0644);
+MODULE_PARM_DESC(max_width, " Fixed sensor width");
+
+static int max_height;
+module_param(max_height, int, 0644);
+MODULE_PARM_DESC(max_height, " Fixed sensor height");
+
+static inline struct ov490_priv *to_ov490(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ov490_priv, sd);
+}
+
+static void ov490_s_port(struct i2c_client *client, int fwd_en)
+{
+	struct ov490_priv *priv = to_ov490(client);
+	int tmp_addr;
+
+	if (priv->max9286_addr) {
+		tmp_addr = client->addr;
+		client->addr = priv->max9286_addr;	/* Deserializer I2C address */
+		reg8_write(client, 0x0a, fwd_en ? 0x11 << priv->port : 0); /* Enable/disable reverse/forward control for this port */
+		usleep_range(5000, 5500);		/* wait 5ms */
+		client->addr = tmp_addr;
+	};
+}
+
+static void ov490_reset(struct i2c_client *client)
+{
+	struct ov490_priv *priv = to_ov490(client);
+	int tmp_addr;
+
+	if (priv->max9286_addr) {
+		if (priv->gpio_resetb < 1 || priv->gpio_resetb > 5)
+			return;
+
+		tmp_addr = client->addr;
+		/* get out from sensor reset */
+		client->addr = priv->max9271_addr;	/* MAX9271 I2C address */
+		reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) |
+			   (priv->active_low_resetb ? 0 : BIT(priv->gpio_resetb))); /* set GPIOn value to reset */
+		usleep_range(2000, 2500);		/* wait 2ms */
+		reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) |
+			   (priv->active_low_resetb ? BIT(priv->gpio_resetb) : 0)); /* set GPIOn value to un-reset */
+		usleep_range(2000, 2500);		/* wait 2ms */
+		client->addr = tmp_addr;
+	}
+
+	if (priv->ti9x4_addr) {
+		client->addr = priv->ti9x4_addr;	/* TI9x4 I2C address */
+
+		reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */
+		usleep_range(2000, 2500);		/* wait 2ms */
+		reg8_write(client, 0x6e, 0x8a);		/* set GPIO1 value to reset */
+		usleep_range(2000, 2500);		/* wait 2ms */
+		reg8_write(client, 0x6e, 0x9a);		/* set GPIO1 value to un-reset */
+	}
+}
+
+static int ov490_set_regs(struct i2c_client *client,
+			  const struct ov490_reg *regs, int nr_regs)
+{
+	int i;
+
+	for (i = 0; i < nr_regs; i++) {
+		if (reg16_write(client, regs[i].reg, regs[i].val)) {
+			usleep_range(100, 150); /* wait 100 us */
+			reg16_write(client, regs[i].reg, regs[i].val);
+		}
+	}
+
+	return 0;
+}
+
+static u8 ov490_ov10640_read(struct i2c_client *client, u16 addr)
+{
+	u8 reg_val = 0;
+
+	reg16_write(client, 0xFFFD, 0x80);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write(client, 0xFFFE, 0x19);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write(client, 0x5000, 0x01); /* read operation */
+	reg16_write(client, 0x5001, addr >> 8);
+	reg16_write(client, 0x5002, addr & 0xff);
+	reg16_write(client, 0xFFFE, 0x80);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write(client, 0x00C0, 0xc1);
+	reg16_write(client, 0xFFFE, 0x19);
+	usleep_range(1000, 1500); /* wait 1 ms */
+	reg16_read(client, 0x5000, &reg_val);
+
+	return reg_val;
+}
+
+static void ov490_ov10640_write(struct i2c_client *client, u16 addr, u8 val)
+{
+	reg16_write(client, 0xFFFD, 0x80);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write(client, 0xFFFE, 0x19);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write(client, 0x5000, 0x00); /* write operation */
+	reg16_write(client, 0x5001, addr >> 8);
+	reg16_write(client, 0x5002, addr & 0xff);
+	reg16_write(client, 0x5003, val);
+	reg16_write(client, 0xFFFE, 0x80);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write(client, 0x00C0, 0xc1);
+}
+
+static int ov490_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static int ov490_get_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov490_priv *priv = to_ov490(client);
+
+	if (format->pad)
+		return -EINVAL;
+
+	mf->width = priv->rect.width;
+	mf->height = priv->rect.height;
+	mf->code = MEDIA_BUS_FMT_YUYV8_2X8;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int ov490_set_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+
+	mf->code = MEDIA_BUS_FMT_YUYV8_2X8;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		cfg->try_fmt = *mf;
+
+	return 0;
+}
+
+static int ov490_enum_mbus_code(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->pad || code->index > 0)
+		return -EINVAL;
+
+	code->code = MEDIA_BUS_FMT_YUYV8_2X8;
+
+	return 0;
+}
+
+static int ov490_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov490_priv *priv = to_ov490(client);
+
+	memcpy(edid->edid, priv->id, 6);
+
+	edid->edid[6] = 0xff;
+	edid->edid[7] = client->addr;
+	edid->edid[8] = OV490_VERSION_REG >> 8;
+	edid->edid[9] = OV490_VERSION_REG & 0xff;
+
+	return 0;
+}
+
+static int ov490_set_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct v4l2_rect *rect = &sel->r;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov490_priv *priv = to_ov490(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
+	    sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	rect->left = ALIGN(rect->left, 2);
+	rect->top = ALIGN(rect->top, 2);
+	rect->width = ALIGN(rect->width, 2);
+	rect->height = ALIGN(rect->height, 2);
+
+	if ((rect->left + rect->width > priv->max_width) ||
+	    (rect->top + rect->height > priv->max_height))
+		*rect = priv->rect;
+
+	priv->rect.left = rect->left;
+	priv->rect.top = rect->top;
+	priv->rect.width = rect->width;
+	priv->rect.height = rect->height;
+
+	return 0;
+}
+
+static int ov490_get_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov490_priv *priv = to_ov490(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = priv->max_width;
+		sel->r.height = priv->max_height;
+		return 0;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = priv->max_width;
+		sel->r.height = priv->max_height;
+		return 0;
+	case V4L2_SEL_TGT_CROP:
+		sel->r = priv->rect;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ov490_g_mbus_config(struct v4l2_subdev *sd,
+			       struct v4l2_mbus_config *cfg)
+{
+	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	cfg->type = V4L2_MBUS_CSI2;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov490_g_register(struct v4l2_subdev *sd,
+			    struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+	u8 val = 0;
+
+	ret = reg16_read(client, (u16)reg->reg, &val);
+	if (ret < 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = sizeof(u16);
+
+	return 0;
+}
+
+static int ov490_s_register(struct v4l2_subdev *sd,
+			    const struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+
+	ret = reg16_write(client, (u16)reg->reg, (u8)reg->val);
+	if ((u8)reg->reg == 0xFFFD)
+		usleep_range(100, 150); /* wait 100 us */
+	if ((u8)reg->reg == 0xFFFE)
+		usleep_range(100, 150); /* wait 100 us */
+	return ret;
+}
+#endif
+
+static struct v4l2_subdev_core_ops ov490_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = ov490_g_register,
+	.s_register = ov490_s_register,
+#endif
+};
+
+static int ov490_s_gamma(int a, int ref)
+{
+	if ((a + ref) > 0xff)
+		return 0xff;
+
+	if ((a + ref) < 0)
+		return 0;
+
+	return a + ref;
+}
+
+static int ov490_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov490_priv *priv = to_ov490(client);
+	int ret = -EINVAL;
+
+	if (!priv->init_complete)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		/* SDE (rough) brightness */
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x00);
+		ret |= reg16_write(client, 0x5001, ctrl->val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xf1);
+		break;
+	case V4L2_CID_CONTRAST:
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, ctrl->val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xfd);
+		break;
+	case V4L2_CID_SATURATION:
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, ctrl->val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xf3);
+		break;
+	case V4L2_CID_HUE:
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, ctrl->val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xf5);
+		break;
+	case V4L2_CID_GAMMA:
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, ov490_s_gamma(ctrl->val, 0x12));
+		ret |= reg16_write(client, 0x5001, ov490_s_gamma(ctrl->val, 0x20));
+		ret |= reg16_write(client, 0x5002, ov490_s_gamma(ctrl->val, 0x3b));
+		ret |= reg16_write(client, 0x5003, ov490_s_gamma(ctrl->val, 0x5d));
+		ret |= reg16_write(client, 0x5004, ov490_s_gamma(ctrl->val, 0x6a));
+		ret |= reg16_write(client, 0x5005, ov490_s_gamma(ctrl->val, 0x76));
+		ret |= reg16_write(client, 0x5006, ov490_s_gamma(ctrl->val, 0x81));
+		ret |= reg16_write(client, 0x5007, ov490_s_gamma(ctrl->val, 0x8b));
+		ret |= reg16_write(client, 0x5008, ov490_s_gamma(ctrl->val, 0x96));
+		ret |= reg16_write(client, 0x5009, ov490_s_gamma(ctrl->val, 0x9e));
+		ret |= reg16_write(client, 0x500a, ov490_s_gamma(ctrl->val, 0xae));
+		ret |= reg16_write(client, 0x500b, ov490_s_gamma(ctrl->val, 0xbc));
+		ret |= reg16_write(client, 0x500c, ov490_s_gamma(ctrl->val, 0xcf));
+		ret |= reg16_write(client, 0x500d, ov490_s_gamma(ctrl->val, 0xde));
+		ret |= reg16_write(client, 0x500e, ov490_s_gamma(ctrl->val, 0xec));
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xf9);
+		break;
+	case V4L2_CID_SHARPNESS:
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, ctrl->val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xfb);
+		break;
+	case V4L2_CID_AUTOGAIN:
+	case V4L2_CID_GAIN:
+	case V4L2_CID_EXPOSURE:
+		if (ctrl->id == V4L2_CID_AUTOGAIN)
+			priv->autogain = ctrl->val;
+		if (ctrl->id == V4L2_CID_GAIN)
+			priv->gain = ctrl->val;
+		if (ctrl->id == V4L2_CID_EXPOSURE)
+			priv->exposure = ctrl->val;
+
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, !priv->autogain);
+		ret |= reg16_write(client, 0x5001, priv->exposure >> 8);
+		ret |= reg16_write(client, 0x5002, priv->exposure & 0xff);
+		ret |= reg16_write(client, 0x5003, priv->exposure >> 8);
+		ret |= reg16_write(client, 0x5004, priv->exposure & 0xff);
+		ret |= reg16_write(client, 0x5005, priv->exposure >> 8);
+		ret |= reg16_write(client, 0x5006, priv->exposure & 0xff);
+		ret |= reg16_write(client, 0x5007, priv->gain >> 8);
+		ret |= reg16_write(client, 0x5008, priv->gain & 0xff);
+		ret |= reg16_write(client, 0x5009, priv->gain >> 8);
+		ret |= reg16_write(client, 0x500a, priv->gain & 0xff);
+		ret |= reg16_write(client, 0x500b, priv->gain >> 8);
+		ret |= reg16_write(client, 0x500c, priv->gain & 0xff);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xea);
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+	case V4L2_CID_RED_BALANCE:
+	case V4L2_CID_BLUE_BALANCE:
+		if (ctrl->id == V4L2_CID_AUTO_WHITE_BALANCE)
+			priv->awb = ctrl->val;
+		if (ctrl->id == V4L2_CID_RED_BALANCE) {
+			priv->red = ctrl->val;
+			priv->red <<= 8;
+			priv->green_r = priv->red / 2;
+		}
+		if (ctrl->id == V4L2_CID_BLUE_BALANCE) {
+			priv->blue = ctrl->val;
+			priv->blue <<= 8;
+			priv->green_b = priv->blue / 2;
+		}
+
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, !priv->awb);
+		ret |= reg16_write(client, 0x5001, priv->red >> 8);
+		ret |= reg16_write(client, 0x5002, priv->red & 0xff);
+		ret |= reg16_write(client, 0x5003, priv->green_r >> 8);
+		ret |= reg16_write(client, 0x5004, priv->green_r & 0xff);
+		ret |= reg16_write(client, 0x5005, priv->green_b >> 8);
+		ret |= reg16_write(client, 0x5006, priv->green_b & 0xff);
+		ret |= reg16_write(client, 0x5007, priv->blue >> 8);
+		ret |= reg16_write(client, 0x5008, priv->blue & 0xff);
+		ret |= reg16_write(client, 0x5009, priv->red >> 8);
+		ret |= reg16_write(client, 0x500a, priv->red & 0xff);
+		ret |= reg16_write(client, 0x500b, priv->green_r >> 8);
+		ret |= reg16_write(client, 0x500c, priv->green_r & 0xff);
+		ret |= reg16_write(client, 0x500d, priv->green_b >> 8);
+		ret |= reg16_write(client, 0x500e, priv->green_b & 0xff);
+		ret |= reg16_write(client, 0x500f, priv->blue >> 8);
+		ret |= reg16_write(client, 0x5010, priv->blue & 0xff);
+		ret |= reg16_write(client, 0x5011, priv->red >> 8);
+		ret |= reg16_write(client, 0x5012, priv->red & 0xff);
+		ret |= reg16_write(client, 0x5013, priv->green_r >> 8);
+		ret |= reg16_write(client, 0x5014, priv->green_r & 0xff);
+		ret |= reg16_write(client, 0x5015, priv->green_b >> 8);
+		ret |= reg16_write(client, 0x5016, priv->green_b & 0xff);
+		ret |= reg16_write(client, 0x5017, priv->blue >> 8);
+		ret |= reg16_write(client, 0x5018, priv->blue & 0xff);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xeb);
+		break;
+	case V4L2_CID_HFLIP:
+#if 1
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, ctrl->val);
+		ret |= reg16_write(client, 0x5001, 0x00);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xdc);
+#else
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x01); // read 0x3128
+		ret |= reg16_write(client, 0x5001, 0x31);
+		ret |= reg16_write(client, 0x5002, 0x28);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_read(client, 0x5000, &val);
+		val &= ~(0x1 << 0);
+		val |= (ctrl->val << 0);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x00); // write 0x3128
+		ret |= reg16_write(client, 0x5001, 0x31);
+		ret |= reg16_write(client, 0x5002, 0x28);
+		ret |= reg16_write(client, 0x5003, val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x01); // read 0x3291
+		ret |= reg16_write(client, 0x5001, 0x32);
+		ret |= reg16_write(client, 0x5002, 0x91);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_read(client, 0x5000, &val);
+		val &= ~(0x1 << 1);
+		val |= (ctrl->val << 1);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x00); // write 0x3291
+		ret |= reg16_write(client, 0x5001, 0x32);
+		ret |= reg16_write(client, 0x5002, 0x91);
+		ret |= reg16_write(client, 0x5003, val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x01); // read 0x3090
+		ret |= reg16_write(client, 0x5001, 0x30);
+		ret |= reg16_write(client, 0x5002, 0x90);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_read(client, 0x5000, &val);
+		val &= ~(0x1 << 2);
+		val |= (ctrl->val << 2);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x00); // write 0x3090
+		ret |= reg16_write(client, 0x5001, 0x30);
+		ret |= reg16_write(client, 0x5002, 0x90);
+		ret |= reg16_write(client, 0x5003, val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+#endif
+		break;
+	case V4L2_CID_VFLIP:
+#if 1
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, ctrl->val);
+		ret |= reg16_write(client, 0x5001, 0x01);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xdc);
+#else
+		ret = reg16_write(client, 0xFFFD, 0x80);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x01); // read 0x3128
+		ret |= reg16_write(client, 0x5001, 0x31);
+		ret |= reg16_write(client, 0x5002, 0x28);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_read(client, 0x5000, &val);
+		val &= ~(0x1 << 1);
+		val |= (ctrl->val << 1);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x00); // write 0x3128
+		ret |= reg16_write(client, 0x5001, 0x31);
+		ret |= reg16_write(client, 0x5002, 0x28);
+		ret |= reg16_write(client, 0x5003, val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x01); // read 0x3291
+		ret |= reg16_write(client, 0x5001, 0x32);
+		ret |= reg16_write(client, 0x5002, 0x91);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_read(client, 0x5000, &val);
+		val &= ~(0x1 << 2);
+		val |= (ctrl->val << 2);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x00); // write 0x3291
+		ret |= reg16_write(client, 0x5001, 0x32);
+		ret |= reg16_write(client, 0x5002, 0x91);
+		ret |= reg16_write(client, 0x5003, val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x01); // read 0x3090
+		ret |= reg16_write(client, 0x5001, 0x30);
+		ret |= reg16_write(client, 0x5002, 0x90);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_read(client, 0x5000, &val);
+		val &= ~(0x1 << 3);
+		val |= (ctrl->val << 3);
+		ret |= reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x5000, 0x00); // write 0x3090
+		ret |= reg16_write(client, 0x5001, 0x30);
+		ret |= reg16_write(client, 0x5002, 0x90);
+		ret |= reg16_write(client, 0x5003, val);
+		ret |= reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x00C0, 0xc1);
+#endif
+		break;
+	case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov490_ctrl_ops = {
+	.s_ctrl = ov490_s_ctrl,
+};
+
+static struct v4l2_subdev_video_ops ov490_video_ops = {
+	.s_stream	= ov490_s_stream,
+	.g_mbus_config	= ov490_g_mbus_config,
+};
+
+static const struct v4l2_subdev_pad_ops ov490_subdev_pad_ops = {
+	.get_edid	= ov490_get_edid,
+	.enum_mbus_code	= ov490_enum_mbus_code,
+	.get_selection	= ov490_get_selection,
+	.set_selection	= ov490_set_selection,
+	.get_fmt	= ov490_get_fmt,
+	.set_fmt	= ov490_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov490_subdev_ops = {
+	.core	= &ov490_core_ops,
+	.video	= &ov490_video_ops,
+	.pad	= &ov490_subdev_pad_ops,
+};
+
+static void ov490_otp_id_read(struct i2c_client *client)
+{
+	struct ov490_priv *priv = to_ov490(client);
+	int i;
+	int otp_bank0_allzero = 1;
+
+#if 0
+	/* read camera id from ov490 OTP memory */
+	reg16_write(client, 0xFFFD, 0x80);
+	reg16_write(client, 0xFFFE, 0x28);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write(client, 0xE084, 0x40); /* manual mode, bank#0 */
+	reg16_write(client, 0xE081, 1); /* start OTP read */
+
+	usleep_range(25000, 26000); /* wait 25 ms */
+
+	for (i = 0; i < 6; i++)
+		reg16_read(client, 0xe000 + i + 4, &priv->id[i]);
+#else
+	/* read camera id from ov10640 OTP memory */
+	ov490_ov10640_write(client, 0x349C, 1);
+	usleep_range(25000, 25500); /* wait 25 ms */
+
+	for (i = 0; i < 6; i++) {
+		/* first 6 bytes are equal on all ov10640 */
+		priv->id[i] = ov490_ov10640_read(client, 0x349e + i + 6);
+		if (priv->id[i])
+			otp_bank0_allzero = 0;
+	}
+
+	if (otp_bank0_allzero) {
+		ov490_ov10640_write(client, 0x3495, 0x41); /* bank#1 */
+		ov490_ov10640_write(client, 0x349C, 1);
+		usleep_range(25000, 25500); /* wait 25 ms */
+
+		for (i = 0; i < 6; i++)
+			priv->id[i] = ov490_ov10640_read(client, 0x34ae + i);
+	}
+#endif
+}
+
+static ssize_t ov490_otp_id_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev));
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov490_priv *priv = to_ov490(client);
+
+	return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+			priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+}
+
+static DEVICE_ATTR(otp_id_ov490, S_IRUGO, ov490_otp_id_show, NULL);
+
+static int ov490_initialize(struct i2c_client *client)
+{
+	struct ov490_priv *priv = to_ov490(client);
+	u8 val = 0;
+	u8 pid = 0, ver = 0;
+	int ret = 0, timeout, retry_timeout = 3;
+
+	if (priv->is_fixed_sensor) {
+		dev_info(&client->dev, "ov490/ov10640 fixed-sensor res %dx%d\n", priv->max_width, priv->max_height);
+		return 0;
+	}
+
+	ov490_s_port(client, 1);
+
+	/* check and show product ID and manufacturer ID */
+	reg16_write(client, 0xFFFD, 0x80);
+	reg16_write(client, 0xFFFE, 0x80);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_read(client, OV490_PID, &pid);
+	reg16_read(client, OV490_VER, &ver);
+
+	if (OV490_VERSION(pid, ver) != OV490_VERSION_REG) {
+		dev_dbg(&client->dev, "Product ID error %x:%x\n", pid, ver);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	if (unlikely(conf_link))
+		goto out;
+
+again:
+	/* Check if firmware booted by reading stream-on status */
+	reg16_write(client, 0xFFFD, 0x80);
+	reg16_write(client, 0xFFFE, 0x29);
+	usleep_range(100, 150); /* wait 100 us */
+	for (timeout = 300; timeout > 0; timeout--) {
+		reg16_read(client, 0xd000, &val);
+		if (val == 0x0c)
+			break;
+		mdelay(1);
+	}
+
+	/* wait firmware apps started by reading OV10640 ID */
+	for (;timeout > 0; timeout--) {
+		reg16_write(client, 0xFFFD, 0x80);
+		reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(100, 150); /* wait 100 us */
+		reg16_write(client, 0x5000, 0x01);
+		reg16_write(client, 0x5001, 0x30);
+		reg16_write(client, 0x5002, 0x0a);
+		reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		reg16_write(client, 0xC0, 0xc1);
+		reg16_write(client, 0xFFFE, 0x19);
+		usleep_range(1000, 1500); /* wait 1 ms */
+		reg16_read(client, 0x5000, &val);
+		if (val == 0xa6)
+			break;
+		mdelay(1);
+	}
+
+	if (!timeout) {
+		dev_err(&client->dev, "Timeout firmware boot wait, retrying\n");
+		/* reset OV10640 using RESETB pin controlled by OV490 GPIO0 */
+		reg16_write(client, 0xFFFD, 0x80);
+		reg16_write(client, 0xFFFE, 0x80);
+		usleep_range(100, 150); /* wait 100 us */
+		reg16_write(client, 0x0050, 0x01);
+		reg16_write(client, 0x0054, 0x01);
+		reg16_write(client, 0x0058, 0x00);
+		mdelay(10);
+		reg16_write(client, 0x0058, 0x01);
+		/* reset OV490 using RESETB pin controlled by serializer */
+		ov490_reset(client);
+		if (retry_timeout--)
+			goto again;
+	}
+
+	/* read resolution used by current firmware */
+	reg16_write(client, 0xFFFD, 0x80);
+	reg16_write(client, 0xFFFE, 0x82);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_read(client, OV490_ISP_HSIZE_HIGH, &val);
+	priv->max_width = val;
+	reg16_read(client, OV490_ISP_HSIZE_LOW, &val);
+	priv->max_width = (priv->max_width << 8) | val;
+	reg16_read(client, OV490_ISP_VSIZE_HIGH, &val);
+	priv->max_height = val;
+	reg16_read(client, OV490_ISP_VSIZE_LOW, &val);
+	priv->max_height = (priv->max_height << 8) | val;
+	/* Program wizard registers */
+	ov490_set_regs(client, ov490_regs_wizard, ARRAY_SIZE(ov490_regs_wizard));
+	/* Set DVP bit swap */
+	reg16_write(client, 0xFFFD, 0x80);
+	reg16_write(client, 0xFFFE, 0x28);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write(client, 0x6009, priv->dvp_order << 4);
+	/* Read OTP IDs */
+	ov490_otp_id_read(client);
+
+out:
+	dev_info(&client->dev, "ov490/ov10640 PID %x%x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n",
+		 pid, ver, priv->max_width, priv->max_height, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+err:
+	ov490_s_port(client, 0);
+
+	return ret;
+}
+
+static int ov490_parse_dt(struct device_node *np, struct ov490_priv *priv)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+	int i;
+	const char *fixed_sensor;
+	struct device_node *endpoint = NULL, *rendpoint = NULL;
+	int tmp_addr = 0;
+
+	for (i = 0; ; i++) {
+		endpoint = of_graph_get_next_endpoint(np, endpoint);
+		if (!endpoint)
+			break;
+
+		of_node_put(endpoint);
+
+		of_property_read_u32(endpoint, "dvp-order", &priv->dvp_order);
+
+		rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0);
+		if (!rendpoint)
+			continue;
+
+		if (!of_property_read_u32(rendpoint, "max9271-addr", &priv->max9271_addr) &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->max9286_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) {
+			if (of_property_read_u32(rendpoint->parent->parent, "maxim,resetb-gpio", &priv->gpio_resetb)) {
+				priv->gpio_resetb = -1;
+			} else {
+				if (of_property_read_bool(rendpoint->parent->parent, "maxim,resetb-active-high"))
+					priv->active_low_resetb = false;
+				else
+					priv->active_low_resetb = true;
+			}
+			break;
+		}
+
+		if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) &&
+		    !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti9x4") &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti9x4_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port))
+			break;
+	}
+
+	if (!priv->max9286_addr && !priv->ti9x4_addr) {
+		dev_err(&client->dev, "deserializer does not present for OV490\n");
+		return -EINVAL;
+	}
+
+	ov490_s_port(client, 1);
+
+	/* setup I2C translator address */
+	tmp_addr = client->addr;
+	if (priv->max9286_addr) {
+		client->addr = priv->max9271_addr;			/* Serializer I2C address */
+
+		reg8_write(client, 0x09, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x0A, OV490_I2C_ADDR << 1);		/* Sensor native I2C address */
+		usleep_range(2000, 2500);				/* wait 2ms */
+	};
+	if (priv->ti9x4_addr) {
+		client->addr = priv->ti9x4_addr;			/* Deserializer I2C address */
+
+		reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */
+		usleep_range(2000, 2500);				/* wait 2ms */
+		reg8_write(client, 0x65, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x5d, OV490_I2C_ADDR << 1);		/* Sensor native I2C address */
+
+		reg8_write(client, 0x6e, 0x9a);				/* GPIO0 - fsin, GPIO1 - resetb */
+	}
+	client->addr = tmp_addr;
+
+	if (!of_property_read_string(np, "maxim,fixed-sensor", &fixed_sensor) &&
+	    strcmp(fixed_sensor, "ov490") == 0) {
+		if (of_property_read_u32(np, "maxim,width", &priv->max_width))
+			priv->max_width = 1280;
+
+		if (of_property_read_u32(np, "maxim,height", &priv->max_height))
+			priv->max_height = 966;
+
+		priv->is_fixed_sensor = true;
+	}
+
+	/* module params override dts */
+	if (dvp_order)
+		priv->dvp_order = dvp_order;
+	if (max_width && max_height) {
+		priv->max_width = max_width;
+		priv->max_height = max_height;
+		priv->is_fixed_sensor = true;
+	}
+
+	return 0;
+}
+
+static int ov490_probe(struct i2c_client *client,
+		       const struct i2c_device_id *did)
+{
+	struct ov490_priv *priv;
+	struct v4l2_ctrl *ctrl;
+	int ret;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&priv->sd, client, &ov490_subdev_ops);
+	priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	priv->exposure = 0x100;
+	priv->gain = 0x100;
+	priv->autogain = 1;
+	priv->red = 0x400;
+	priv->blue = 0x400;
+	priv->green_r = priv->red / 2;
+	priv->green_b = priv->blue / 2;
+	priv->awb = 1;
+	v4l2_ctrl_handler_init(&priv->hdl, 4);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_BRIGHTNESS, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_CONTRAST, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_SATURATION, 0, 7, 1, 2);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_HUE, 0, 23, 1, 12);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_GAMMA, -128, 128, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_SHARPNESS, 0, 10, 1, 3);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_AUTOGAIN, 0, 1, 1, priv->autogain);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_GAIN, 0, 0xffff, 1, priv->gain);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_EXPOSURE, 0, 0xffff, 1, priv->exposure);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, priv->autogain);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_RED_BALANCE, 2, 0xf, 1, priv->red >> 8);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_BLUE_BALANCE, 2, 0xf, 1, priv->blue >> 8);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_HFLIP, 0, 1, 1, 1);
+	v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_VFLIP, 0, 1, 1, 0);
+	ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops,
+			  V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 9);
+	if (ctrl)
+		ctrl->flags &= ~V4L2_CTRL_FLAG_READ_ONLY;
+	priv->sd.ctrl_handler = &priv->hdl;
+
+	ret = priv->hdl.error;
+	if (ret)
+		goto cleanup;
+
+	v4l2_ctrl_handler_setup(&priv->hdl);
+
+	priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+	priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad);
+	if (ret < 0)
+		goto cleanup;
+
+	ret = ov490_parse_dt(client->dev.of_node, priv);
+	if (ret)
+		goto cleanup;
+
+	ret = ov490_initialize(client);
+	if (ret < 0)
+		goto cleanup;
+
+	priv->rect.left = 0;
+	priv->rect.top = 0;
+	priv->rect.width = priv->max_width;
+	priv->rect.height = priv->max_height;
+
+	ret = v4l2_async_register_subdev(&priv->sd);
+	if (ret)
+		goto cleanup;
+
+	if (device_create_file(&client->dev, &dev_attr_otp_id_ov490) != 0) {
+		dev_err(&client->dev, "sysfs otp_id entry creation failed\n");
+		goto cleanup;
+	}
+
+	priv->init_complete = 1;
+
+	return 0;
+
+cleanup:
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+#ifdef CONFIG_SOC_CAMERA_OV490_OV10640
+	v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
+		client->addr, client->adapter->name);
+#endif
+	return ret;
+}
+
+static int ov490_remove(struct i2c_client *client)
+{
+	struct ov490_priv *priv = i2c_get_clientdata(client);
+
+	device_remove_file(&client->dev, &dev_attr_otp_id_ov490);
+	v4l2_async_unregister_subdev(&priv->sd);
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+
+	return 0;
+}
+
+#ifdef CONFIG_SOC_CAMERA_OV490_OV10640
+static const struct i2c_device_id ov490_id[] = {
+	{ "ov490-ov10640", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov490_id);
+
+static const struct of_device_id ov490_of_ids[] = {
+	{ .compatible = "ovti,ov490-ov10640", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ov490_of_ids);
+
+static struct i2c_driver ov490_i2c_driver = {
+	.driver	= {
+		.name		= "ov490-ov10640",
+		.of_match_table	= ov490_of_ids,
+	},
+	.probe		= ov490_probe,
+	.remove		= ov490_remove,
+	.id_table	= ov490_id,
+};
+
+module_i2c_driver(ov490_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OV490-OV10640");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/i2c/soc_camera/ov490_ov10640.h b/drivers/media/i2c/soc_camera/ov490_ov10640.h
new file mode 100644
index 0000000..b22e93e
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov490_ov10640.h
@@ -0,0 +1,102 @@
+/*
+ * OmniVision ov490-ov10640 sensor camera wizard 1280x1080@30/UYVY/BT601/8bit
+ *
+ * Copyright (C) 2016-2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+//#define OV490_DISPLAY_PATTERN
+
+struct ov490_reg {
+	u16	reg;
+	u8	val;
+};
+
+static const struct ov490_reg ov490_regs_wizard[] = {
+/* The following registers should match firmware */
+{0xfffd, 0x80},
+{0xfffe, 0x82},
+{0x0071, 0x11},
+{0x0075, 0x11},
+{0xfffe, 0x29},
+{0x6010, 0x01},
+/* ov490 EMB line disable in YUV and RAW data, NOTE: EMB line is still used in ISP and sensor */
+{0xe000, 0x14},
+#if 0 /* do not disable EMB line in ISP! */
+{0x4017, 0x00},
+#endif
+{0xfffe, 0x28},
+{0x6000, 0x04},
+{0x6004, 0x00},
+{0x6008, 0x00}, // PCLK polarity - useless due to silicon bug -> use 0x808000bb register
+{0xfffe, 0x80},
+{0x0091, 0x00},
+{0x00bb, 0x1d}, // bit[3]=0 - PCLK polarity workaround
+/* ov10640 EMB line disable */
+#if 0 /* do not disable EMB line in sensor! */
+{0xfffe, 0x19},
+{0x5000, 0x00},
+{0x5001, 0x30},
+{0x5002, 0x91},
+{0x5003, 0x08},
+{0xfffe, 0x80},
+{0x00c0, 0xc1},
+#endif
+/* Ov490 FSIN: app_fsin_from_fsync */
+{0xfffe, 0x85},
+{0x0008, 0x00},
+{0x0009, 0x01},
+{0x000A, 0x05}, // fsin0 src
+{0x000B, 0x00},
+{0x0030, 0x02}, // fsin0_delay
+{0x0031, 0x00},
+{0x0032, 0x00},
+{0x0033, 0x00},
+{0x0038, 0x02}, // fsin1_delay
+{0x0039, 0x00},
+{0x003A, 0x00},
+{0x003B, 0x00},
+{0x0070, 0x2C}, // fsin0_length
+{0x0071, 0x01},
+{0x0072, 0x00},
+{0x0073, 0x00},
+{0x0074, 0x64}, // fsin1_length
+{0x0075, 0x00},
+{0x0076, 0x00},
+{0x0077, 0x00},
+{0x0000, 0x14},
+{0x0001, 0x00},
+{0x0002, 0x00},
+{0x0003, 0x00},
+{0x0004, 0x32}, // load fsin0,load fsin1,load other, it will be cleared automatically.
+{0x0005, 0x00},
+{0x0006, 0x00},
+{0x0007, 0x00},
+{0xfffe, 0x80},
+{0x0081, 0x00}, // 03;SENSOR FSIN
+/* ov10640 FSIN */
+{0xfffe, 0x19},
+{0x5000, 0x00},
+{0x5001, 0x30},
+{0x5002, 0x8c},
+{0x5003, 0xb2},
+{0xfffe, 0x80},
+{0x00c0, 0xc1},
+/* ov10640 HFLIP=1 by default */
+{0xfffe, 0x19},
+{0x5000, 0x01},
+{0x5001, 0x00},
+{0xfffe, 0x80},
+{0x00c0, 0xdc},
+#ifdef OV490_DISPLAY_PATTERN
+{0xfffd, 0x80},
+{0xfffe, 0x19},
+{0x5000, 0x02},
+{0xfffe, 0x80},
+{0x00c0, 0xd6},
+#endif
+};
diff --git a/drivers/media/i2c/soc_camera/ov495_ov2775.c b/drivers/media/i2c/soc_camera/ov495_ov2775.c
new file mode 100644
index 0000000..bb5991c
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov495_ov2775.c
@@ -0,0 +1,640 @@
+/*
+ * OmniVision ov495-ov2775 sensor camera driver
+ *
+ * Copyright (C) 2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+
+#include "ov495_ov2775.h"
+
+#define OV495_I2C_ADDR		0x24
+
+#define OV495_PID		0x300a
+#define OV495_VER		0x300b
+#define OV495_VERSION_REG	0x0495
+#define OV495_VERSION(pid, ver)	(((pid) << 8) | ((ver) & 0xff))
+
+#define OV495_ISP_HSIZE_LOW	0x60
+#define OV495_ISP_HSIZE_HIGH	0x61
+#define OV495_ISP_VSIZE_LOW	0x62
+#define OV495_ISP_VSIZE_HIGH	0x63
+
+struct ov495_priv {
+	struct v4l2_subdev		sd;
+	struct v4l2_ctrl_handler	hdl;
+	struct media_pad		pad;
+	struct v4l2_rect		rect;
+	int				max_width;
+	int				max_height;
+	int				init_complete;
+	u8				id[6];
+	int				exposure;
+	int				gain;
+	int				autogain;
+	/* serializers */
+	int				max9286_addr;
+	int				max9271_addr;
+	int				ti9x4_addr;
+	int				ti9x3_addr;
+	int				port;
+	int				gpio_resetb;
+	int				gpio_fsin;
+
+};
+
+static int force_conf_link;
+
+static __init int ov495_force_conf_link(char *str)
+{
+	/* force configuration link */
+	/* used only if robust firmware flashing required (f.e. recovery) */
+	force_conf_link = 1;
+	return 0;
+}
+early_param("force_conf_link", ov495_force_conf_link);
+
+static inline struct ov495_priv *to_ov495(const struct i2c_client *client)
+{
+	return container_of(i2c_get_clientdata(client), struct ov495_priv, sd);
+}
+
+static int ov495_set_regs(struct i2c_client *client,
+			  const struct ov495_reg *regs, int nr_regs)
+{
+	int i;
+
+	for (i = 0; i < nr_regs; i++) {
+		if (reg16_write(client, regs[i].reg, regs[i].val)) {
+			usleep_range(100, 150); /* wait 100 us */
+			reg16_write(client, regs[i].reg, regs[i].val);
+		}
+	}
+
+	return 0;
+}
+
+static int ov495_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static int ov495_get_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov495_priv *priv = to_ov495(client);
+
+	if (format->pad)
+		return -EINVAL;
+
+	mf->width = priv->rect.width;
+	mf->height = priv->rect.height;
+	mf->code = MEDIA_BUS_FMT_YUYV8_2X8;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int ov495_set_fmt(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_subdev_format *format)
+{
+	struct v4l2_mbus_framefmt *mf = &format->format;
+
+	mf->code = MEDIA_BUS_FMT_YUYV8_2X8;
+	mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	mf->field = V4L2_FIELD_NONE;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		cfg->try_fmt = *mf;
+
+	return 0;
+}
+
+static int ov495_enum_mbus_code(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->pad || code->index > 0)
+		return -EINVAL;
+
+	code->code = MEDIA_BUS_FMT_YUYV8_2X8;
+
+	return 0;
+}
+
+static int ov495_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov495_priv *priv = to_ov495(client);
+
+	memcpy(edid->edid, priv->id, 6);
+
+	edid->edid[6] = 0xff;
+	edid->edid[7] = client->addr;
+	edid->edid[8] = OV495_VERSION_REG >> 8;
+	edid->edid[9] = OV495_VERSION_REG & 0xff;
+
+	return 0;
+}
+
+static int ov495_set_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct v4l2_rect *rect = &sel->r;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov495_priv *priv = to_ov495(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE ||
+	    sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	rect->left = ALIGN(rect->left, 2);
+	rect->top = ALIGN(rect->top, 2);
+	rect->width = ALIGN(rect->width, 2);
+	rect->height = ALIGN(rect->height, 2);
+
+	if ((rect->left + rect->width > priv->max_width) ||
+	    (rect->top + rect->height > priv->max_height))
+		*rect = priv->rect;
+
+	priv->rect.left = rect->left;
+	priv->rect.top = rect->top;
+	priv->rect.width = rect->width;
+	priv->rect.height = rect->height;
+
+	return 0;
+}
+
+static int ov495_get_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov495_priv *priv = to_ov495(client);
+
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = priv->max_width;
+		sel->r.height = priv->max_height;
+		return 0;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = priv->max_width;
+		sel->r.height = priv->max_height;
+		return 0;
+	case V4L2_SEL_TGT_CROP:
+		sel->r = priv->rect;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ov495_g_mbus_config(struct v4l2_subdev *sd,
+			       struct v4l2_mbus_config *cfg)
+{
+	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	cfg->type = V4L2_MBUS_CSI2;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov495_g_register(struct v4l2_subdev *sd,
+			    struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+	u8 val = 0;
+
+	ret = reg16_read(client, (u16)reg->reg, &val);
+	if (ret < 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = sizeof(u16);
+
+	return 0;
+}
+
+static int ov495_s_register(struct v4l2_subdev *sd,
+			    const struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+
+	ret = reg16_write(client, (u16)reg->reg, (u8)reg->val);
+	if ((u8)reg->reg == 0xFFFD)
+		usleep_range(100, 150); /* wait 100 us */
+	if ((u8)reg->reg == 0xFFFE)
+		usleep_range(100, 150); /* wait 100 us */
+	return ret;
+}
+#endif
+
+static struct v4l2_subdev_core_ops ov495_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = ov495_g_register,
+	.s_register = ov495_s_register,
+#endif
+};
+
+static int ov495_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov495_priv *priv = to_ov495(client);
+	int ret = -EINVAL;
+
+	if (!priv->init_complete)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		break;
+	case V4L2_CID_CONTRAST:
+		break;
+	case V4L2_CID_SATURATION:
+		break;
+	case V4L2_CID_HUE:
+		break;
+	case V4L2_CID_GAMMA:
+		break;
+	case V4L2_CID_SHARPNESS:
+		break;
+	case V4L2_CID_AUTOGAIN:
+	case V4L2_CID_GAIN:
+	case V4L2_CID_EXPOSURE:
+		break;
+	case V4L2_CID_HFLIP:
+		ret = reg16_write(client, 0x3516, 0x00);
+		ret |= reg16_write(client, 0x0ffc, 0x00);
+		ret |= reg16_write(client, 0x0500, ctrl->val);
+		ret |= reg16_write(client, 0x0501, 0x00);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x30C0, 0xdc);
+		ret |= reg16_write(client, 0x3516, 0x01);
+		break;
+	case V4L2_CID_VFLIP:
+		ret = reg16_write(client, 0x3516, 0x00);
+		ret |= reg16_write(client, 0x0ffc, 0x00);
+		ret |= reg16_write(client, 0x0500, ctrl->val);
+		ret |= reg16_write(client, 0x0501, 0x01);
+		usleep_range(100, 150); /* wait 100 us */
+		ret |= reg16_write(client, 0x30C0, 0xdc);
+		ret |= reg16_write(client, 0x3516, 0x01);
+		break;
+	case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov495_ctrl_ops = {
+	.s_ctrl = ov495_s_ctrl,
+};
+
+static struct v4l2_subdev_video_ops ov495_video_ops = {
+	.s_stream	= ov495_s_stream,
+	.g_mbus_config	= ov495_g_mbus_config,
+};
+
+static const struct v4l2_subdev_pad_ops ov495_subdev_pad_ops = {
+	.get_edid	= ov495_get_edid,
+	.enum_mbus_code	= ov495_enum_mbus_code,
+	.get_selection	= ov495_get_selection,
+	.set_selection	= ov495_set_selection,
+	.get_fmt	= ov495_get_fmt,
+	.set_fmt	= ov495_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov495_subdev_ops = {
+	.core	= &ov495_core_ops,
+	.video	= &ov495_video_ops,
+	.pad	= &ov495_subdev_pad_ops,
+};
+
+static void ov495_otp_id_read(struct i2c_client *client)
+{
+	struct ov495_priv *priv = to_ov495(client);
+	int i;
+
+#if 0
+	/* read camera id from ov495 OTP memory */
+	reg16_write(client, 0xFFFD, 0x80);
+	reg16_write(client, 0xFFFE, 0x20);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_write(client, 0x7384, 0x40); /* manual mode, bank#0 */
+	reg16_write(client, 0x7381, 1); /* start OTP read */
+
+	usleep_range(25000, 26000); /* wait 25 ms */
+
+	for (i = 0; i < 6; i++)
+		reg16_read(client, 0x7300 + i + 4, &priv->id[i]);
+#else
+	/* read camera id from ov2775 OTP memory */
+	reg16_write(client, 0x3516, 0x00); /* unlock write */
+	reg16_write(client, 0x0FFC, 0);
+	reg16_write(client, 0x0500, 0x00); /* write 0x34a1 -> 1 */
+	reg16_write(client, 0x0501, 0x34);
+	reg16_write(client, 0x0502, 0xa1);
+	reg16_write(client, 0x0503, 1);
+	reg16_write(client, 0x30C0, 0xc1);
+
+	usleep_range(25000, 25500); /* wait 25 ms */
+
+	for (i = 0; i < 6; i++) {
+		reg16_write(client, 0x3516, 0x00); /* unlock write */
+		reg16_write(client, 0x0500, 0x01); /* read (0x7a00 + i) */
+		reg16_write(client, 0x0501, 0x7a);
+		reg16_write(client, 0x0502, 0x00 + i + (i < 3 ? 11 : 3)); /* take bytes 11,12,13,6,7,8 */
+		reg16_write(client, 0x30C0, 0xc1);
+		usleep_range(1000, 1500); /* wait 1 ms */
+		reg16_read(client, 0x0500, &priv->id[i]);
+	}
+#endif
+}
+
+static ssize_t ov495_otp_id_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev));
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov495_priv *priv = to_ov495(client);
+
+	return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+			priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+}
+
+static DEVICE_ATTR(otp_id_ov495, S_IRUGO, ov495_otp_id_show, NULL);
+
+static int ov495_initialize(struct i2c_client *client)
+{
+	struct ov495_priv *priv = to_ov495(client);
+	u8 pid = 0, ver = 0;
+	int ret = 0;
+
+	/* check and show product ID and manufacturer ID */
+	reg16_write(client, 0xFFFD, 0x80);
+	reg16_write(client, 0xFFFE, 0x80);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_read(client, OV495_PID, &pid);
+	reg16_read(client, OV495_VER, &ver);
+
+	if (OV495_VERSION(pid, ver) != OV495_VERSION_REG) {
+		dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	if (unlikely(force_conf_link))
+		goto out;
+
+#if 0
+	/* read resolution used by current firmware */
+	reg16_write(client, 0xFFFD, 0x80);
+	reg16_write(client, 0xFFFE, 0x82);
+	usleep_range(100, 150); /* wait 100 us */
+	reg16_read(client, OV495_ISP_HSIZE_HIGH, &val);
+	priv->max_width = val;
+	reg16_read(client, OV495_ISP_HSIZE_LOW, &val);
+	priv->max_width = (priv->max_width << 8) | val;
+	reg16_read(client, OV495_ISP_VSIZE_HIGH, &val);
+	priv->max_height = val;
+	reg16_read(client, OV495_ISP_VSIZE_LOW, &val);
+	priv->max_height = (priv->max_height << 8) | val;
+#else
+	priv->max_width = 1920;
+	priv->max_height = 1080;
+#endif
+
+	/* set virtual channel */
+	ov495_regs_wizard[3].val = 0x1e | (priv->port << 6);
+	/* Program wizard registers */
+	ov495_set_regs(client, ov495_regs_wizard, ARRAY_SIZE(ov495_regs_wizard));
+	/* Read OTP IDs */
+	ov495_otp_id_read(client);
+
+out:
+	dev_info(&client->dev, "ov495/ov2775 PID %x%x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n",
+		 pid, ver, priv->max_width, priv->max_height, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+err:
+	return ret;
+}
+
+static int ov495_parse_dt(struct device_node *np, struct ov495_priv *priv)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+	int i;
+	struct device_node *endpoint = NULL, *rendpoint = NULL;
+	int tmp_addr = 0;
+
+	for (i = 0; ; i++) {
+		endpoint = of_graph_get_next_endpoint(np, endpoint);
+		if (!endpoint)
+			break;
+
+		of_node_put(endpoint);
+
+		rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0);
+		if (!rendpoint)
+			continue;
+
+		if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) &&
+		    !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti9x4") &&
+		    !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti9x4_addr) &&
+		    !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port))
+			break;
+	}
+
+	if (!priv->ti9x4_addr) {
+		dev_err(&client->dev, "deserializer does not present for OV495\n");
+		return -EINVAL;
+	}
+
+	/* setup I2C translator address */
+	tmp_addr = client->addr;
+	if (priv->ti9x4_addr) {
+		client->addr = priv->ti9x4_addr;			/* Deserializer I2C address */
+
+		reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */
+		usleep_range(2000, 2500);				/* wait 2ms */
+		reg8_write(client, 0x65, tmp_addr << 1);		/* Sensor translated I2C address */
+		reg8_write(client, 0x5d, OV495_I2C_ADDR << 1);		/* Sensor native I2C address */
+
+		reg8_write(client, 0x6e, 0x9a);				/* GPIO0 - fsin, GPIO1 - resetb */
+		/* TODO: why too long? move logic to workqueue? */
+		mdelay(350);						/* time needed to boot all sensor IPs */
+	}
+	client->addr = tmp_addr;
+
+	return 0;
+}
+
+static int ov495_probe(struct i2c_client *client,
+		       const struct i2c_device_id *did)
+{
+	struct ov495_priv *priv;
+	struct v4l2_ctrl *ctrl;
+	int ret;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&priv->sd, client, &ov495_subdev_ops);
+	priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	priv->exposure = 0x100;
+	priv->gain = 0x100;
+	priv->autogain = 1;
+	v4l2_ctrl_handler_init(&priv->hdl, 4);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_BRIGHTNESS, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_CONTRAST, 0, 16, 1, 7);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_SATURATION, 0, 7, 1, 2);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_HUE, 0, 23, 1, 12);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_GAMMA, -128, 128, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_SHARPNESS, 0, 10, 1, 3);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_AUTOGAIN, 0, 1, 1, priv->autogain);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_GAIN, 0, 0xffff, 1, priv->gain);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_EXPOSURE, 0, 0xffff, 1, priv->exposure);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_HFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_VFLIP, 0, 1, 1, 0);
+	ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops,
+			  V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 9);
+	if (ctrl)
+		ctrl->flags &= ~V4L2_CTRL_FLAG_READ_ONLY;
+	priv->sd.ctrl_handler = &priv->hdl;
+
+	ret = priv->hdl.error;
+	if (ret)
+		goto cleanup;
+
+	v4l2_ctrl_handler_setup(&priv->hdl);
+
+	priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+	priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad);
+	if (ret < 0)
+		goto cleanup;
+
+	ret = ov495_parse_dt(client->dev.of_node, priv);
+	if (ret)
+		goto cleanup;
+
+	ret = ov495_initialize(client);
+	if (ret < 0)
+		goto cleanup;
+
+	priv->rect.left = 0;
+	priv->rect.top = 0;
+	priv->rect.width = priv->max_width;
+	priv->rect.height = priv->max_height;
+
+	ret = v4l2_async_register_subdev(&priv->sd);
+	if (ret)
+		goto cleanup;
+
+	if (device_create_file(&client->dev, &dev_attr_otp_id_ov495) != 0) {
+		dev_err(&client->dev, "sysfs otp_id entry creation failed\n");
+		goto cleanup;
+	}
+
+	priv->init_complete = 1;
+
+	return 0;
+
+cleanup:
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+#ifdef CONFIG_SOC_CAMERA_OV495_OV2775
+	v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
+		client->addr, client->adapter->name);
+#endif
+	return ret;
+}
+
+static int ov495_remove(struct i2c_client *client)
+{
+	struct ov495_priv *priv = i2c_get_clientdata(client);
+
+	device_remove_file(&client->dev, &dev_attr_otp_id_ov495);
+	v4l2_async_unregister_subdev(&priv->sd);
+	media_entity_cleanup(&priv->sd.entity);
+	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_device_unregister_subdev(&priv->sd);
+
+	return 0;
+}
+
+#ifdef CONFIG_SOC_CAMERA_OV495_OV2775
+static const struct i2c_device_id ov495_id[] = {
+	{ "ov495-ov2775", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov495_id);
+
+static const struct of_device_id ov495_of_ids[] = {
+	{ .compatible = "ovti,ov495-ov2775", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ov495_of_ids);
+
+static struct i2c_driver ov495_i2c_driver = {
+	.driver	= {
+		.name		= "ov495-ov2775",
+		.of_match_table	= ov495_of_ids,
+	},
+	.probe		= ov495_probe,
+	.remove		= ov495_remove,
+	.id_table	= ov495_id,
+};
+
+module_i2c_driver(ov495_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OV495-OV2775");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/i2c/soc_camera/ov495_ov2775.h b/drivers/media/i2c/soc_camera/ov495_ov2775.h
new file mode 100644
index 0000000..17c94ae
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov495_ov2775.h
@@ -0,0 +1,23 @@
+/*
+ * OmniVision ov495-ov2775 sensor camera wizard 1920x1080@30/UYVY/MIPI
+ *
+ * Copyright (C) 2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+struct ov495_reg {
+	u16	reg;
+	u8	val;
+};
+
+static struct ov495_reg ov495_regs_wizard[] = {
+{0x3516, 0x00}, /* unlock write */
+{0xFFFD, 0x80},
+{0xFFFE, 0x20},
+{0x8017, 0x1e | (0 << 6)},
+{0x7c10, 0x01}, /* UYVY */
+};
diff --git a/drivers/media/i2c/soc_camera/ti9x4.c b/drivers/media/i2c/soc_camera/ti9x4.c
new file mode 100644
index 0000000..2ce98b4
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ti9x4.c
@@ -0,0 +1,520 @@
+ /*
+ * TI DS90UB954/960/964 FPDLinkIII driver
+ *
+ * Copyright (C) 2017-2018 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#include "ti9x4.h"
+
+struct ti9x4_priv {
+	struct v4l2_subdev	sd[4];
+	struct fwnode_handle	*sd_fwnode[4];
+	int			des_addr;
+	int			links;
+	int			lanes;
+	int			csi_rate;
+	const char		*forwarding_mode;
+	int			is_coax;
+	int			dvp_bus;
+	int			hsync;
+	int			vsync;
+	int			poc_delay;
+	atomic_t		use_count;
+	struct i2c_client	*client;
+	int			ti9x3_addr_map[4];
+	char			chip_id[6];
+	int			ser_id;
+	struct gpio_desc	*poc_gpio[4]; /* PoC power supply */
+};
+
+static int ser_id;
+module_param(ser_id, int, 0644);
+MODULE_PARM_DESC(ser_id, "  Serializer ID (default: TI913)");
+
+static int is_stp;
+module_param(is_stp, int, 0644);
+MODULE_PARM_DESC(is_stp, "  STP cable (default: Coax cable)");
+
+static int dvp_bus = 8;
+module_param(dvp_bus, int, 0644);
+MODULE_PARM_DESC(dvp_bus, "  DVP/CSI over FPDLink (default: DVP 8-bit)");
+
+static int hsync;
+module_param(hsync, int, 0644);
+MODULE_PARM_DESC(hsync, " HSYNC invertion (default: 0 - not inverted)");
+
+static int vsync = 1;
+module_param(vsync, int, 0644);
+MODULE_PARM_DESC(vsync, " VSYNC invertion (default: 1 - inverted)");
+
+static int poc_delay;
+module_param(poc_delay, int, 0644);
+MODULE_PARM_DESC(poc_delay, " Delay in ms after POC enable (default: 0 ms)");
+
+#ifdef TI954_SILICON_ERRATA
+static int indirect_write(struct i2c_client *client, unsigned int page, u8 reg, u8 val)
+{
+	if (page > 7)
+		return -EINVAL;
+
+	reg8_write(client, 0xb0, page << 2);
+	reg8_write(client, 0xb1, reg);
+	reg8_write(client, 0xb2, val);
+
+	return 0;
+}
+
+static int indirect_read(struct i2c_client *client, unsigned int page, u8 reg, u8 *val)
+{
+	if (page > 7)
+		return -EINVAL;
+
+	reg8_write(client, 0xb0, page << 2);
+	reg8_write(client, 0xb1, reg);
+	reg8_read(client, 0xb2, val);
+
+	return 0;
+}
+#endif
+
+static void ti9x4_read_chipid(struct i2c_client *client)
+{
+	struct ti9x4_priv *priv = i2c_get_clientdata(client);
+
+	/* Chip ID */
+	reg8_read(client, 0xf1, &priv->chip_id[0]);
+	reg8_read(client, 0xf2, &priv->chip_id[1]);
+	reg8_read(client, 0xf3, &priv->chip_id[2]);
+	reg8_read(client, 0xf4, &priv->chip_id[3]);
+	reg8_read(client, 0xf5, &priv->chip_id[4]);
+	priv->chip_id[5] = '\0';
+}
+
+static void ti9x4_initial_setup(struct i2c_client *client)
+{
+	struct ti9x4_priv *priv = i2c_get_clientdata(client);
+
+	/* Initial setup */
+	client->addr = priv->des_addr;				/* TI9x4 I2C */
+	reg8_write(client, 0x08, 0x1c);				/* I2C glitch filter depth */
+	reg8_write(client, 0x0a, 0x79);				/* I2C high pulse width */
+	reg8_write(client, 0x0b, 0x79);				/* I2C low pulse width */
+	reg8_write(client, 0x0d, 0xb9);				/* VDDIO 3.3V */
+	switch (priv->csi_rate) {
+	case 1600: /* REFCLK = 25MHZ */
+	case 1450: /* REFCLK = 22.5MHZ */
+		reg8_write(client, 0x1f, 0x00);			/* CSI rate 1.5/1.6Gbps */
+		break;
+	case 800: /* REFCLK = 25MHZ */
+	case 700: /* REFCLK = 22.5MHZ */
+		reg8_write(client, 0x1f, 0x02);			/* CSI rate 700/800Mbps */
+		break;
+	case 400: /* REFCLK = 25MHZ */
+		reg8_write(client, 0x1f, 0x03);			/* CSI rate 400Mbps */
+		break;
+	default:
+		dev_err(&client->dev, "unsupported CSI rate %d\n", priv->csi_rate);
+	}
+
+	if (strcmp(priv->forwarding_mode, "round-robin") == 0) {
+		reg8_write(client, 0x21, 0x01);			/* Round Robin forwarding enable */
+	} else if (strcmp(priv->forwarding_mode, "synchronized") == 0) {
+		reg8_write(client, 0x21, 0x44);			/* Basic Syncronized forwarding enable (FrameSync must be enabled!!) */
+	}
+
+	reg8_write(client, 0x32, 0x01);				/* Select TX (CSI) port 0 */
+	reg8_write(client, 0x33, ((priv->lanes - 1) ^ 0x3) << 4); /* disable CSI output, set CSI lane count, non-continuous CSI mode */
+	reg8_write(client, 0x20, 0xf0);				/* disable port forwarding */
+#if 0
+	/* FrameSync setup for REFCLK=25MHz,   FPS=30: period_counts=1/2/FPS*25MHz  =1/2/30*25Mhz  =416666 -> FS_TIME=416666 */
+	/* FrameSync setup for REFCLK=22.5MHz, FPS=30: period_counts=1/2/FPS*22.5Mhz=1/2/30*22.5Mhz=375000 -> FS_TIME=375000 */
+// #define FS_TIME (priv->csi_rate == 1450 ? 376000 : 417666)
+ #define FS_TIME (priv->csi_rate == 1450 ? 385000 : 428000) // FPS=29.2 (new vendor's firmware AWB restriction?)
+	reg8_write(client, 0x1a, FS_TIME >> 16);		/* FrameSync time 24bit */
+	reg8_write(client, 0x1b, (FS_TIME >> 8) & 0xff);
+	reg8_write(client, 0x1c, FS_TIME & 0xff);
+	reg8_write(client, 0x18, 0x43);				/* Enable FrameSync, 50/50 mode, Frame clock from 25MHz */
+#else
+	/* FrameSync setup for REFCLK=25MHz,   FPS=30: period_counts=1/FPS/12mks=1/30/12e-6=2777 -> HI=2, LO=2775 */
+	/* FrameSync setup for REFCLK=22.5MHz, FPS=30: period_counts=1/FPS/13.333mks=1/30/13.333e-6=2500 -> HI=2, LO=2498 */
+ #define FS_TIME (priv->csi_rate == 1450 ? (2498+15) : (2775+15))
+	reg8_write(client, 0x19, 2 >> 8);			/* FrameSync high time MSB */
+	reg8_write(client, 0x1a, 2 & 0xff);			/* FrameSync high time LSB */
+	reg8_write(client, 0x1b, FS_TIME >> 8);			/* FrameSync low time MSB */
+	reg8_write(client, 0x1c, FS_TIME & 0xff);		/* FrameSync low time LSB */
+	reg8_write(client, 0x18, 0x01);				/* Enable FrameSync, HI/LO mode, Frame clock from port0 */
+#endif
+}
+
+//#define SENSOR_ID 0x30  // ov10635
+//#define SENSOR_ID 0x24  // ov490
+
+static void ti9x4_fpdlink3_setup(struct i2c_client *client, int idx)
+{
+	struct ti9x4_priv *priv = i2c_get_clientdata(client);
+	u8 port_config = 0x78;
+	u8 port_config2 = 0;
+
+	/* FPDLinkIII setup */
+	client->addr = priv->des_addr;				/* TI9x4 I2C */
+	reg8_write(client, 0x4c, (idx << 4) | (1 << idx));	/* Select RX port number */
+	usleep_range(2000, 2500);				/* wait 2ms */
+
+	switch (priv->ser_id) {
+	case TI913_ID:
+		reg8_write(client, 0x58, 0x58);			/* Back channel: Freq=2.5Mbps */
+		break;
+	case TI953_ID:
+		reg8_write(client, 0x58, 0x5e);			/* Back channel: Freq=50Mbps */
+		break;
+	default:
+		break;
+	}
+
+	reg8_write(client, 0x5c, priv->ti9x3_addr_map[idx] << 1); /* TI9X3 I2C addr */
+//	reg8_write(client, 0x5d, SENSOR_ID << 1);		/* SENSOR I2C native - must be set by sensor driver */
+//	reg8_write(client, 0x65, (0x60 + idx) << 1);		/* SENSOR I2C translated - must be set by sensor driver */
+
+	if (priv->is_coax)
+		port_config |= 0x04;				/* Coax */
+	else
+		port_config |= 0x00;				/* STP */
+
+	switch (priv->dvp_bus) {
+	case 8:
+		port_config2 |= 0x80;				/* RAW10 as 8-bit prosessing using upper bits */
+		/* fall through */
+	case 10:
+		port_config |= 0x03;				/* DVP over FPDLink (TI913 compatible) RAW10/RAW8 */
+		break;
+	case 12:
+		port_config |= 0x02;				/* DVP over FPDLink (TI913 compatible) RAW12 */
+		break;
+	default:
+		port_config |= 0x00;				/* CSI over FPDLink (TI953 compatible) */
+	}
+
+	if (priv->vsync)
+		port_config2 |= 0x01;				/* VSYNC acive low */
+	if (priv->hsync)
+		port_config2 |= 0x02;				/* HSYNC acive low */
+
+	reg8_write(client, 0x6d, port_config);
+	reg8_write(client, 0x7c, port_config2);
+	reg8_write(client, 0x70, (idx << 6) | 0x1e);		/* CSI data type: yuv422 8-bit, assign VC */
+	reg8_write(client, 0x71, (idx << 6) | 0x2a);		/* CSI data type: RAW8, assign VC */
+	reg8_write(client, 0xbc, 0x00);				/* Setup minimal time between FV and LV to 3 PCLKs */
+	reg8_write(client, 0x6e, 0x88);				/* Sensor reset: backchannel GPIO0/GPIO1 set low */
+}
+
+static int ti9x4_initialize(struct i2c_client *client)
+{
+	struct ti9x4_priv *priv = i2c_get_clientdata(client);
+	int idx;
+
+	dev_info(&client->dev, "LINKs=%d, LANES=%d, FORWARDING=%s, CABLE=%s, ID=%s\n",
+			       priv->links, priv->lanes, priv->forwarding_mode, priv->is_coax ? "coax" : "stp", priv->chip_id);
+
+	ti9x4_initial_setup(client);
+
+	for (idx = 0; idx < priv->links; idx++) {
+		if (!IS_ERR(priv->poc_gpio[idx])) {
+			gpiod_direction_output(priv->poc_gpio[idx], 1); /* POC power on */
+			mdelay(priv->poc_delay);
+		}
+
+		ti9x4_fpdlink3_setup(client, idx);
+	}
+
+	client->addr = priv->des_addr;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ti9x4_g_register(struct v4l2_subdev *sd,
+				      struct v4l2_dbg_register *reg)
+{
+	struct ti9x4_priv *priv = v4l2_get_subdevdata(sd);
+	struct i2c_client *client = priv->client;
+	int ret;
+	u8 val = 0;
+
+	ret = reg8_read(client, (u8)reg->reg, &val);
+	if (ret < 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = sizeof(u8);
+
+	return 0;
+}
+
+static int ti9x4_s_register(struct v4l2_subdev *sd,
+				      const struct v4l2_dbg_register *reg)
+{
+	struct ti9x4_priv *priv = v4l2_get_subdevdata(sd);
+	struct i2c_client *client = priv->client;
+
+	return reg8_write(client, (u8)reg->reg, (u8)reg->val);
+}
+#endif
+
+static int ti9x4_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ti9x4_priv *priv = v4l2_get_subdevdata(sd);
+	struct i2c_client *client = priv->client;
+
+	if (on) {
+		if (atomic_inc_return(&priv->use_count) == 1)
+			reg8_write(client, 0x20, 0x00);		/* enable port forwarding to CSI */
+	} else {
+		if (atomic_dec_return(&priv->use_count) == 0)
+			reg8_write(client, 0x20, 0xf0);		/* disable port forwarding to CSI */
+	}
+
+	return 0;
+}
+
+static int ti9x4_registered_async(struct v4l2_subdev *sd)
+{
+	struct ti9x4_priv *priv = v4l2_get_subdevdata(sd);
+	struct i2c_client *client = priv->client;
+
+	reg8_write(client, 0x33, ((priv->lanes - 1) ^ 0x3) << 4 | 0x1); /* enable CSI output, set CSI lane count, non-continuous CSI mode */
+
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops ti9x4_subdev_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register		= ti9x4_g_register,
+	.s_register		= ti9x4_s_register,
+#endif
+	.s_power		= ti9x4_s_power,
+	.registered_async	= ti9x4_registered_async,
+};
+
+static struct v4l2_subdev_ops ti9x4_subdev_ops = {
+	.core	= &ti9x4_subdev_core_ops,
+};
+
+static int ti9x4_parse_dt(struct i2c_client *client)
+{
+	struct ti9x4_priv *priv = i2c_get_clientdata(client);
+	struct device_node *np = client->dev.of_node;
+	struct device_node *endpoint = NULL, *rendpoint = NULL;
+	struct property *prop;
+	int err, pwen, i;
+	int sensor_delay;
+	char forwarding_mode_default[20] = "round-robin"; /* round-robin, synchronized */
+	struct property *csi_rate_prop, *dvp_order_prop;
+	u8 val = 0;
+	char poc_name[10];
+
+	if (of_property_read_u32(np, "ti,links", &priv->links))
+		priv->links = 4;
+
+	if (of_property_read_u32(np, "ti,lanes", &priv->lanes))
+		priv->lanes = 4;
+
+	pwen = of_get_gpio(np, 0);
+	if (pwen > 0) {
+		err = devm_gpio_request_one(&client->dev, pwen, GPIOF_OUT_INIT_LOW, dev_name(&client->dev));
+		if (err)
+			dev_err(&client->dev, "cannot request PWEN gpio %d: %d\n", pwen, err);
+		else
+			mdelay(250);
+	}
+
+	for (i = 0; i < 4; i++) {
+		sprintf(poc_name, "POC%d", i);
+		priv->poc_gpio[i] = devm_gpiod_get_optional(&client->dev, poc_name, 0);
+	}
+
+	reg8_read(client, 0x00, &val);				/* read TI9x4 I2C address */
+	if (val != (priv->des_addr << 1)) {
+		prop = of_find_property(np, "reg", NULL);
+		if (prop)
+			of_remove_property(np, prop);
+		return -ENODEV;
+	}
+
+	ti9x4_read_chipid(client);
+
+#ifdef TI954_SILICON_ERRATA
+	indirect_write(client, 7, 0x15, 0x30);
+	if (pwen > 0)
+		gpio_set_value(pwen, 1);
+	usleep_range(5000, 5500);				/* wait 5ms */
+	indirect_write(client, 7, 0x15, 0);
+#endif
+	if (!of_property_read_u32(np, "ti,sensor_delay", &sensor_delay))
+		mdelay(sensor_delay);
+	if (of_property_read_string(np, "ti,forwarding-mode", &priv->forwarding_mode))
+		priv->forwarding_mode = forwarding_mode_default;
+	if (of_property_read_bool(np, "ti,stp"))
+		priv->is_coax = 0;
+	else
+		priv->is_coax = 1;
+	if (of_property_read_u32(np, "ti,dvp_bus", &priv->dvp_bus))
+		priv->dvp_bus = 8;
+	if (of_property_read_u32(np, "ti,hsync", &priv->hsync))
+		priv->vsync = 0;
+	if (of_property_read_u32(np, "ti,vsync", &priv->vsync))
+		priv->vsync = 1;
+	if (of_property_read_u32(np, "ti,ser_id", &priv->ser_id))
+		priv->ser_id = TI913_ID;
+	if (of_property_read_u32(np, "ti,poc-delay", &priv->poc_delay))
+		priv->poc_delay = 50;
+
+	/* module params override dts */
+	if (is_stp)
+		priv->is_coax = 0;
+	if (dvp_bus != 8)
+		priv->dvp_bus = dvp_bus;
+	if (hsync)
+		priv->hsync = hsync;
+	if (!vsync)
+		priv->vsync = vsync;
+	if (ser_id)
+		priv->ser_id = ser_id;
+	if (poc_delay)
+		priv->poc_delay = poc_delay;
+
+	for (i = 0; ; i++) {
+		endpoint = of_graph_get_next_endpoint(np, endpoint);
+		if (!endpoint)
+			break;
+
+		of_node_put(endpoint);
+
+		if (i < priv->links) {
+			if (of_property_read_u32(endpoint, "ti9x3-addr", &priv->ti9x3_addr_map[i])) {
+				dev_err(&client->dev, "ti9x3-addr not set\n");
+				return -EINVAL;
+			}
+			priv->sd_fwnode[i] = of_fwnode_handle(endpoint);
+		}
+
+		rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0);
+		if (!rendpoint)
+			continue;
+
+		csi_rate_prop = of_find_property(endpoint, "csi-rate", NULL);
+		if (csi_rate_prop) {
+			of_property_read_u32(endpoint, "csi-rate", &priv->csi_rate);
+			of_update_property(rendpoint, csi_rate_prop);
+		}
+
+		dvp_order_prop = of_find_property(endpoint, "dvp-order", NULL);
+		if (dvp_order_prop)
+			of_update_property(rendpoint, dvp_order_prop);
+	}
+
+	return 0;
+}
+
+static int ti9x4_probe(struct i2c_client *client,
+			     const struct i2c_device_id *did)
+{
+	struct ti9x4_priv *priv;
+	int err, i;
+
+	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, priv);
+	priv->des_addr = client->addr;
+	priv->client = client;
+	atomic_set(&priv->use_count, 0);
+
+	err = ti9x4_parse_dt(client);
+	if (err)
+		goto out;
+
+	err = ti9x4_initialize(client);
+	if (err < 0)
+		goto out;
+
+	for (i = 0; i < priv->links; i++) {
+		v4l2_subdev_init(&priv->sd[i], &ti9x4_subdev_ops);
+		priv->sd[i].owner = client->dev.driver->owner;
+		priv->sd[i].dev = &client->dev;
+		priv->sd[i].grp_id = i;
+		v4l2_set_subdevdata(&priv->sd[i], priv);
+		priv->sd[i].fwnode = priv->sd_fwnode[i];
+
+		snprintf(priv->sd[i].name, V4L2_SUBDEV_NAME_SIZE, "%s %d-%04x",
+			 client->dev.driver->name, i2c_adapter_id(client->adapter),
+			 client->addr);
+
+		err = v4l2_async_register_subdev(&priv->sd[i]);
+		if (err < 0)
+			goto out;
+	}
+
+out:
+	return err;
+}
+
+static int ti9x4_remove(struct i2c_client *client)
+{
+	struct ti9x4_priv *priv = i2c_get_clientdata(client);
+	int i;
+
+	for (i = 0; i < priv->links; i++) {
+		v4l2_async_unregister_subdev(&priv->sd[i]);
+		v4l2_device_unregister_subdev(&priv->sd[i]);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id ti9x4_dt_ids[] = {
+	{ .compatible = "ti,ti9x4" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ti9x4_dt_ids);
+
+static const struct i2c_device_id ti9x4_id[] = {
+	{ "ti9x4", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ti9x4_id);
+
+static struct i2c_driver ti9x4_i2c_driver = {
+	.driver	= {
+		.name		= "ti9x4",
+		.of_match_table	= of_match_ptr(ti9x4_dt_ids),
+	},
+	.probe		= ti9x4_probe,
+	.remove		= ti9x4_remove,
+	.id_table	= ti9x4_id,
+};
+
+module_i2c_driver(ti9x4_i2c_driver);
+
+MODULE_DESCRIPTION("FPDLinkIII driver for DS90UB9x4");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/soc_camera/ti9x4.h b/drivers/media/i2c/soc_camera/ti9x4.h
new file mode 100644
index 0000000..b53b4c6
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ti9x4.h
@@ -0,0 +1,156 @@
+/*
+ * TI FPDLinkIII driver include file
+ *
+ * Copyright (C) 2017 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _TI9X4_H
+#define _TI9X4_H
+
+//#define DEBUG
+#ifdef DEBUG
+#undef dev_dbg
+#define dev_dbg dev_info
+#endif
+
+#define MAXIM_NUM_RETRIES	1 /* number of read/write retries */
+#define TI913_ID		0x58
+#define TI953_ID		0x30 /* or starapped to 0x32 */
+#define TI9X4_ID		0x00 /* strapped */
+#define BROADCAST		0x6f
+
+static inline int reg8_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int ret, retries;
+
+	for (retries = MAXIM_NUM_RETRIES; retries; retries--) {
+		ret = i2c_smbus_read_byte_data(client, reg);
+		if (!(ret < 0))
+			break;
+	}
+
+	if (ret < 0) {
+		dev_dbg(&client->dev,
+			"read fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	} else {
+		*val = ret;
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+static inline int reg8_write(struct i2c_client *client, u8 reg, u8 val)
+{
+	int ret, retries;
+
+	for (retries = MAXIM_NUM_RETRIES; retries; retries--) {
+		ret = i2c_smbus_write_byte_data(client, reg, val);
+		if (!(ret < 0))
+			break;
+	}
+
+	if (ret < 0) {
+		dev_dbg(&client->dev,
+			"write fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+static inline int reg16_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+	int ret, retries;
+	u8 buf[2] = {reg >> 8, reg & 0xff};
+
+	for (retries = MAXIM_NUM_RETRIES; retries; retries--) {
+		ret = i2c_master_send(client, buf, 2);
+		if (ret == 2) {
+			ret = i2c_master_recv(client, buf, 1);
+			if (ret == 1)
+				break;
+		}
+	}
+
+	if (ret < 0) {
+		dev_dbg(&client->dev,
+			"read fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	} else {
+		*val = buf[0];
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+static inline int reg16_write(struct i2c_client *client, u16 reg, u8 val)
+{
+	int ret, retries;
+	u8 buf[3] = {reg >> 8, reg & 0xff, val};
+
+	for (retries = MAXIM_NUM_RETRIES; retries; retries--) {
+		ret = i2c_master_send(client, buf, 3);
+		if (ret == 3)
+			break;
+	}
+
+	if (ret < 0) {
+		dev_dbg(&client->dev,
+			"write fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+static inline int reg16_read16(struct i2c_client *client, u16 reg, u16 *val)
+{
+	int ret, retries;
+	u8 buf[2] = {reg >> 8, reg & 0xff};
+
+	for (retries = MAXIM_NUM_RETRIES; retries; retries--) {
+		ret = i2c_master_send(client, buf, 2);
+		if (ret == 2) {
+			ret = i2c_master_recv(client, buf, 2);
+			if (ret == 2)
+				break;
+		}
+	}
+
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"read fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	} else {
+		*val = ((u16)buf[0] << 8) | buf[1];
+	}
+
+	return ret < 0 ? ret : 0;
+}
+
+static inline int reg16_write16(struct i2c_client *client, u16 reg, u16 val)
+{
+	int ret, retries;
+	u8 buf[4] = {reg >> 8, reg & 0xff, val >> 8, val & 0xff};
+
+	for (retries = MAXIM_NUM_RETRIES; retries; retries--) {
+		ret = i2c_master_send(client, buf, 4);
+		if (ret == 4)
+			break;
+	}
+
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"write fail: chip 0x%x register 0x%x: %d\n",
+			client->addr, reg, ret);
+	}
+
+	return ret < 0 ? ret : 0;
+}
+#endif /* _TI9X4_H */
diff --git a/drivers/media/platform/soc_camera/rcar_csi2.c b/drivers/media/platform/soc_camera/rcar_csi2.c
index 05f623468..2e710e3 100644
--- a/drivers/media/platform/soc_camera/rcar_csi2.c
+++ b/drivers/media/platform/soc_camera/rcar_csi2.c
@@ -20,24 +20,29 @@
 #include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/io.h>
+#include <linux/of_graph.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 #include <linux/module.h>
+#include <linux/sys_soc.h>
 
 #include <media/rcar_csi2.h>
 #include <media/soc_camera.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-fwnode.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-mediabus.h>
 #include <media/v4l2-subdev.h>
 
-#include <media/v4l2-of.h>
+#include <media/v4l2-fwnode.h>
+
+//#define RCAR_CSI2_DUMP
 
 #define DRV_NAME "rcar_csi2"
-#define CONNECT_SLAVE_NAME "adv7482"
 #define VC_MAX_CHANNEL		4
 
 #define RCAR_CSI2_TREF		0x00
@@ -62,6 +67,7 @@
 
 #define RCAR_CSI2_LINKCNT		0x48
 #define RCAR_CSI2_LSWAP			0x4C
+#define RCAR_CSI2_PHTW			0x50
 #define RCAR_CSI2_PHTC			0x58
 #define RCAR_CSI2_PHYPLL		0x68
 
@@ -69,6 +75,10 @@
 #define RCAR_CSI2_PHCLM			0x78
 #define RCAR_CSI2_PHDLM			0x7C
 
+#define RCAR_CSI2_CSI0CLKFCPR		0x254 /* CSI0CLK Frequency Configuration Preset */
+/* CSI0CLK frequency configuration bit */
+#define CSI0CLKFREQRANGE(n)		((n & 0x3f) << 16)
+
 #define RCAR_CSI2_PHYCNT_SHUTDOWNZ		(1 << 17)
 #define RCAR_CSI2_PHYCNT_RSTZ			(1 << 16)
 #define RCAR_CSI2_PHYCNT_ENABLECLK		(1 << 4)
@@ -105,6 +115,9 @@
 #define RCAR_CSI2_LSWAP_L0SEL_PLANE2		(2 << 0)
 #define RCAR_CSI2_LSWAP_L0SEL_PLANE3		(3 << 0)
 
+#define RCAR_CSI2_PHTW_DWEN			(1 << 24)
+#define RCAR_CSI2_PHTW_CWEN			(1 << 8)
+
 #define RCAR_CSI2_PHTC_TESTCLR			(1 << 0)
 
 /* interrupt status registers */
@@ -153,6 +166,16 @@
 #define RCAR_CSI2_INTSTATE_ERRSYNCESC		(1 << 1)
 #define RCAR_CSI2_INTSTATE_ERRCONTROL		(1 << 0)
 
+static const struct soc_device_attribute r8a7797[] = {
+	{ .soc_id = "r8a7797" },
+	{ }
+};
+
+static const struct soc_device_attribute r8a7795[] = {
+	{ .soc_id = "r8a7795", .revision = "ES2.0" },
+	{ }
+};
+
 enum chip_id {
 	RCAR_GEN3,
 	RCAR_GEN2,
@@ -173,6 +196,7 @@ struct rcar_csi2_link_config {
 	unsigned char lanes;
 	unsigned long vcdt;
 	unsigned long vcdt2;
+	unsigned int csi_rate;
 };
 
 #define INIT_RCAR_CSI2_LINK_CONFIG(m) \
@@ -186,8 +210,7 @@ struct rcar_csi_irq_counter_log {
 };
 
 struct rcar_csi2 {
-	struct v4l2_subdev		subdev;
-	struct v4l2_mbus_framefmt	*mf;
+	struct v4l2_subdev		subdev[4];
 	unsigned int			irq;
 	unsigned long			mipi_flags;
 	void __iomem			*base;
@@ -199,7 +222,9 @@ struct rcar_csi2 {
 	unsigned int			field;
 	unsigned int			code;
 	unsigned int			lanes;
+	unsigned int			csi_rate;
 	spinlock_t			lock;
+	atomic_t			use_count;
 };
 
 #define RCAR_CSI_80MBPS		0
@@ -245,10 +270,104 @@ struct rcar_csi2 {
 #define RCAR_CSI_1400MBPS	40
 #define RCAR_CSI_1450MBPS	41
 #define RCAR_CSI_1500MBPS	42
+#define RCAR_CSI_NUMRATES	43
+
+#define RCAR_CSI2_PHxM0(i)		(0xf0 + i * 0x08)
+#define RCAR_CSI2_PHxM1(i)		(0xf4 + i * 0x08)
+#define RCAR_CSI2_PHRM(i)		(0x110 + i * 0x04)
+#define RCAR_CSI2_PHCM(i)		(0x120 + i * 0x04)
+#define RCAR_CSI2_SERCCNT		0x140
+#define RCAR_CSI2_SSERCCNT		0x144
+#define RCAR_CSI2_ECCCM			0x148
+#define RCAR_CSI2_ECECM			0x14c
+#define RCAR_CSI2_CRCECM		0x150
+#define RCAR_CSI2_LCNT(i)		(0x160 + i * 0x04)
+#define RCAR_CSI2_LCNTM(i)		(0x168 + i * 0x04)
+#define RCAR_CSI2_FCNTM			0x170
+#define RCAR_CSI2_FCNTM2		0x174
+#define RCAR_CSI2_VINSM(i)		(0x190 + i * 0x04)
+#define RCAR_CSI2_PHM(i)		(0x1C0 + i * 0x04)
+
+#define RCAR_CSI2_INTSTATE_ALL		0x3FFFFCDD
+
+#ifdef RCAR_CSI2_DUMP
+static void rcar_sci2_debug_show(struct rcar_csi2 *priv)
+{
+	int i;
+	u32 reg0, reg1;
+
+	printk("Debug registers:\n");
+	printk("FCNTM : 0x%08x\n", ioread32(priv->base + RCAR_CSI2_FCNTM));
+	printk("FCNTM2: 0x%08x\n", ioread32(priv->base + RCAR_CSI2_FCNTM2));
+
+	for (i = 0; i < 4; i++) {
+		reg0 = ioread32(priv->base + RCAR_CSI2_PHxM0(i));
+		reg1 = ioread32(priv->base + RCAR_CSI2_PHxM1(i));
+
+		printk("Packet header %d: dt: 0x%02x, vc: %d, wc: %d, cnt: %d\n",
+			i,
+			reg0 & 0x3F, (reg0 >> 6) & 0x03, (reg0 >> 8) & 0xffff,
+			reg1 & 0xffff);
+	}
+	for (i = 0; i < 3; i++) {
+		reg0 = ioread32(priv->base + RCAR_CSI2_PHRM(i));
+
+		printk("Packet header R %d dt: 0x%02x, vc: %d, wc: %d, ecc: 0x%02x\n",
+			i,
+			reg0 & 0x3F, (reg0 >> 6) & 0x03, (reg0 >> 8) & 0xffff,
+			(reg0 >> 24) & 0xff);
+	}
+	for (i = 0; i < 2; i++) {
+		reg0 = ioread32(priv->base + RCAR_CSI2_PHCM(i));
+
+		printk("Packet header C %d: dt: 0x%02x, vc: %d, wc: %d, cal_parity: 0x%02x\n",
+			i,
+			reg0 & 0x3F, (reg0 >> 6) & 0x03, (reg0 >> 8) & 0xffff,
+			(reg0 >> 24) & 0xff);
+	}
+	for (i = 0; i < 8; i++) {
+		reg0 = ioread32(priv->base + RCAR_CSI2_PHM(i));
+
+		printk("Packet header Monitor %d: dt: 0x%02x, vc: %d, wc: %d, ecc: 0x%02x\n",
+			i + 1,
+			reg0 & 0x3F, (reg0 >> 6) & 0x03, (reg0 >> 8) & 0xffff,
+			(reg0 >> 24) & 0xff);
+	}
+	for (i = 0; i < 3; i++)
+		printk("VINSM%d: 0x%08x\n", i, ioread32(priv->base + RCAR_CSI2_VINSM(i)));
+	printk("SERCCNT: %d\n",
+		ioread32(priv->base + RCAR_CSI2_SERCCNT));
+	printk("SSERCCNT: %d\n",
+		ioread32(priv->base + RCAR_CSI2_SSERCCNT));
+	printk("ECCCM: %d\n",
+		ioread32(priv->base + RCAR_CSI2_ECCCM));
+	printk("ECECM: %d\n",
+		ioread32(priv->base + RCAR_CSI2_ECECM));
+	printk("CRCECM: %d\n",
+		ioread32(priv->base + RCAR_CSI2_CRCECM));
+	for (i = 0; i < 2; i++)
+		printk("LCNT%d: 0x%08x\n", i, ioread32(priv->base + RCAR_CSI2_LCNT(i)));
+	for (i = 0; i < 2; i++)
+		printk("LCNTM%d: 0x%08x\n", i, ioread32(priv->base + RCAR_CSI2_LCNTM(i)));
+}
+#else
+#define rcar_sci2_debug_show(args)
+#endif /* RCAR_CSI2_DUMP */
 
 static int rcar_csi2_set_phy_freq(struct rcar_csi2 *priv)
 {
-	const uint32_t const hs_freq_range[43] = {
+	const uint32_t hs_freq_range_v3m[43] = {
+		0x00, 0x00, 0x20, 0x40, 0x02,	/* 80M, 90M, 100M, 110M, 120M */
+		0x02, 0x22, 0x42, 0x04, 0x04,	/* 130M, 140M, 150M, 160M, 170M */
+		0x24, 0x44, 0x44, 0x06, 0x26,	/* 180M, 190M, 205M, 220M, 235M */
+		0x46, 0x08, 0x28, 0x0a, 0x2a,	/* 250M, 270M, 300M, 325M, 350M */
+		0x4a, 0x4a, 0x4a, 0x4a, 0x4a,	/* 400M, 450M, 500M, 550M, 600M */
+		0x10, 0x30, 0x12, 0x32, 0x52,	/* 650M, 700M, 750M, 800M, 950M */
+		0x72, 0x14, 0x34, 0x52, 0x74,	/* 900M, 950M, 1000M, 1050M, 1100M */
+		0x16, 0x36, 0x56, 0x76, 0x18,	/* 1150M, 1200M, 1250M, 1300M, 1350M */
+		0x38, 0x58, 0x78		/* 1400M, 1450M, 1500M */
+	};
+	const uint32_t hs_freq_range_m3[43] = {
 		0x00, 0x10, 0x20, 0x30, 0x01,  /* 0-4   */
 		0x11, 0x21, 0x31, 0x02, 0x12,  /* 5-9   */
 		0x22, 0x32, 0x03, 0x13, 0x23,  /* 10-14 */
@@ -259,60 +378,49 @@ static int rcar_csi2_set_phy_freq(struct rcar_csi2 *priv)
 		0x0B, 0x1B, 0x2B, 0x3B, 0x0C,  /* 35-39 */
 		0x1C, 0x2C, 0x3C               /* 40-42 */
 	};
+	const uint32_t hs_freq_range_h3[43] = {
+		0x00, 0x10, 0x20, 0x30, 0x01,  /* 0-4   */
+		0x11, 0x21, 0x31, 0x02, 0x12,  /* 5-9   */
+		0x22, 0x32, 0x03, 0x13, 0x23,  /* 10-14 */
+		0x33, 0x04, 0x14, 0x25, 0x35,  /* 15-19 */
+		0x05, 0x26, 0x36, 0x37, 0x07,  /* 20-24 */
+		0x18, 0x28, 0x39, 0x09, 0x19,  /* 25-29 */
+		0x29, 0x3A, 0x0A, 0x1A, 0x2A,  /* 30-34 */
+		0x3B, 0x0B, 0x1B, 0x2B, 0x3C,  /* 35-39 */
+		0x0C, 0x1C, 0x2C               /* 40-42 */
+	};
+	const uint32_t csi2_rate_range[43] = {
+		80, 90, 100, 110, 120,		/* 0-4   */
+		130, 140, 150, 160, 170,	/* 5-9   */
+		180, 190, 205, 220, 235,	/* 10-14 */
+		250, 275, 300, 325, 350,	/* 15-19 */
+		400, 450, 500, 550, 600,	/* 20-24 */
+		650, 700, 750, 800, 850,	/* 25-29 */
+		900, 950, 1000, 1050, 1100,	/* 30-34 */
+		1150, 1200, 1250, 1300, 1350,	/* 35-39 */
+		1400, 1450, 1500		/* 40-42 */
+	};
 	uint32_t bps_per_lane = RCAR_CSI_190MBPS;
 
-	dev_dbg(&priv->pdev->dev, "Input size (%dx%d%c)\n",
-			 priv->mf->width, priv->mf->height,
-			 (priv->mf->field == V4L2_FIELD_NONE) ? 'p' : 'i');
-
-	switch (priv->lanes) {
-	case 1:
-		bps_per_lane = RCAR_CSI_400MBPS;
-		break;
-	case 4:
-		if (priv->mf->field == V4L2_FIELD_NONE) {
-			if ((priv->mf->width == 1920) &&
-				(priv->mf->height == 1080))
-				bps_per_lane = RCAR_CSI_900MBPS;
-			else if ((priv->mf->width == 1280) &&
-				 (priv->mf->height == 720))
-				bps_per_lane = RCAR_CSI_450MBPS;
-			else if ((priv->mf->width == 720) &&
-				 (priv->mf->height == 480))
-				bps_per_lane = RCAR_CSI_190MBPS;
-			else if ((priv->mf->width == 720) &&
-				 (priv->mf->height == 576))
-				bps_per_lane = RCAR_CSI_190MBPS;
-			else if ((priv->mf->width == 640) &&
-				 (priv->mf->height == 480))
-				bps_per_lane = RCAR_CSI_100MBPS;
-			else
-				goto error;
-		} else {
-			if ((priv->mf->width == 1920) &&
-				(priv->mf->height == 1080))
-				bps_per_lane = RCAR_CSI_450MBPS;
-			else
-				goto error;
-		}
-		break;
-	default:
-		dev_err(&priv->pdev->dev, "ERROR: lanes is invalid (%d)\n",
-								 priv->lanes);
-		return -EINVAL;
+	for (bps_per_lane = 0; bps_per_lane < RCAR_CSI_NUMRATES; bps_per_lane++) {
+		if (priv->csi_rate <= csi2_rate_range[bps_per_lane])
+			break;
 	}
 
 	dev_dbg(&priv->pdev->dev, "bps_per_lane (%d)\n", bps_per_lane);
 
-	iowrite32((hs_freq_range[bps_per_lane] << 16),
+	if (soc_device_match(r8a7797))
+		iowrite32((hs_freq_range_v3m[bps_per_lane] << 16) |
+				RCAR_CSI2_PHTW_DWEN | RCAR_CSI2_PHTW_CWEN | 0x44,
+				priv->base + RCAR_CSI2_PHTW);
+	else if (soc_device_match(r8a7795))
+		iowrite32(hs_freq_range_h3[bps_per_lane] << 16,
+				priv->base + RCAR_CSI2_PHYPLL);
+	else
+		/* h3 ws1.x is similar to m3 */
+		iowrite32(hs_freq_range_m3[bps_per_lane] << 16,
 				priv->base + RCAR_CSI2_PHYPLL);
 	return 0;
-
-error:
-	dev_err(&priv->pdev->dev, "Not support resolution (%dx%d%c)\n",
-		 priv->mf->width, priv->mf->height,
-		 (priv->mf->field == V4L2_FIELD_NONE) ? 'p' : 'i');
-	return -EINVAL;
 }
 
 static irqreturn_t rcar_csi2_irq(int irq, void *data)
@@ -370,6 +478,16 @@ static int rcar_csi2_hwinit(struct rcar_csi2 *priv)
 			iowrite32(0x0001000f, priv->base + RCAR_CSI2_FLD);
 			tmp |= 0x1;
 			break;
+		case 2:
+			/* First field number setting */
+			iowrite32(0x0001000f, priv->base + RCAR_CSI2_FLD);
+			tmp |= 0x3;
+			break;
+		case 3:
+			/* First field number setting */
+			iowrite32(0x0001000f, priv->base + RCAR_CSI2_FLD);
+			tmp |= 0x7;
+			break;
 		case 4:
 			/* First field number setting */
 			iowrite32(0x0002000f, priv->base + RCAR_CSI2_FLD);
@@ -382,11 +500,27 @@ static int rcar_csi2_hwinit(struct rcar_csi2 *priv)
 			return -EINVAL;
 		}
 
+		if (soc_device_match(r8a7795)) {
+			/* Set PHY Test Interface Write Register in R-Car H3(ES2.0) */
+			iowrite32(0x01cc01e2, priv->base + RCAR_CSI2_PHTW);
+			iowrite32(0x010101e3, priv->base + RCAR_CSI2_PHTW);
+			iowrite32(0x010101e4, priv->base + RCAR_CSI2_PHTW);
+			iowrite32(0x01100104, priv->base + RCAR_CSI2_PHTW);
+			iowrite32(0x01030100, priv->base + RCAR_CSI2_PHTW);
+			iowrite32(0x01800107, priv->base + RCAR_CSI2_PHTW);
+		}
+
 		/* set PHY frequency */
 		ret = rcar_csi2_set_phy_freq(priv);
 		if (ret < 0)
 			return ret;
 
+		/* Set CSI0CLK Frequency Configuration Preset Register
+		 * in R-Car H3(ES2.0)
+		 */
+		if (soc_device_match(r8a7795))
+			iowrite32(CSI0CLKFREQRANGE(32), priv->base + RCAR_CSI2_CSI0CLKFCPR);
+
 		/* Enable lanes */
 		iowrite32(tmp, priv->base + RCAR_CSI2_PHYCNT);
 
@@ -447,32 +581,22 @@ static int rcar_csi2_hwinit(struct rcar_csi2 *priv)
 
 static int rcar_csi2_s_power(struct v4l2_subdev *sd, int on)
 {
-	struct rcar_csi2 *priv = container_of(sd, struct rcar_csi2, subdev);
-	struct v4l2_subdev *tmp_sd;
-	struct v4l2_subdev_format fmt = {
-		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
-	};
-	struct v4l2_mbus_framefmt *mf = &fmt.format;
+	struct rcar_csi2 *priv = v4l2_get_subdevdata(sd);
 	int ret = 0;
 
 	if (on) {
-		v4l2_device_for_each_subdev(tmp_sd, sd->v4l2_dev) {
-			if (strncmp(tmp_sd->name, CONNECT_SLAVE_NAME,
-				sizeof(CONNECT_SLAVE_NAME) - 1) == 0) {
-				v4l2_subdev_call(tmp_sd, pad, get_fmt,
-							 NULL, &fmt);
-				if (ret < 0)
-					return ret;
-			}
+		if (atomic_inc_return(&priv->use_count) == 1) {
+			pm_runtime_get_sync(&priv->pdev->dev);
+			ret = rcar_csi2_hwinit(priv);
+			if (ret < 0)
+				return ret;
 		}
-		priv->mf = mf;
-		pm_runtime_get_sync(&priv->pdev->dev);
-		ret = rcar_csi2_hwinit(priv);
-		if (ret < 0)
-			return ret;
 	} else {
-		rcar_csi2_hwdeinit(priv);
-		pm_runtime_put_sync(&priv->pdev->dev);
+		if (atomic_dec_return(&priv->use_count) == 0) {
+			rcar_sci2_debug_show(priv);
+			rcar_csi2_hwdeinit(priv);
+			pm_runtime_put_sync(&priv->pdev->dev);
+		}
 	}
 
 	return ret;
@@ -488,6 +612,7 @@ static struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
 
 #ifdef CONFIG_OF
 static const struct of_device_id rcar_csi2_of_table[] = {
+	{ .compatible = "renesas,r8a7797-csi2", .data = (void *)RCAR_GEN3 },
 	{ .compatible = "renesas,r8a7796-csi2", .data = (void *)RCAR_GEN3 },
 	{ .compatible = "renesas,r8a7795-csi2", .data = (void *)RCAR_GEN3 },
 	{ },
@@ -496,6 +621,7 @@ MODULE_DEVICE_TABLE(of, rcar_csi2_of_table);
 #endif
 
 static struct platform_device_id rcar_csi2_id_table[] = {
+	{ "r8a7797-csi2",  RCAR_GEN3 },
 	{ "r8a7796-csi2",  RCAR_GEN3 },
 	{ "r8a7795-csi2",  RCAR_GEN3 },
 	{},
@@ -505,7 +631,7 @@ MODULE_DEVICE_TABLE(platform, rcar_csi2_id_table);
 static int rcar_csi2_parse_dt(struct device_node *np,
 			struct rcar_csi2_link_config *config)
 {
-	struct v4l2_of_endpoint bus_cfg;
+	struct v4l2_fwnode_endpoint bus_cfg;
 	struct device_node *endpoint;
 	struct device_node *vc_np, *vc_ch;
 	const char *str;
@@ -518,19 +644,20 @@ static int rcar_csi2_parse_dt(struct device_node *np,
 	if (!endpoint)
 		return -EINVAL;
 
-	v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+	v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg);
+	ret = of_property_read_u32(endpoint, "csi-rate", &config->csi_rate);
+	if (ret < 0) {
+		printk(KERN_ERR "csi-rate not set\n");
+		return ret;
+	}
 	of_node_put(endpoint);
 
 	config->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
 
-	ret = of_property_read_string(np, "adi,input-interface", &str);
-	if (ret < 0)
-		return ret;
-
 	vc_np = of_get_child_by_name(np, "virtual,channel");
 
-	config->vcdt = 0;
-	config->vcdt2 = 0;
+	config->vcdt = 0x81008000;
+	config->vcdt2 = 0x83008200;
 	for (i = 0; i < VC_MAX_CHANNEL; i++) {
 		sprintf(csi_name, "csi2_vc%d", i);
 
@@ -549,6 +676,8 @@ static int rcar_csi2_parse_dt(struct device_node *np,
 				config->vcdt |= (0x24 << (i * 16));
 			else if (!strcmp(str, "ycbcr422"))
 				config->vcdt |= (0x1e << (i * 16));
+			else if (!strcmp(str, "raw8"))
+				config->vcdt |= (0x2a << (i * 16));
 			else
 				config->vcdt |= 0;
 
@@ -563,6 +692,8 @@ static int rcar_csi2_parse_dt(struct device_node *np,
 				config->vcdt2 |= (0x24 << (j * 16));
 			else if (!strcmp(str, "ycbcr422"))
 				config->vcdt2 |= (0x1e << (j * 16));
+			else if (!strcmp(str, "raw8"))
+				config->vcdt2 |= (0x2a << (j * 16));
 			else
 				config->vcdt2 |= 0;
 
@@ -584,6 +715,7 @@ static int rcar_csi2_probe(struct platform_device *pdev)
 	/* Platform data specify the PHY, lanes, ECC, CRC */
 	struct rcar_csi2_pdata *pdata;
 	struct rcar_csi2_link_config link_config;
+	int i;
 
 	dev_dbg(&pdev->dev, "CSI2 probed.\n");
 
@@ -594,12 +726,7 @@ static int rcar_csi2_probe(struct platform_device *pdev)
 		if (ret)
 			return ret;
 
-		if (link_config.lanes == 4)
-			dev_info(&pdev->dev,
-				"Detected rgb888 in rcar_csi2_parse_dt\n");
-		else
-			dev_info(&pdev->dev,
-				"Detected YCbCr422 in rcar_csi2_parse_dt\n");
+		dev_info(&pdev->dev, "Data lanes %d, link freq %d\n", link_config.lanes, link_config.csi_rate);
 	} else {
 		pdata = pdev->dev.platform_data;
 		if (!pdata)
@@ -631,23 +758,27 @@ static int rcar_csi2_probe(struct platform_device *pdev)
 		return ret;
 
 	priv->pdev = pdev;
-	priv->subdev.owner = THIS_MODULE;
-	priv->subdev.dev = &pdev->dev;
 	priv->lanes = link_config.lanes;
 	priv->vcdt = link_config.vcdt;
 	priv->vcdt2 = link_config.vcdt2;
+	priv->csi_rate = link_config.csi_rate;
+	atomic_set(&priv->use_count, 0);
 
-	platform_set_drvdata(pdev, &priv->subdev);
+	platform_set_drvdata(pdev, priv);
 
-	v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops);
-	v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
+	for (i= 0; i < 4; i++) {
+		priv->subdev[i].owner = THIS_MODULE;
+		priv->subdev[i].dev = &pdev->dev;
+		v4l2_subdev_init(&priv->subdev[i], &rcar_csi2_subdev_ops);
+		v4l2_set_subdevdata(&priv->subdev[i], priv);
 
-	snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "rcar_csi2.%s",
-		 dev_name(&pdev->dev));
+		snprintf(priv->subdev[i].name, V4L2_SUBDEV_NAME_SIZE, "rcar_csi2.%s",
+			 dev_name(&pdev->dev));
 
-	ret = v4l2_async_register_subdev(&priv->subdev);
-	if (ret < 0)
-		return ret;
+		ret = v4l2_async_register_subdev(&priv->subdev[i]);
+		if (ret < 0)
+			return ret;
+	}
 
 	spin_lock_init(&priv->lock);
 
@@ -660,10 +791,11 @@ static int rcar_csi2_probe(struct platform_device *pdev)
 
 static int rcar_csi2_remove(struct platform_device *pdev)
 {
-	struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
-	struct rcar_csi2 *priv = container_of(subdev, struct rcar_csi2, subdev);
+	struct rcar_csi2 *priv = platform_get_drvdata(pdev);
+	int i;
 
-	v4l2_async_unregister_subdev(&priv->subdev);
+	for (i= 0; i < 4; i++)
+		v4l2_async_unregister_subdev(&priv->subdev[i]);
 	pm_runtime_disable(&pdev->dev);
 
 	return 0;
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
index 400958b..9733555 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -26,6 +26,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/of_graph.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
@@ -34,11 +35,12 @@
 
 #include <media/soc_camera.h>
 #include <media/drv-intf/soc_mediabus.h>
+#include <media/v4l2-async.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
 #include <media/v4l2-mediabus.h>
-#include <media/v4l2-of.h>
 #include <media/v4l2-subdev.h>
 #include <media/videobuf2-dma-contig.h>
 
@@ -95,17 +97,21 @@
 #define VNC8A_REG	0xF0	/* Video n Coefficient Set C8A Register */
 #define VNC8B_REG	0xF4	/* Video n Coefficient Set C8B Register */
 #define VNC8C_REG	0xF8	/* Video n Coefficient Set C8C Register */
+#define VNLUTP_REG	0x100	/* Video n Lookup table pointer */
+#define VNLUTD_REG	0x104	/* Video n Lookup table data register */
 
 /* Register bit fields for R-Car VIN */
 /* Video n Main Control Register bits */
 #define VNMC_DPINE		(1 << 27)
 #define VNMC_SCLE		(1 << 26)
 #define VNMC_FOC		(1 << 21)
+#define VNMC_LUTE		(1 << 20)
 #define VNMC_YCAL		(1 << 19)
 #define VNMC_INF_YUV8_BT656	(0 << 16)
 #define VNMC_INF_YUV8_BT601	(1 << 16)
 #define VNMC_INF_YUV10_BT656	(2 << 16)
 #define VNMC_INF_YUV10_BT601	(3 << 16)
+#define VNMC_INF_RAW8		(4 << 16)
 #define VNMC_INF_YUV16		(5 << 16)
 #define VNMC_INF_RGB888		(6 << 16)
 #define VNMC_INF_MASK		(7 << 16)
@@ -138,6 +144,7 @@
 #define VNINTS_FOS		(1 << 0)
 
 /* Video n Data Mode Register bits */
+#define VNDMR_YMODE_Y8		(1 << 12)
 #define VNDMR_EXRGB		(1 << 8)
 #define VNDMR_BPSM		(1 << 4)
 #define VNDMR_DTMD_YCSEP	(1 << 1)
@@ -154,7 +161,7 @@
 #define VNCSI_IFMD_REG	0x20	/* Video n CSI2 Interface Mode Register */
 
 #define VNCSI_IFMD_DES1		(1 << 26) /* CSI20 */
-#define VNCSI_IFMD_DES0		(1 << 25) /* H3:CSI40/41, M3:CSI40 */
+#define VNCSI_IFMD_DES0		(1 << 25) /* H3:CSI40/41, M3:CSI40, V3M:CSI40 */
 
 #define VNCSI_IFMD_CSI_CHSEL(n)	(n << 0)
 #define VNCSI_IFMD_SEL_NUMBER	5
@@ -178,6 +185,10 @@
 #define RCAR_VIN_BT656			(1 << 3)
 #define RCAR_VIN_CSI2			(1 << 4)
 
+static int lut_reverse;
+module_param(lut_reverse, int, 0644);
+MODULE_PARM_DESC(lut_reverse, " Use LUT for data order reverse (only 8-bit data allowed)*/");
+
 static int ifmd0_reg_match[VNCSI_IFMD_SEL_NUMBER];
 static int ifmd4_reg_match[VNCSI_IFMD_SEL_NUMBER];
 static int ifmd0_init = true;
@@ -185,6 +196,7 @@ static int ifmd4_init = true;
 
 enum chip_id {
 	RCAR_GEN3,
+	RCAR_V3M,
 	RCAR_M3,
 	RCAR_H3,
 	RCAR_GEN2,
@@ -360,10 +372,54 @@ static const struct vin_gen3_ifmd vin_m3_vc_ifmd[] = {
 	},
 };
 
+static const struct vin_gen3_ifmd vin_v3_vc_ifmd[] = {
+	{ 0x0000,
+		{
+			{RCAR_CSI40, RCAR_VIRTUAL_CH0},
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+			{RCAR_CSI40, RCAR_VIRTUAL_CH1},
+		}
+	},
+	{ 0x0001,
+		{
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+			{RCAR_CSI40, RCAR_VIRTUAL_CH0},
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+		}
+	},
+	{ 0x0002,
+		{
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+			{RCAR_CSI40, RCAR_VIRTUAL_CH0},
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+		}
+	},
+	{ 0x0003,
+		{
+			{RCAR_CSI40, RCAR_VIRTUAL_CH0},
+			{RCAR_CSI40, RCAR_VIRTUAL_CH1},
+			{RCAR_CSI40, RCAR_VIRTUAL_CH2},
+			{RCAR_CSI40, RCAR_VIRTUAL_CH3},
+		}
+	},
+	{ 0x0004,
+		{
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+			{RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE},
+		}
+	},
+};
+
 enum csi2_fmt {
 	RCAR_CSI_FMT_NONE = -1,
 	RCAR_CSI_RGB888,
 	RCAR_CSI_YCBCR422,
+	RCAR_CSI_RAW8,
 };
 
 struct vin_coeff {
@@ -729,10 +785,14 @@ struct rcar_vin_priv {
 	enum csi2_fmt			csi_fmt;
 	enum virtual_ch			vc;
 	bool				csi_sync;
+	bool				deser_sync;
+	int				lut_updated;
 
 	struct rcar_vin_async_client	*async_client;
 	/* Asynchronous CSI2 linking */
 	struct v4l2_subdev		*csi2_sd;
+	/* Asynchronous Deserializer linking */
+	struct v4l2_subdev		*deser_sd;
 	/* Synchronous probing compatibility */
 	struct platform_device		*csi2_pdev;
 
@@ -849,7 +909,8 @@ static int rcar_vin_videobuf_setup(struct vb2_queue *vq,
 	struct rcar_vin_priv *priv = ici->priv;
 	struct rcar_vin_cam *cam = icd->host_priv;
 
-	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3 ||
+		priv->chip == RCAR_V3M) {
 		if ((priv->ratio_h > 0x10000) || (priv->ratio_v > 0x10000)) {
 			dev_err(icd->parent, "Scaling rate parameter error\n");
 			return -EINVAL;
@@ -894,6 +955,8 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
 	struct rcar_vin_cam *cam = icd->host_priv;
 	u32 vnmc, dmr, interrupts;
 	bool progressive = false, output_is_yuv = false, input_is_yuv = false;
+	int i;
+	u32 lutd;
 
 	switch (priv->field) {
 	case V4L2_FIELD_TOP:
@@ -944,6 +1007,10 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
 			VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
 		input_is_yuv = true;
 		break;
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+		vnmc |= VNMC_INF_RAW8 | VNMC_BPS;
+		break;
 	default:
 		break;
 	}
@@ -951,7 +1018,8 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
 	/* output format */
 	switch (icd->current_fmt->host_fmt->fourcc) {
 	case V4L2_PIX_FMT_NV12:
-		if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+		if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3 ||
+			priv->chip == RCAR_V3M) {
 			iowrite32(ALIGN((cam->out_width * cam->out_height),
 					 0x80), priv->base + VNUVAOF_REG);
 			dmr = VNDMR_DTMD_YCSEP_YCBCR420;
@@ -975,6 +1043,10 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
 		dmr = 0;
 		output_is_yuv = true;
 		break;
+	case V4L2_PIX_FMT_GREY:
+		dmr = VNDMR_DTMD_YCSEP | VNDMR_YMODE_Y8;
+		output_is_yuv = true;
+		break;
 	case V4L2_PIX_FMT_ARGB555:
 		dmr = VNDMR_DTMD_ARGB;
 		break;
@@ -983,6 +1055,7 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
 		break;
 	case V4L2_PIX_FMT_XBGR32:
 		if (priv->chip != RCAR_H3 && priv->chip != RCAR_M3 &&
+			priv->chip != RCAR_V3M &&
 		    priv->chip != RCAR_GEN2 && priv->chip != RCAR_H1 &&
 		    priv->chip != RCAR_E1)
 			goto e_format;
@@ -990,11 +1063,16 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
 		dmr = VNDMR_EXRGB;
 		break;
 	case V4L2_PIX_FMT_ABGR32:
-		if (priv->chip != RCAR_H3 && priv->chip != RCAR_M3)
+		if (priv->chip != RCAR_H3 && priv->chip != RCAR_M3 &&
+			priv->chip != RCAR_V3M)
 			goto e_format;
 
 		dmr = VNDMR_EXRGB | VNDMR_DTMD_ARGB;
 		break;
+	case V4L2_PIX_FMT_SBGGR8:
+	case V4L2_PIX_FMT_SBGGR12:
+		dmr = 0;
+		break;
 	default:
 		goto e_format;
 	}
@@ -1006,13 +1084,16 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
 	if (input_is_yuv == output_is_yuv)
 		vnmc |= VNMC_BPS;
 
-	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3 ||
+		priv->chip == RCAR_V3M) {
 		if (priv->pdata_flags & RCAR_VIN_CSI2)
 			vnmc &= ~VNMC_DPINE;
 		else
 			vnmc |= VNMC_DPINE;
 
-		if ((icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_NV12)
+		if ((icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_NV12) &&
+		    (icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_SBGGR8) &&
+		    (icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_SBGGR12)
 			&& is_scaling(cam))
 			vnmc |= VNMC_SCLE;
 	}
@@ -1024,6 +1105,33 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
 	if (vin_debug)
 		interrupts |= VNIE_FOE;
 
+	if (lut_reverse && !priv->lut_updated) {
+		iowrite32(0, priv->base + VNLUTP_REG);
+
+		for (i = 0; i < 1024; i++) {
+			/* reverse MSB 8bits image at 10bit LUT address */
+			lutd  = ((i >> 2) & BIT(0) ? BIT(7) : 0);
+			lutd |= ((i >> 2) & BIT(1) ? BIT(6) : 0);
+			lutd |= ((i >> 2) & BIT(2) ? BIT(5) : 0);
+			lutd |= ((i >> 2) & BIT(3) ? BIT(4) : 0);
+			lutd |= ((i >> 2) & BIT(4) ? BIT(3) : 0);
+			lutd |= ((i >> 2) & BIT(5) ? BIT(2) : 0);
+			lutd |= ((i >> 2) & BIT(6) ? BIT(1) : 0);
+			lutd |= ((i >> 2) & BIT(7) ? BIT(0) : 0);
+#if 0
+			/* strait (no any density convertion, used for testing) */
+			lutd = i >> 2;
+#endif
+			lutd = (lutd << 16) | (lutd << 8) | lutd;
+			iowrite32(lutd, priv->base + VNLUTD_REG);
+		}
+		/* update LUT table once */
+		priv->lut_updated = 1;
+	}
+
+	if (lut_reverse)
+		vnmc |= VNMC_LUTE;
+
 	/* ack interrupts */
 	iowrite32(interrupts, priv->base + VNINTS_REG);
 	/* enable interrupts */
@@ -1162,6 +1270,10 @@ static void rcar_vin_videobuf_queue(struct vb2_buffer *vb)
  */
 static void rcar_vin_wait_stop_streaming(struct rcar_vin_priv *priv)
 {
+	/* update the status if hardware is not stopped */
+	if (ioread32(priv->base + VNMS_REG) & VNMS_CA)
+		priv->state = RUNNING;
+
 	while (priv->state != STOPPED) {
 		/* issue stop if running */
 		if (priv->state == RUNNING)
@@ -1312,6 +1424,26 @@ static struct v4l2_subdev *find_csi2(struct rcar_vin_priv *pcdev)
 	return NULL;
 }
 
+static struct v4l2_subdev *find_deser(struct rcar_vin_priv *pcdev)
+{
+	struct v4l2_subdev *sd;
+	char name[] = "max9286";
+	char name2[] = "ti9x4";
+
+	v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev) {
+		if (!strncmp(name, sd->name, sizeof(name) - 1)) {
+			pcdev->deser_sd = sd;
+			return sd;
+		}
+		if (!strncmp(name2, sd->name, sizeof(name2) - 1)) {
+			pcdev->deser_sd = sd;
+			return sd;
+		}
+	}
+
+	return NULL;
+}
+
 static int rcar_vin_add_device(struct soc_camera_device *icd)
 {
 	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -1323,9 +1455,11 @@ static int rcar_vin_add_device(struct soc_camera_device *icd)
 
 	pm_runtime_get_sync(ici->v4l2_dev.dev);
 
-	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3 ||
+		priv->chip == RCAR_V3M) {
 		struct v4l2_subdev *csi2_sd = find_csi2(priv);
-		int ret;
+		struct v4l2_subdev *deser_sd = find_deser(priv);
+		int ret = 0;
 
 		if (csi2_sd) {
 			csi2_sd->grp_id = soc_camera_grp_id(icd);
@@ -1340,6 +1474,18 @@ static int rcar_vin_add_device(struct soc_camera_device *icd)
 			if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
 				return ret;
 		}
+		if (deser_sd) {
+			v4l2_set_subdev_hostdata(deser_sd, icd);
+
+			ret = v4l2_subdev_call(deser_sd, core, s_power, 1);
+			priv->deser_sync = true;
+
+			if (ret < 0 && ret != -EINVAL)
+				priv->deser_sync = false;
+
+			if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+				return ret;
+		}
 		/*
 		 * -ENODEV is special:
 		 * either csi2_sd == NULL or the CSI-2 driver
@@ -1367,6 +1513,7 @@ static void rcar_vin_remove_device(struct soc_camera_device *icd)
 	struct rcar_vin_priv *priv = ici->priv;
 	struct vb2_v4l2_buffer *vbuf;
 	struct v4l2_subdev *csi2_sd = find_csi2(priv);
+	struct v4l2_subdev *deser_sd = find_deser(priv);
 	int i;
 
 	/* disable capture, disable interrupts */
@@ -1393,6 +1540,8 @@ static void rcar_vin_remove_device(struct soc_camera_device *icd)
 
 	if ((csi2_sd) && (priv->csi_sync))
 		v4l2_subdev_call(csi2_sd, core, s_power, 0);
+	if ((deser_sd) && (priv->deser_sync))
+		v4l2_subdev_call(deser_sd, core, s_power, 0);
 
 	dev_dbg(icd->parent, "R-Car VIN driver detached from camera %d\n",
 		icd->devnum);
@@ -1569,14 +1718,21 @@ static int rcar_vin_set_rect(struct soc_camera_device *icd)
 		break;
 	}
 
-	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
-		if ((icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_NV12)
+	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3 ||
+		priv->chip == RCAR_V3M) {
+		if ((icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_NV12) &&
+		    (icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_SBGGR8) &&
+		    (icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_SBGGR12)
 			&& is_scaling(cam)) {
 			ret = rcar_vin_uds_set(priv, cam);
 			if (ret < 0)
 				return ret;
 		}
-		if (is_scaling(cam) ||
+		if ((icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_SBGGR8) ||
+		    (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_SBGGR12))
+			iowrite32(ALIGN(cam->out_width / 2, 0x10),
+				 priv->base + VNIS_REG);
+		else if (is_scaling(cam) ||
 		   (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV16) ||
 		   (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV12))
 			iowrite32(ALIGN(cam->out_width, 0x20),
@@ -1720,14 +1876,16 @@ static int rcar_vin_set_bus_param(struct soc_camera_device *icd)
 	if (ret < 0 && ret != -ENOIOCTLCMD)
 		return ret;
 
-	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3 ||
+		priv->chip == RCAR_V3M) {
 		if (cfg.type == V4L2_MBUS_CSI2)
 			vnmc &= ~VNMC_DPINE;
 		else
 			vnmc |= VNMC_DPINE;
 	}
 
-	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3)
+	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3 ||
+		priv->chip == RCAR_V3M)
 		val = VNDMR2_FTEV;
 	else
 		val = VNDMR2_FTEV | VNDMR2_VLV(1);
@@ -1815,6 +1973,14 @@ static const struct soc_mbus_pixelfmt rcar_vin_formats[] = {
 		.layout			= SOC_MBUS_LAYOUT_PACKED,
 	},
 	{
+		.fourcc			= V4L2_PIX_FMT_GREY,
+		.name			= "GREY8",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_NONE,
+		.order			= SOC_MBUS_ORDER_LE,
+		.layout			= SOC_MBUS_LAYOUT_PACKED,
+	},
+	{
 		.fourcc			= V4L2_PIX_FMT_RGB565,
 		.name			= "RGB565",
 		.bits_per_sample	= 16,
@@ -1846,6 +2012,22 @@ static const struct soc_mbus_pixelfmt rcar_vin_formats[] = {
 		.order			= SOC_MBUS_ORDER_LE,
 		.layout			= SOC_MBUS_LAYOUT_PACKED,
 	},
+	{
+		.fourcc			= V4L2_PIX_FMT_SBGGR8,
+		.name			= "Bayer 8 BGGR",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_NONE,
+		.order			= SOC_MBUS_ORDER_LE,
+		.layout			= SOC_MBUS_LAYOUT_PACKED,
+	},
+	{
+		.fourcc			= V4L2_PIX_FMT_SBGGR12,
+		.name			= "Bayer 12 BGGR",
+		.bits_per_sample	= 8,
+		.packing		= SOC_MBUS_PACKING_NONE,
+		.order			= SOC_MBUS_ORDER_LE,
+		.layout			= SOC_MBUS_LAYOUT_PACKED,
+	},
 };
 
 static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
@@ -1959,6 +2141,8 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
 	case MEDIA_BUS_FMT_YUYV8_2X8:
 	case MEDIA_BUS_FMT_YUYV10_2X10:
 	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
 		if (cam->extra_fmt)
 			break;
 
@@ -2165,12 +2349,15 @@ static int rcar_vin_set_fmt(struct soc_camera_device *icd,
 	case V4L2_PIX_FMT_ABGR32:
 	case V4L2_PIX_FMT_UYVY:
 	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_GREY:
 	case V4L2_PIX_FMT_RGB565:
 	case V4L2_PIX_FMT_ARGB555:
 	case V4L2_PIX_FMT_NV16:
 		can_scale = true;
 		break;
 	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_SBGGR8:
+	case V4L2_PIX_FMT_SBGGR12:
 	default:
 		can_scale = false;
 		break;
@@ -2263,7 +2450,8 @@ static int rcar_vin_try_fmt(struct soc_camera_device *icd,
 	/* odd number clipping by pixel post clip processing, */
 	/* it is outputted to a memory per even pixels. */
 	if ((pixfmt == V4L2_PIX_FMT_NV16) || (pixfmt == V4L2_PIX_FMT_NV12) ||
-		(pixfmt == V4L2_PIX_FMT_YUYV) || (pixfmt == V4L2_PIX_FMT_UYVY))
+		(pixfmt == V4L2_PIX_FMT_YUYV) || (pixfmt == V4L2_PIX_FMT_UYVY) ||
+		(pixfmt == V4L2_PIX_FMT_GREY))
 		v4l_bound_align_image(&pix->width, 5, priv->max_width, 1,
 				      &pix->height, 2, priv->max_height, 0, 0);
 	else
@@ -2289,7 +2477,8 @@ static int rcar_vin_try_fmt(struct soc_camera_device *icd,
 	if (ret < 0)
 		return ret;
 
-	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3 ||
+		priv->chip == RCAR_V3M) {
 		/* Adjust max scaling size for Gen3 */
 		if (pix->width > 4096)
 			pix->width = priv->max_width;
@@ -2432,6 +2621,19 @@ static int rcar_vin_cropcap(struct soc_camera_device *icd,
 }
 #endif
 
+static int rcar_vin_get_edid(struct soc_camera_device *icd,
+			     struct v4l2_edid *edid)
+{
+	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+	int ret;
+
+	ret = v4l2_subdev_call(sd, pad, get_edid, edid);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
 static struct soc_camera_host_ops rcar_vin_host_ops = {
 	.owner		= THIS_MODULE,
 	.add		= rcar_vin_add_device,
@@ -2450,10 +2652,12 @@ static struct soc_camera_host_ops rcar_vin_host_ops = {
 	.get_selection	= rcar_vin_get_selection,
 	.cropcap	= rcar_vin_cropcap,
 #endif
+	.get_edid	= rcar_vin_get_edid,
 };
 
 #ifdef CONFIG_OF
 static const struct of_device_id rcar_vin_of_table[] = {
+	{ .compatible = "renesas,vin-r8a7797", .data = (void *)RCAR_V3M },
 	{ .compatible = "renesas,vin-r8a7796", .data = (void *)RCAR_M3 },
 	{ .compatible = "renesas,vin-r8a7795", .data = (void *)RCAR_H3 },
 	{ .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 },
@@ -2469,7 +2673,7 @@ static const struct of_device_id rcar_vin_of_table[] = {
 MODULE_DEVICE_TABLE(of, rcar_vin_of_table);
 #endif
 
-#define MAP_MAX_NUM 32
+#define MAP_MAX_NUM 128
 static DECLARE_BITMAP(device_map, MAP_MAX_NUM);
 static DEFINE_MUTEX(list_lock);
 
@@ -2578,6 +2782,12 @@ static struct soc_camera_device *rcar_vin_add_pdev(
 	return platform_get_drvdata(pdev);
 }
 
+static const struct v4l2_async_notifier_operations rcar_vin_sensor_ops = {
+	.bound = rcar_vin_async_bound,
+	.unbind = rcar_vin_async_unbind,
+	.complete = rcar_vin_async_complete,
+};
+
 static int rcar_vin_soc_of_bind(struct rcar_vin_priv *priv,
 		       struct soc_camera_host *ici,
 		       struct device_node *ep,
@@ -2597,8 +2807,8 @@ static int rcar_vin_soc_of_bind(struct rcar_vin_priv *priv,
 	if (!info)
 		return -ENOMEM;
 
-	info->sasd.asd.match.of.node = remote;
-	info->sasd.asd.match_type = V4L2_ASYNC_MATCH_OF;
+	info->sasd.asd.match.fwnode.fwnode = of_fwnode_handle(remote);
+	info->sasd.asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
 	info->subdev = &info->sasd.asd;
 
 	/* Or shall this be managed by the soc-camera device? */
@@ -2618,9 +2828,7 @@ static int rcar_vin_soc_of_bind(struct rcar_vin_priv *priv,
 
 	sasc->notifier.subdevs = &info->subdev;
 	sasc->notifier.num_subdevs = 1;
-	sasc->notifier.bound = rcar_vin_async_bound;
-	sasc->notifier.unbind = rcar_vin_async_unbind;
-	sasc->notifier.complete = rcar_vin_async_complete;
+	sasc->notifier.ops = &rcar_vin_sensor_ops;
 
 	priv->async_client = sasc;
 
@@ -2651,7 +2859,7 @@ static int rcar_vin_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *match = NULL;
 	struct rcar_vin_priv *priv;
-	struct v4l2_of_endpoint ep;
+	struct v4l2_fwnode_endpoint ep;
 	struct device_node *np;
 	struct resource *mem;
 	unsigned int pdata_flags;
@@ -2659,7 +2867,10 @@ static int rcar_vin_probe(struct platform_device *pdev)
 	const char *str;
 	unsigned int i;
 	struct device_node *epn = NULL, *ren = NULL;
+	struct device_node *csi2_ren = NULL, *max9286_ren = NULL, *ti9x4_ren = NULL;
 	bool csi_use = false;
+	bool max9286_use = false;
+	bool ti9x4_use = false;
 
 	match = of_match_device(of_match_ptr(rcar_vin_of_table), &pdev->dev);
 
@@ -2686,16 +2897,25 @@ static int rcar_vin_probe(struct platform_device *pdev)
 		dev_dbg(&pdev->dev, "node name:%s\n",
 			of_node_full_name(ren->parent));
 
-		if (strcmp(ren->parent->name, "csi2") == 0)
+		if (strcmp(ren->parent->name, "csi2") == 0) {
+			csi2_ren = ren;
 			csi_use = true;
+		}
 
-		of_node_put(ren);
+		if (strcmp(ren->parent->name, "max9286") == 0) {
+			max9286_ren = of_parse_phandle(epn, "remote-endpoint", 0);
+			max9286_use = true;
+		}
 
-		if (i)
-			break;
+		if (strcmp(ren->parent->name, "ti9x4") == 0) {
+			ti9x4_ren = of_parse_phandle(epn, "remote-endpoint", 0);
+			ti9x4_use = true;
+		}
+
+		of_node_put(ren);
 	}
 
-	ret = v4l2_of_parse_endpoint(np, &ep);
+	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep);
 	if (ret) {
 		dev_err(&pdev->dev, "could not parse endpoint\n");
 		return ret;
@@ -2744,17 +2964,24 @@ static int rcar_vin_probe(struct platform_device *pdev)
 	priv->ici.drv_name = dev_name(&pdev->dev);
 	priv->ici.ops = &rcar_vin_host_ops;
 	priv->csi_sync = false;
+	priv->deser_sync = false;
 
 	priv->pdata_flags = pdata_flags;
 	if (!match) {
 		priv->ici.nr = pdev->id;
 		priv->chip = pdev->id_entry->driver_data;
 	} else {
-		priv->ici.nr = of_alias_get_id(pdev->dev.of_node, "vin");
+		if (of_property_read_u32(pdev->dev.of_node, "renesas,id", &i)) {
+			dev_err(&pdev->dev, "%s: No renesas,id property found\n",
+				of_node_full_name(pdev->dev.of_node));
+			return -EINVAL;
+		}
+		priv->ici.nr = i;
 		priv->chip = (enum chip_id)match->data;
 	}
 
-	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3 ||
+		priv->chip == RCAR_V3M) {
 		priv->max_width = 4096;
 		priv->max_height = 4096;
 	} else {
@@ -2762,7 +2989,8 @@ static int rcar_vin_probe(struct platform_device *pdev)
 		priv->max_height = 2048;
 	}
 
-	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) {
+	if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3 ||
+		priv->chip == RCAR_V3M) {
 		u32 ifmd = 0;
 		bool match_flag = false;
 		const struct vin_gen3_ifmd *gen3_ifmd_table = NULL;
@@ -2841,6 +3069,8 @@ static int rcar_vin_probe(struct platform_device *pdev)
 			gen3_ifmd_table = vin_h3_vc_ifmd;
 		else if (priv->chip == RCAR_M3)
 			gen3_ifmd_table = vin_m3_vc_ifmd;
+		else if (priv->chip == RCAR_V3M)
+			gen3_ifmd_table = vin_v3_vc_ifmd;
 
 		for (i = 0; i < num; i++) {
 			if ((gen3_ifmd_table[i].v_sel[priv->index].csi2_ch
@@ -2924,7 +3154,19 @@ static int rcar_vin_probe(struct platform_device *pdev)
 		goto cleanup;
 
 	if (csi_use) {
-		ret = rcar_vin_soc_of_bind(priv, &priv->ici, epn, ren->parent);
+		ret = rcar_vin_soc_of_bind(priv, &priv->ici, epn, csi2_ren->parent);
+		if (ret)
+			goto cleanup;
+	}
+
+	if (max9286_use) {
+		ret = rcar_vin_soc_of_bind(priv, &priv->ici, epn, max9286_ren);
+		if (ret)
+			goto cleanup;
+	}
+
+	if (ti9x4_use) {
+		ret = rcar_vin_soc_of_bind(priv, &priv->ici, epn, ti9x4_ren);
 		if (ret)
 			goto cleanup;
 	}
@@ -2983,6 +3225,9 @@ static int rcar_vin_resume(struct device *dev)
 	} else if (priv->chip == RCAR_M3) {
 		ifmd = VNCSI_IFMD_DES1;
 		gen3_ifmd_table = vin_m3_vc_ifmd;
+	} else if (priv->chip == RCAR_V3M) {
+		ifmd = VNCSI_IFMD_DES1;
+		gen3_ifmd_table = vin_v3_vc_ifmd;
 	}
 
 	for (i = 0; i < num; i++) {
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 916ff68..1f840cf 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -44,7 +44,7 @@
 #define DEFAULT_WIDTH	640
 #define DEFAULT_HEIGHT	480
 
-#define MAP_MAX_NUM 32
+#define MAP_MAX_NUM 128
 static DECLARE_BITMAP(device_map, MAP_MAX_NUM);
 static LIST_HEAD(hosts);
 static LIST_HEAD(devices);
@@ -1044,6 +1044,18 @@ static int soc_camera_s_parm(struct file *file, void *fh,
 	return -ENOIOCTLCMD;
 }
 
+static int soc_camera_g_edid(struct file *file, void *fh,
+			     struct v4l2_edid *edid)
+{
+	struct soc_camera_device *icd = file->private_data;
+	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+	if (ici->ops->get_edid)
+		return ici->ops->get_edid(icd, edid);
+
+	return -ENOIOCTLCMD;
+}
+
 static int soc_camera_probe(struct soc_camera_host *ici,
 			    struct soc_camera_device *icd);
 
@@ -1602,7 +1614,7 @@ static void scan_of_host(struct soc_camera_host *ici)
 		of_node_put(ren);
 
 		if (i) {
-			dev_err(dev, "multiple subdevices aren't supported yet!\n");
+			dev_dbg(dev, "multiple subdevices aren't supported yet!\n");
 			break;
 		}
 	}
@@ -2013,6 +2025,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
 	.vidioc_s_selection	 = soc_camera_s_selection,
 	.vidioc_g_parm		 = soc_camera_g_parm,
 	.vidioc_s_parm		 = soc_camera_s_parm,
+	.vidioc_g_edid		 = soc_camera_g_edid,
 };
 
 static int video_dev_create(struct soc_camera_device *icd)
diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c
index 0ad4b28..d4e5c1e 100644
--- a/drivers/media/platform/soc_camera/soc_mediabus.c
+++ b/drivers/media/platform/soc_camera/soc_mediabus.c
@@ -57,6 +57,16 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
 		.layout			= SOC_MBUS_LAYOUT_PACKED,
 	},
 }, {
+	.code = MEDIA_BUS_FMT_YUYV10_2X10,
+	.fmt = {
+		.fourcc			= V4L2_PIX_FMT_YUYV,
+		.name			= "YUYV",
+		.bits_per_sample	= 10,
+		.packing		= SOC_MBUS_PACKING_2X10_PADHI,
+		.order			= SOC_MBUS_ORDER_LE,
+		.layout			= SOC_MBUS_LAYOUT_PACKED,
+	},
+}, {
 	.code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
 	.fmt = {
 		.fourcc			= V4L2_PIX_FMT_RGB555,
@@ -403,6 +413,10 @@ int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf,
 		*numerator = 2;
 		*denominator = 1;
 		return 0;
+	case SOC_MBUS_PACKING_2X10_PADHI:
+		*numerator = 3;
+		*denominator = 1;
+		return 0;
 	case SOC_MBUS_PACKING_1_5X8:
 		*numerator = 3;
 		*denominator = 2;
@@ -428,6 +442,8 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
 	case SOC_MBUS_PACKING_2X8_PADLO:
 	case SOC_MBUS_PACKING_EXTEND16:
 		return width * 2;
+	case SOC_MBUS_PACKING_2X10_PADHI:
+		return width * 3;
 	case SOC_MBUS_PACKING_1_5X8:
 		return width * 3 / 2;
 	case SOC_MBUS_PACKING_VARIABLE:
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 7b6c556..1fed0f8 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -398,6 +398,7 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
 		case V4L2_ASYNC_MATCH_I2C:
 			break;
 		case V4L2_ASYNC_MATCH_FWNODE:
+#if 0
 			if (v4l2_async_notifier_fwnode_has_async_subdev(
 				    notifier, asd->match.fwnode.fwnode, i)) {
 				dev_err(dev,
@@ -405,6 +406,7 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier)
 				ret = -EEXIST;
 				goto err_unlock;
 			}
+#endif
 			break;
 		default:
 			dev_err(dev, "Invalid match type %u on %p\n",
diff --git a/include/media/drv-intf/soc_mediabus.h b/include/media/drv-intf/soc_mediabus.h
index 2ff7737..e5f3f53 100644
--- a/include/media/drv-intf/soc_mediabus.h
+++ b/include/media/drv-intf/soc_mediabus.h
@@ -21,6 +21,8 @@
  * @SOC_MBUS_PACKING_2X8_PADHI:	16 bits transferred in 2 8-bit samples, in the
  *				possibly incomplete byte high bits are padding
  * @SOC_MBUS_PACKING_2X8_PADLO:	as above, but low bits are padding
+ * @SOC_MBUS_PACKING_2X10_PADHI:20 bits transferred in 2 10-bit samples. The
+ *                              high bits are padding
  * @SOC_MBUS_PACKING_EXTEND16:	sample width (e.g., 10 bits) has to be extended
  *				to 16 bits
  * @SOC_MBUS_PACKING_VARIABLE:	compressed formats with variable packing
@@ -33,6 +35,7 @@ enum soc_mbus_packing {
 	SOC_MBUS_PACKING_NONE,
 	SOC_MBUS_PACKING_2X8_PADHI,
 	SOC_MBUS_PACKING_2X8_PADLO,
+	SOC_MBUS_PACKING_2X10_PADHI,
 	SOC_MBUS_PACKING_EXTEND16,
 	SOC_MBUS_PACKING_VARIABLE,
 	SOC_MBUS_PACKING_1_5X8,
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 4d8cb07..4381be0 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -118,6 +118,7 @@ struct soc_camera_host_ops {
 	int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *);
 	int (*enum_framesizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *);
 	unsigned int (*poll)(struct file *, poll_table *);
+	int (*get_edid)(struct soc_camera_device *, struct v4l2_edid *);
 };
 
 #define SOCAM_SENSOR_INVERT_PCLK	(1 << 0)
-- 
2.7.4

