summaryrefslogtreecommitdiffstats
path: root/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0492-media-i2c-move-gmsl-fpdlink-drivers-to-separate-fold.patch
diff options
context:
space:
mode:
Diffstat (limited to 'bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0492-media-i2c-move-gmsl-fpdlink-drivers-to-separate-fold.patch')
-rw-r--r--bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0492-media-i2c-move-gmsl-fpdlink-drivers-to-separate-fold.patch6794
1 files changed, 6794 insertions, 0 deletions
diff --git a/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0492-media-i2c-move-gmsl-fpdlink-drivers-to-separate-fold.patch b/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0492-media-i2c-move-gmsl-fpdlink-drivers-to-separate-fold.patch
new file mode 100644
index 00000000..a4851c0d
--- /dev/null
+++ b/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0492-media-i2c-move-gmsl-fpdlink-drivers-to-separate-fold.patch
@@ -0,0 +1,6794 @@
+From 40f71bc01fcacef975e9fd507ec212a08aeeade9 Mon Sep 17 00:00:00 2001
+From: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
+Date: Mon, 27 Apr 2020 10:47:53 +0300
+Subject: [PATCH] media: i2c: move gmsl/fpdlink drivers to separate
+ folder
+
+Move GMSL/FPDLINK driver to separate folder
+
+Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
+---
+ drivers/media/i2c/soc_camera/Kconfig | 28 +-
+ drivers/media/i2c/soc_camera/Makefile | 7 +-
+ drivers/media/i2c/soc_camera/dummy.c | 491 -----------
+ drivers/media/i2c/soc_camera/fpdlink/Kconfig | 5 +
+ drivers/media/i2c/soc_camera/fpdlink/Makefile | 2 +
+ drivers/media/i2c/soc_camera/fpdlink/ti9x4.c | 745 +++++++++++++++++
+ drivers/media/i2c/soc_camera/fpdlink/ti9x4.h | 204 +++++
+ drivers/media/i2c/soc_camera/gmsl/Kconfig | 11 +
+ drivers/media/i2c/soc_camera/gmsl/Makefile | 3 +
+ drivers/media/i2c/soc_camera/gmsl/max9286.c | 1072 +++++++++++++++++++++++++
+ drivers/media/i2c/soc_camera/gmsl/max9288.c | 768 ++++++++++++++++++
+ drivers/media/i2c/soc_camera/imagers/Kconfig | 5 +
+ drivers/media/i2c/soc_camera/imagers/Makefile | 2 +
+ drivers/media/i2c/soc_camera/imagers/dummy.c | 491 +++++++++++
+ drivers/media/i2c/soc_camera/max9286.c | 1072 -------------------------
+ drivers/media/i2c/soc_camera/max9288.c | 768 ------------------
+ drivers/media/i2c/soc_camera/ti9x4.c | 745 -----------------
+ drivers/media/i2c/soc_camera/ti9x4.h | 204 -----
+ 18 files changed, 3315 insertions(+), 3308 deletions(-)
+ delete mode 100644 drivers/media/i2c/soc_camera/dummy.c
+ create mode 100644 drivers/media/i2c/soc_camera/fpdlink/Kconfig
+ create mode 100644 drivers/media/i2c/soc_camera/fpdlink/Makefile
+ create mode 100644 drivers/media/i2c/soc_camera/fpdlink/ti9x4.c
+ create mode 100644 drivers/media/i2c/soc_camera/fpdlink/ti9x4.h
+ create mode 100644 drivers/media/i2c/soc_camera/gmsl/Kconfig
+ create mode 100644 drivers/media/i2c/soc_camera/gmsl/Makefile
+ create mode 100644 drivers/media/i2c/soc_camera/gmsl/max9286.c
+ create mode 100644 drivers/media/i2c/soc_camera/gmsl/max9288.c
+ create mode 100644 drivers/media/i2c/soc_camera/imagers/Kconfig
+ create mode 100644 drivers/media/i2c/soc_camera/imagers/Makefile
+ create mode 100644 drivers/media/i2c/soc_camera/imagers/dummy.c
+ delete mode 100644 drivers/media/i2c/soc_camera/max9286.c
+ delete mode 100644 drivers/media/i2c/soc_camera/max9288.c
+ delete mode 100644 drivers/media/i2c/soc_camera/ti9x4.c
+ delete 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 729b5af..6792522 100644
+--- a/drivers/media/i2c/soc_camera/Kconfig
++++ b/drivers/media/i2c/soc_camera/Kconfig
+@@ -83,30 +83,6 @@ config SOC_CAMERA_TW9910
+ 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_MAX9288
+- tristate "max9288 GMSL support"
+- depends on SOC_CAMERA && I2C
+- help
+- This is a MAXIM max9288 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
+-
+ config SOC_CAMERA_IMX219
+ tristate "imx219 camera support"
+ depends on SOC_CAMERA && I2C
+@@ -118,3 +94,7 @@ config SOC_CAMERA_AR0147
+ depends on SOC_CAMERA && I2C
+ help
+ This is an AR0147 imager glue
++
++source "drivers/media/i2c/soc_camera/gmsl/Kconfig"
++source "drivers/media/i2c/soc_camera/fpdlink/Kconfig"
++source "drivers/media/i2c/soc_camera/imagers/Kconfig"
+diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile
+index d916bba..5e08254 100644
+--- a/drivers/media/i2c/soc_camera/Makefile
++++ b/drivers/media/i2c/soc_camera/Makefile
+@@ -11,11 +11,10 @@ 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_MAX9288) += max9288.o
+-obj-$(CONFIG_SOC_CAMERA_TI9X4) += ti9x4.o
+ obj-$(CONFIG_SOC_CAMERA_OV106XX) += ov106xx.o
+ obj-$(CONFIG_SOC_CAMERA_IMX219) += imx219.o
+-obj-y += dummy.o
+
+ obj-$(CONFIG_SOC_CAMERA_AR0147) += ar0147.o
++obj-y += imagers/
++obj-y += gmsl/
++obj-y += fpdlink/
+diff --git a/drivers/media/i2c/soc_camera/dummy.c b/drivers/media/i2c/soc_camera/dummy.c
+deleted file mode 100644
+index d213fff..0000000
+--- a/drivers/media/i2c/soc_camera/dummy.c
++++ /dev/null
+@@ -1,491 +0,0 @@
+-/*
+- * Dummy sensor camera driver
+- *
+- * Copyright (C) 2019 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>
+-
+-struct dummy_priv {
+- struct v4l2_subdev sd;
+- struct v4l2_ctrl_handler hdl;
+- struct media_pad pad;
+- struct v4l2_rect rect;
+- u8 id[6];
+- int max_width;
+- int max_height;
+- const char * media_bus_format;
+- int mbus_format;
+-};
+-
+-static int width = 1920;
+-module_param(width, int, 0644);
+-MODULE_PARM_DESC(width, " width (default: 1920)");
+-
+-static int height = 1080;
+-module_param(height, int, 0644);
+-MODULE_PARM_DESC(height, " height (default: 1080)");
+-
+-static char *mbus = "yuyv";
+-module_param(mbus, charp, 0644);
+-MODULE_PARM_DESC(mbus, " MEDIA_BUS_FORMAT (default: YUYV)");
+-
+-static inline struct dummy_priv *to_dummy(const struct i2c_client *client)
+-{
+- return container_of(i2c_get_clientdata(client), struct dummy_priv, sd);
+-}
+-
+-static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+-{
+- return &container_of(ctrl->handler, struct dummy_priv, hdl)->sd;
+-}
+-
+-static int dummy_s_stream(struct v4l2_subdev *sd, int enable)
+-{
+- return 0;
+-}
+-
+-static int dummy_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 dummy_priv *priv = to_dummy(client);
+-
+- if (format->pad)
+- return -EINVAL;
+-
+- mf->width = priv->rect.width;
+- mf->height = priv->rect.height;
+- mf->code = priv->mbus_format;
+- mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+- mf->field = V4L2_FIELD_NONE;
+-
+- return 0;
+-}
+-
+-static int dummy_set_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 dummy_priv *priv = to_dummy(client);
+-
+- mf->code = priv->mbus_format;
+- 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 dummy_enum_mbus_code(struct v4l2_subdev *sd,
+- struct v4l2_subdev_pad_config *cfg,
+- struct v4l2_subdev_mbus_code_enum *code)
+-{
+- struct i2c_client *client = v4l2_get_subdevdata(sd);
+- struct dummy_priv *priv = to_dummy(client);
+-
+- if (code->pad || code->index > 0)
+- return -EINVAL;
+-
+- code->code = priv->mbus_format;
+-
+- return 0;
+-}
+-
+-static int dummy_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+-{
+- struct i2c_client *client = v4l2_get_subdevdata(sd);
+- struct dummy_priv *priv = to_dummy(client);
+-
+- memcpy(edid->edid, priv->id, 6);
+-
+- edid->edid[6] = 0xff;
+- edid->edid[7] = client->addr;
+- edid->edid[8] = 'D' >> 8;
+- edid->edid[9] = 'Y' & 0xff;
+-
+- return 0;
+-}
+-
+-static int dummy_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 dummy_priv *priv = to_dummy(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 dummy_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 dummy_priv *priv = to_dummy(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 dummy_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 dummy_g_register(struct v4l2_subdev *sd,
+- struct v4l2_dbg_register *reg)
+-{
+- reg->val = 0;
+- reg->size = sizeof(u16);
+-
+- return 0;
+-}
+-
+-static int dummy_s_register(struct v4l2_subdev *sd,
+- const struct v4l2_dbg_register *reg)
+-{
+- return 0;
+-}
+-#endif
+-
+-static struct v4l2_subdev_core_ops dummy_core_ops = {
+-#ifdef CONFIG_VIDEO_ADV_DEBUG
+- .g_register = dummy_g_register,
+- .s_register = dummy_s_register,
+-#endif
+-};
+-
+-static int dummy_s_ctrl(struct v4l2_ctrl *ctrl)
+-{
+- 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_ANALOGUE_GAIN:
+- case V4L2_CID_EXPOSURE:
+- case V4L2_CID_HFLIP:
+- case V4L2_CID_VFLIP:
+- case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+- break;
+- }
+-
+- return 0;
+-}
+-
+-static const struct v4l2_ctrl_ops dummy_ctrl_ops = {
+- .s_ctrl = dummy_s_ctrl,
+-};
+-
+-static struct v4l2_subdev_video_ops dummy_video_ops = {
+- .s_stream = dummy_s_stream,
+- .g_mbus_config = dummy_g_mbus_config,
+-};
+-
+-static const struct v4l2_subdev_pad_ops dummy_subdev_pad_ops = {
+- .get_edid = dummy_get_edid,
+- .enum_mbus_code = dummy_enum_mbus_code,
+- .get_selection = dummy_get_selection,
+- .set_selection = dummy_set_selection,
+- .get_fmt = dummy_get_fmt,
+- .set_fmt = dummy_set_fmt,
+-};
+-
+-static struct v4l2_subdev_ops dummy_subdev_ops = {
+- .core = &dummy_core_ops,
+- .video = &dummy_video_ops,
+- .pad = &dummy_subdev_pad_ops,
+-};
+-
+-static void dummy_otp_id_read(struct i2c_client *client)
+-{
+- struct dummy_priv *priv = to_dummy(client);
+-
+- /* dummy camera id */
+- priv->id[0] = 'd';
+- priv->id[1] = 'u';
+- priv->id[2] = 'm';
+- priv->id[3] = 'm';
+- priv->id[4] = 'y';
+- priv->id[5] = '.';
+-}
+-
+-static ssize_t dummy_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 dummy_priv *priv = to_dummy(client);
+-
+- dummy_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_dummy, S_IRUGO, dummy_otp_id_show, NULL);
+-
+-static int dummy_initialize(struct i2c_client *client)
+-{
+- struct dummy_priv *priv = to_dummy(client);
+-
+- if (strcmp(priv->media_bus_format, "yuyv") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_YUYV8_2X8;
+- else if (strcmp(priv->media_bus_format, "uyvy") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_UYVY8_2X8;
+- else if (strcmp(priv->media_bus_format, "grey") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_Y8_1X8;
+- else if (strcmp(priv->media_bus_format, "rggb8") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SRGGB8_1X8;
+- else if (strcmp(priv->media_bus_format, "bggr8") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SBGGR8_1X8;
+- else if (strcmp(priv->media_bus_format, "grbg8") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SGRBG8_1X8;
+- else if (strcmp(priv->media_bus_format, "rggb12") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SRGGB12_1X12;
+- else if (strcmp(priv->media_bus_format, "bggr12") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SBGGR12_1X12;
+- else if (strcmp(priv->media_bus_format, "grbg12") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SGRBG12_1X12;
+- else if (strcmp(priv->media_bus_format, "rggb14") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SRGGB14_1X14;
+- else if (strcmp(priv->media_bus_format, "bggr14") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SBGGR14_1X14;
+- else if (strcmp(priv->media_bus_format, "grbg14") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SGRBG14_1X14;
+- else if (strcmp(priv->media_bus_format, "rggb16") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SRGGB16_1X16;
+- else if (strcmp(priv->media_bus_format, "bggr16") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SBGGR16_1X16;
+- else if (strcmp(priv->media_bus_format, "grbg16") == 0)
+- priv->mbus_format = MEDIA_BUS_FMT_SGRBG16_1X16;
+- else {
+- v4l_err(client, "failed to parse mbus format (%s)\n", priv->media_bus_format);
+- return -EINVAL;
+- }
+-
+- /* Read OTP IDs */
+- dummy_otp_id_read(client);
+-
+- dev_info(&client->dev, "Dummy camera sensor, res %dx%d, mbus %s, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n",
+- priv->max_width, priv->max_height, priv->media_bus_format, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
+-
+- return 0;
+-}
+-
+-static int dummy_parse_dt(struct device_node *np, struct dummy_priv *priv)
+-{
+- if (of_property_read_u32(np, "dummy,width", &priv->max_width))
+- priv->max_width = width;
+-
+- if (of_property_read_u32(np, "dummy,height", &priv->max_height))
+- priv->max_height = height;
+-
+- if (of_property_read_string(np, "dummy,mbus", &priv->media_bus_format))
+- priv->media_bus_format = mbus;
+-
+- /* module params override dts */
+- if (strcmp(mbus, "yuyv"))
+- priv->media_bus_format = mbus;
+- if (width != 1920)
+- priv->max_width = width;
+- if (height != 1080)
+- priv->max_height = height;
+-
+- return 0;
+-}
+-
+-static int dummy_probe(struct i2c_client *client,
+- const struct i2c_device_id *did)
+-{
+- struct dummy_priv *priv;
+- int ret;
+-
+- priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+-
+- v4l2_i2c_subdev_init(&priv->sd, client, &dummy_subdev_ops);
+- priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+-
+- v4l2_ctrl_handler_init(&priv->hdl, 4);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_BRIGHTNESS, 0, 16, 1, 7);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_CONTRAST, 0, 16, 1, 7);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_SATURATION, 0, 7, 1, 2);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_HUE, 0, 23, 1, 12);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_GAMMA, -128, 128, 1, 0);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_SHARPNESS, 0, 10, 1, 3);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_AUTOGAIN, 0, 1, 1, 0);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_GAIN, 1, 0x7ff, 1, 0x200);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_ANALOGUE_GAIN, 1, 0xe, 1, 0xa);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_EXPOSURE, 1, 0x600, 1, 0x144);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
+- V4L2_CID_HFLIP, 0, 1, 1, 0);
+- v4l2_ctrl_new_std(&priv->hdl, &dummy_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 = dummy_parse_dt(client->dev.of_node, priv);
+- if (ret)
+- goto cleanup;
+-
+- ret = dummy_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_dummy) != 0) {
+- dev_err(&client->dev, "sysfs otp_id entry creation failed\n");
+- goto cleanup;
+- }
+-
+- return 0;
+-
+-cleanup:
+- media_entity_cleanup(&priv->sd.entity);
+- v4l2_ctrl_handler_free(&priv->hdl);
+- v4l2_device_unregister_subdev(&priv->sd);
+- v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
+- client->addr, client->adapter->name);
+- return ret;
+-}
+-
+-static int dummy_remove(struct i2c_client *client)
+-{
+- struct dummy_priv *priv = i2c_get_clientdata(client);
+-
+- device_remove_file(&client->dev, &dev_attr_otp_id_dummy);
+- 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;
+-}
+-
+-static const struct i2c_device_id dummy_id[] = {
+- { "dummy-camera", 0 },
+- { }
+-};
+-MODULE_DEVICE_TABLE(i2c, dummy_id);
+-
+-static const struct of_device_id dummy_of_ids[] = {
+- { .compatible = "dummy-camera", },
+- { }
+-};
+-MODULE_DEVICE_TABLE(of, dummy_of_ids);
+-
+-static struct i2c_driver dummy_i2c_driver = {
+- .driver = {
+- .name = "dummy-camera",
+- .of_match_table = dummy_of_ids,
+- },
+- .probe = dummy_probe,
+- .remove = dummy_remove,
+- .id_table = dummy_id,
+-};
+-module_i2c_driver(dummy_i2c_driver);
+-
+-MODULE_DESCRIPTION("Dummy SoC camera driver");
+-MODULE_AUTHOR("Vladimir Barinov");
+-MODULE_LICENSE("GPL");
+diff --git a/drivers/media/i2c/soc_camera/fpdlink/Kconfig b/drivers/media/i2c/soc_camera/fpdlink/Kconfig
+new file mode 100644
+index 0000000..9550563
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/fpdlink/Kconfig
+@@ -0,0 +1,5 @@
++config SOC_CAMERA_TI9X4
++ tristate "ti9x4 FPDLink3 support"
++ depends on I2C
++ help
++ This is an Texas Instruments ti9X4 FPDLink3 driver
+diff --git a/drivers/media/i2c/soc_camera/fpdlink/Makefile b/drivers/media/i2c/soc_camera/fpdlink/Makefile
+new file mode 100644
+index 0000000..29f4a59
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/fpdlink/Makefile
+@@ -0,0 +1,2 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_SOC_CAMERA_TI9X4) += ti9x4.o
+diff --git a/drivers/media/i2c/soc_camera/fpdlink/ti9x4.c b/drivers/media/i2c/soc_camera/fpdlink/ti9x4.c
+new file mode 100644
+index 0000000..340e61e
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/fpdlink/ti9x4.c
+@@ -0,0 +1,745 @@
++ /*
++ * 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/reboot.h>
++#include <linux/videodev2.h>
++
++#include <media/v4l2-common.h>
++#include <media/v4l2-clk.h>
++#include <media/v4l2-device.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 fs_time;
++ int fps_numerator;
++ int fps_denominator;
++ int is_coax;
++ int dvp_bus;
++ int dvp_lsb;
++ 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;
++ int vc_map;
++ int csi_map;
++ int gpio[4];
++ struct gpio_desc *pwen; /* chip power en */
++ struct gpio_desc *poc_gpio[4]; /* PoC power supply */
++ struct v4l2_clk *ref_clk; /* ref clock */
++ struct notifier_block reboot_notifier;
++};
++
++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 dvp_lsb = 0;
++module_param(dvp_lsb, int, 0644);
++MODULE_PARM_DESC(dvp_lsb, " DVP 8-bit LSB/MSB selection (default: DVP 8-bit MSB)");
++
++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)");
++
++static int vc_map = 0x3210;
++module_param(vc_map, int, 0644);
++MODULE_PARM_DESC(vc_map, " CSI VC MAP (default: 0xe4 - linear map VCx=LINKx)");
++
++static int csi_map = 0;
++module_param(csi_map, int, 0644);
++MODULE_PARM_DESC(csi_map, " CSI TX MAP (default: 0 - forwarding of all links to CSI0)");
++
++static int gpio0 = 0, gpio1 = 0, gpio2 = 0, gpio3 = 0;
++module_param(gpio0, int, 0644);
++MODULE_PARM_DESC(gpio0, " GPIO0 function select (default: GPIO0 low level)");
++module_param(gpio1, int, 0644);
++MODULE_PARM_DESC(gpio1, " GPIO1 function select (default: GPIO1 low level)");
++module_param(gpio2, int, 0644);
++MODULE_PARM_DESC(gpio2, " GPIO2 function select (default: GPIO2 low level)");
++module_param(gpio3, int, 0644);
++MODULE_PARM_DESC(gpio3, " GPIO3 function select (default: GPIO3 low level)");
++
++#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, 0x0d, 0xb9); /* VDDIO 3.3V */
++ switch (priv->csi_rate) {
++ case 1600: /* REFCLK = 25MHZ */
++ case 1500: /* REFCLK = 23MHZ */
++ case 1450: /* REFCLK = 22.5MHZ */
++ reg8_write(client, 0x1f, 0x00); /* CSI rate 1.5/1.6Gbps */
++ break;
++ case 1200: /* REFCLK = 25MHZ */
++ case 1100: /* REFCLK = 22.5MHZ */
++ reg8_write(client, 0x1f, 0x01); /* CSI rate 1.1/1.2Gbps */
++ break;
++ case 800: /* REFCLK = 25MHZ */
++ case 700: /* REFCLK = 22.5MHZ */
++ reg8_write(client, 0x1f, 0x02); /* CSI rate 700/800Mbps */
++ break;
++ case 400: /* REFCLK = 25MHZ */
++ case 350: /* REFCLK = 22.5MHZ */
++ reg8_write(client, 0x1f, 0x03); /* CSI rate 350/400Mbps */
++ break;
++ default:
++ dev_err(&client->dev, "unsupported CSI rate %d\n", priv->csi_rate);
++ }
++
++ switch (priv->csi_rate) {
++ case 1600:
++ case 1200:
++ case 800:
++ case 400:
++ /* FrameSync setup for REFCLK=25MHz, FPS=30: period_counts=1/FPS/12mks=1/30/12e-6=2777 -> HI=2, LO=2775 */
++ priv->fs_time = 2790;
++ break;
++ case 1500:
++ /* FrameSync setup for REFCLK=23MHz, FPS=30: period_counts=1/FPS/13.043mks=1/30/13.043e-6=2556 -> HI=2, LO=2554 */
++ priv->fs_time = 2570;
++ break;
++ case 1450:
++ case 1100:
++ case 700:
++ case 350:
++ /* FrameSync setup for REFCLK=22.5MHz, FPS=30: period_counts=1/FPS/13.333mks=1/30/13.333e-6=2500 -> HI=2, LO=2498 */
++ priv->fs_time = 2513;
++ break;
++ default:
++ priv->fs_time = 0;
++ dev_err(&client->dev, "unsupported CSI rate %d\n", priv->csi_rate);
++ }
++
++ if (strcmp(priv->forwarding_mode, "round-robin") == 0) {
++ reg8_write(client, 0x21, 0x03); /* Round Robin forwarding enable for CSI0/CSI1 */
++ } else if (strcmp(priv->forwarding_mode, "synchronized") == 0) {
++ reg8_write(client, 0x21, 0x54); /* Basic Syncronized forwarding enable (FrameSync must be enabled!!) for CSI0/CSI1 */
++ }
++
++ reg8_write(client, 0x32, 0x03); /* Select TX for CSI0/CSI1, RX for CSI0 */
++ 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 | priv->csi_map); /* 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
++ reg8_write(client, 0x19, 2 >> 8); /* FrameSync high time MSB */
++ reg8_write(client, 0x1a, 2 & 0xff); /* FrameSync high time LSB */
++ reg8_write(client, 0x1b, priv->fs_time >> 8); /* FrameSync low time MSB */
++ reg8_write(client, 0x1c, priv->fs_time & 0xff); /* FrameSync low time LSB */
++ reg8_write(client, 0x18, 0x00); /* Disable FrameSync - must be enabled after all cameras are set up */
++#endif
++}
++
++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, 0x30 << 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 |= (priv->dvp_lsb ? 0xC0 : 0x80); /* RAW10 as 8-bit prosessing using LSB/MSB 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, ((priv->vc_map >> (idx * 4)) << 6) | 0x1e); /* CSI data type: yuv422 8-bit, assign VC */
++ reg8_write(client, 0x71, ((priv->vc_map >> (idx * 4)) << 6) | 0x2c); /* CSI data type: RAW12, assign VC */
++ reg8_write(client, 0xbc, 0x00); /* Setup minimal time between FV and LV to 3 PCLKs */
++ reg8_write(client, 0x72, priv->vc_map >> (idx * 4)); /* CSI VC MAP */
++}
++
++static int ti9x4_initialize(struct i2c_client *client)
++{
++ struct ti9x4_priv *priv = i2c_get_clientdata(client);
++ int idx, timeout;
++ u8 port_sts1[4] = {0, 0, 0, 0}, port_sts2[4] = {0, 0, 0, 0};
++
++ 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;
++
++ /* check lock status */
++ for (timeout = 500 / priv->links; timeout > 0; timeout--) {
++ for (idx = 0; idx < priv->links; idx++) {
++ if ((port_sts1[idx] & 0x1) && (port_sts2[idx] & 0x4))
++ continue;
++
++ reg8_write(client, 0x4c, (idx << 4) | (1 << idx)); /* Select RX port number */
++ usleep_range(1000, 1500); /* wait 1ms */
++ reg8_read(client, 0x4d, &port_sts1[idx]); /* Lock status */
++ reg8_read(client, 0x4e, &port_sts2[idx]); /* Freq stable */
++ }
++ }
++
++ if (!timeout)
++ dev_info(&client->dev, "Receiver lock status [%d,%d,%d,%d]\n",
++ (port_sts1[0] & 0x1) && (port_sts2[0] & 0x4),
++ (port_sts1[1] & 0x1) && (port_sts2[1] & 0x4),
++ (port_sts1[2] & 0x1) && (port_sts2[2] & 0x4),
++ (port_sts1[3] & 0x1) && (port_sts2[3] & 0x4));
++
++ if (priv->poc_delay)
++ mdelay(100);
++
++ for (idx = 0; idx < priv->links; idx++) {
++ reg8_write(client, 0x4c, (idx << 4) | (1 << idx)); /* Select RX port number */
++ usleep_range(1000, 1500); /* wait 1ms */
++
++ /*
++ * Enable only FSIN for remote gpio, all permanent states (0 or 1) setup on serializer side:
++ * this avoids intermittent remote gpio noise (f.e. reset or spuriouse fsin) caused by
++ * unstable/bad link, hence unstable backchannel
++ */
++ client->addr = priv->ti9x3_addr_map[idx]; /* TI9X3 I2C addr */
++ switch (priv->ser_id) {
++ case TI913_ID:
++ reg8_write(client, 0x0d, 0x55); /* Enable remote GPIO0/1 */
++ reg8_write(client, 0x11, 0x10); /* I2C high pulse width */
++ reg8_write(client, 0x12, 0x10); /* I2C low pulse width */
++ break;
++ case TI953_ID:
++ reg8_write(client, 0x0d, (priv->gpio[0] & 0x1) << 0 |
++ (priv->gpio[1] & 0x1) << 1 |
++ (priv->gpio[2] & 0x1) << 2 |
++ (priv->gpio[3] & 0x1) << 3 |
++ (priv->gpio[0] & 0x2) << 3 |
++ (priv->gpio[1] & 0x2) << 4 |
++ (priv->gpio[2] & 0x2) << 5 |
++ (priv->gpio[3] & 0x2) << 6); /* Enable FSIN remote GPIOs and set local constant gpios */
++ reg8_write(client, 0x0e, (!!priv->gpio[0] << 4) |
++ (!!priv->gpio[1] << 5) |
++ (!!priv->gpio[2] << 6) |
++ (!!priv->gpio[3] << 7)); /* Enable serializer GPIOs only for output */
++ reg8_write(client, 0x0b, 0x10); /* I2C high pulse width */
++ reg8_write(client, 0x0c, 0x10); /* I2C low pulse width */
++ break;
++ }
++ client->addr = priv->des_addr;
++
++ reg8_write(client, 0x6e, 0x88 | (priv->gpio[1] << 4) | priv->gpio[0]); /* Remote GPIO1/GPIO0 setup */
++ reg8_write(client, 0x6f, 0x88 | (priv->gpio[3] << 4) | priv->gpio[2]); /* Remote GPIO3/GPIO2 setup */
++ }
++
++ 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 | priv->csi_map); /* enable port forwarding to CSI */
++ } else {
++ if (atomic_dec_return(&priv->use_count) == 0)
++ reg8_write(client, 0x20, 0xf0 | priv->csi_map); /* 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 */
++ reg8_write(client, 0x18, 0x01); /* Enable FrameSync, HI/LO mode, Frame clock from port0 */
++// reg8_write(client, 0x18, 0x80); /* Enable FrameSync, Frame clock is external */
++
++ return 0;
++}
++
++static int ti9x4_reboot_notifier(struct notifier_block *nb, unsigned long event, void *buf)
++{
++ struct ti9x4_priv *priv = container_of(nb, struct ti9x4_priv, reboot_notifier);
++ int idx;
++
++ for (idx = 0; idx < priv->links; idx++) {
++ if (!IS_ERR(priv->poc_gpio[idx]))
++ gpiod_direction_output(priv->poc_gpio[idx], 0); /* POC power off */
++ }
++
++ return NOTIFY_OK;
++}
++
++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 int ti9x4_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
++{
++ return 0;
++}
++
++static int ti9x4_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
++{
++ struct ti9x4_priv *priv = v4l2_get_subdevdata(sd);
++ struct i2c_client *client = priv->client;
++ struct v4l2_captureparm *cp = &parms->parm.capture;
++
++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return -EINVAL;
++ if (cp->extendedmode != 0)
++ return -EINVAL;
++
++ if (priv->fps_denominator != cp->timeperframe.denominator ||
++ priv->fps_numerator != cp->timeperframe.numerator) {
++ int f_time;
++
++ f_time = priv->fs_time * 30 * cp->timeperframe.numerator / cp->timeperframe.denominator;
++ reg8_write(client, 0x1b, f_time >> 8); /* FrameSync low time MSB */
++ reg8_write(client, 0x1c, f_time & 0xff); /* FrameSync low time LSB */
++
++ priv->fps_denominator = cp->timeperframe.denominator;
++ priv->fps_numerator = cp->timeperframe.numerator;
++ }
++
++ return 0;
++}
++
++static struct v4l2_subdev_video_ops ti9x4_video_ops = {
++ .g_parm = ti9x4_g_parm,
++ .s_parm = ti9x4_s_parm,
++};
++
++static struct v4l2_subdev_ops ti9x4_subdev_ops = {
++ .core = &ti9x4_subdev_core_ops,
++ .video = &ti9x4_video_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 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 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;
++
++ priv->ref_clk = v4l2_clk_get(&client->dev, "ref_clk");
++ if (!IS_ERR(priv->ref_clk)) {
++ dev_info(&client->dev, "ref_clk = %luKHz", v4l2_clk_get_rate(priv->ref_clk) / 1000);
++ v4l2_clk_enable(priv->ref_clk);
++ }
++
++ priv->pwen = devm_gpiod_get(&client->dev, NULL, GPIOF_OUT_INIT_HIGH);
++ if (!IS_ERR(priv->pwen)) {
++ mdelay(5);
++ gpiod_direction_output(priv->pwen, 0);
++ mdelay(5);
++ }
++
++ for (i = 0; i < 4; i++) {
++ sprintf(name, "POC%d", i);
++ priv->poc_gpio[i] = devm_gpiod_get_optional(&client->dev, kstrdup(name, GFP_KERNEL), 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_bool(np, "ti,dvp_lsb"))
++ priv->dvp_lsb = 1;
++ else
++ priv->dvp_lsb = 0;
++ if (of_property_read_u32(np, "ti,hsync", &priv->hsync))
++ priv->hsync = 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 = 10;
++ if (of_property_read_u32(np, "ti,vc-map", &priv->vc_map))
++ priv->vc_map = 0x3210;
++ for (i = 0; i < 4; i++) {
++ sprintf(name, "ti,gpio%d", i);
++ if (of_property_read_u32(np, name, &priv->gpio[i]))
++ priv->gpio[i] = 0;
++ }
++
++ /*
++ * CSI forwarding of all links is to CSI0 by default.
++ * Decide if any link will be forwarded to CSI1 instead CSI0
++ */
++ prop = of_find_property(np, "ti,csi1-links", NULL);
++ if (prop) {
++ const __be32 *link = NULL;
++ u32 v;
++
++ for (i = 0; i < 4; i++) {
++ link = of_prop_next_u32(prop, link, &v);
++ if (!link)
++ break;
++ priv->csi_map |= BIT(v);
++ }
++ } else {
++ priv->csi_map = 0;
++ }
++
++ /* module params override dts */
++ if (is_stp)
++ priv->is_coax = 0;
++ if (dvp_bus != 8)
++ priv->dvp_bus = dvp_bus;
++ if (dvp_lsb)
++ priv->dvp_lsb = dvp_lsb;
++ 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;
++ if (vc_map != 0x3210)
++ priv->vc_map = vc_map;
++ if (csi_map)
++ priv->csi_map = csi_map;
++ if (gpio0)
++ priv->gpio[0] = gpio0;
++ if (gpio1)
++ priv->gpio[1] = gpio1;
++ if (gpio2)
++ priv->gpio[2] = gpio2;
++ if (gpio3)
++ priv->gpio[3] = gpio3;
++
++ for (i = 0; ; i++) {
++ endpoint = of_graph_get_next_endpoint(np, endpoint);
++ if (!endpoint)
++ break;
++
++ if (i < priv->links) {
++ if (of_property_read_u32(endpoint, "ti9x3-addr", &priv->ti9x3_addr_map[i])) {
++ of_node_put(endpoint);
++ 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);
++ }
++
++ of_node_put(endpoint);
++ 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);
++ priv->fps_numerator = 1;
++ priv->fps_denominator = 30;
++
++ 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;
++ }
++
++ priv->reboot_notifier.notifier_call = ti9x4_reboot_notifier;
++ err = register_reboot_notifier(&priv->reboot_notifier);
++ if (err)
++ dev_err(&client->dev, "failed to register reboot notifier\n");
++
++out:
++ return err;
++}
++
++static int ti9x4_remove(struct i2c_client *client)
++{
++ struct ti9x4_priv *priv = i2c_get_clientdata(client);
++ int i;
++
++ unregister_reboot_notifier(&priv->reboot_notifier);
++
++ 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/fpdlink/ti9x4.h b/drivers/media/i2c/soc_camera/fpdlink/ti9x4.h
+new file mode 100644
+index 0000000..6825f8a
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/fpdlink/ti9x4.h
+@@ -0,0 +1,204 @@
++/*
++ * 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_read_n(struct i2c_client *client, u16 reg, u8 *val, int n)
++{
++ 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, val, n);
++ if (ret == n)
++ break;
++ }
++ }
++
++ if (ret < 0) {
++ dev_dbg(&client->dev,
++ "read fail: chip 0x%x registers 0x%x-0x%x: %d\n",
++ client->addr, reg, reg + n, ret);
++ }
++
++ return ret < 0 ? ret : 0;
++}
++
++static inline int reg16_write_n(struct i2c_client *client, u16 reg, const u8* val, int n)
++{
++ int ret, retries;
++ u8 buf[2 + n];
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++ memcpy(&buf[2], val, n);
++
++ for (retries = MAXIM_NUM_RETRIES; retries; retries--) {
++ ret = i2c_master_send(client, buf, 2 + n);
++ if (ret == 2 + n)
++ break;
++ }
++
++ if (ret < 0) {
++ dev_dbg(&client->dev,
++ "write fail: chip 0x%x register 0x%x-0x%x: %d\n",
++ client->addr, reg, reg + n, 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/i2c/soc_camera/gmsl/Kconfig b/drivers/media/i2c/soc_camera/gmsl/Kconfig
+new file mode 100644
+index 0000000..c7a33bf
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/gmsl/Kconfig
+@@ -0,0 +1,11 @@
++config SOC_CAMERA_MAX9286
++ tristate "max9286 GMSL support"
++ depends on I2C
++ help
++ This is a MAXIM max9286 GMSL driver
++
++config SOC_CAMERA_MAX9288
++ tristate "max9288 GMSL support"
++ depends on I2C
++ help
++ This is a MAXIM max9288 GMSL driver
+diff --git a/drivers/media/i2c/soc_camera/gmsl/Makefile b/drivers/media/i2c/soc_camera/gmsl/Makefile
+new file mode 100644
+index 0000000..9925314
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/gmsl/Makefile
+@@ -0,0 +1,3 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_SOC_CAMERA_MAX9286) += max9286.o
++obj-$(CONFIG_SOC_CAMERA_MAX9288) += max9288.o
+diff --git a/drivers/media/i2c/soc_camera/gmsl/max9286.c b/drivers/media/i2c/soc_camera/gmsl/max9286.c
+new file mode 100644
+index 0000000..2d56b41
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/gmsl/max9286.c
+@@ -0,0 +1,1072 @@
++/*
++ * 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/reboot.h>
++#include <linux/videodev2.h>
++
++#include <media/v4l2-common.h>
++#include <media/v4l2-device.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;
++ int fps_numerator;
++ int fps_denominator;
++ int pclk;
++ char pclk_rising_edge;
++ int gpio_resetb;
++ int active_low_resetb;
++ int him;
++ int hsync;
++ int vsync;
++ int timeout;
++ int poc_delay;
++ int bws;
++ int dbl;
++ int dt;
++ int hsgen;
++ u64 crossbar;
++ char cb[16];
++ int hts;
++ int vts;
++ int hts_delay;
++ int imager_width;
++ atomic_t use_count;
++ u32 csi2_outord;
++ u32 switchin;
++ struct i2c_client *client;
++ int max9271_addr_map[4];
++ int ser_id;
++ struct gpio_desc *poc_gpio[4]; /* PoC power supply */
++ struct notifier_block reboot_notifier;
++
++ /* link statistic */
++ int prbserr[4];
++ int deterr[4];
++ int correrr[4];
++};
++
++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 timeout_n = 100;
++module_param(timeout_n, int, 0644);
++MODULE_PARM_DESC(timeout_n, " Timeout of link detection (default: 100 retries)");
++
++static int poc_delay = 50;
++module_param(poc_delay, int, 0644);
++MODULE_PARM_DESC(poc_delay, " Delay in ms after POC enable (default: 50 ms)");
++
++static int bws;
++module_param(bws, int, 0644);
++MODULE_PARM_DESC(bws, " BWS mode (default: 0 - 24-bit gmsl packets)");
++
++static int dbl = 1;
++module_param(dbl, int, 0644);
++MODULE_PARM_DESC(dbl, " DBL mode (default: 1 - DBL mode enabled)");
++
++static int dt = 3;
++module_param(dt, int, 0644);
++MODULE_PARM_DESC(dt, " DataType (default: 3 - YUV8), 0 - RGB888, 5 - RAW8, 6 - RAW10, 7 - RAW12, 8 - RAW14");
++
++static int hsgen;
++module_param(hsgen, int, 0644);
++MODULE_PARM_DESC(hsgen, " Enable HS embedded generator (default: 0 - disabled)");
++
++static int pclk = 100;
++module_param(pclk, int, 0644);
++MODULE_PARM_DESC(pclk, " PCLK rate (default: 100MHz)");
++
++static int switchin = 0;
++module_param(switchin, int, 0644);
++MODULE_PARM_DESC(switchin, " COAX SWITCH IN+ and IN- (default: 0 - not switched)");
++
++static unsigned long crossbar = 0xba9876543210;
++module_param(crossbar, ulong, 0644);
++MODULE_PARM_DESC(crossbar, " Crossbar setup (default: ba9876543210 - reversed)");
++
++enum {
++ RGB888_DT = 0,
++ RGB565_DT,
++ RGB666_DT,
++ YUV8_DT, /* default */
++ YUV10_DT,
++ RAW8_DT,
++ RAW10_DT,
++ RAW12_DT,
++ RAW14_DT,
++};
++
++static int dt2bpp [9] = {
++ 24, /* RGB888 */
++ 16, /* RGB565 */
++ 18, /* RGB666 */
++ 8, /* YUV8 - default */
++ 10, /* YUV10 */
++ 8, /* RAW8/RAW16 */
++ 10, /* RAW10 */
++ 12, /* RAW12 */
++ 14, /* RAW14 */
++};
++
++static char* ser_name(int id)
++{
++ switch (id) {
++ case MAX9271_ID:
++ return "MAX9271";
++ case MAX96705_ID:
++ return "MAX96705";
++ case MAX96707_ID:
++ return "MAX96707";
++ default:
++ return "unknown";
++ }
++}
++
++static void max9286_write_remote_verify(struct i2c_client *client, int idx, u8 reg, u8 val)
++{
++ struct max9286_priv *priv = i2c_get_clientdata(client);
++ int timeout;
++
++ for (timeout = 0; timeout < 10; timeout++) {
++ int tmp_addr;
++ u8 sts = 0;
++ u8 val2 = 0;
++
++ reg8_write(client, reg, val);
++
++ tmp_addr = client->addr;
++ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */
++ reg8_read(client, 0x70, &sts);
++ client->addr = tmp_addr;
++ if (sts & BIT(idx)) /* if ACKed */ {
++ reg8_read(client, reg, &val2);
++ if (val2 == val)
++ break;
++ }
++
++ usleep_range(1000, 1500);
++ }
++
++ if (timeout >= 10)
++ dev_err(&client->dev, "timeout remote write acked\n");
++}
++
++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, 0x1b, priv->switchin); /* coax polarity (default - normal) */
++ reg8_write(client, 0x1c, (priv->him ? 0xf0 : 0x00) |
++ (priv->bws ? 0x05 : 0x04)); /* high-immunity/legacy mode, BWS 24bit */
++}
++
++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;
++
++ if (priv->active_low_resetb)
++ reset_on = !reset_on;
++
++ /* sensor reset/unreset using serializer gpio */
++ client->addr = addr;
++ reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | (reset_on ? BIT(priv->gpio_resetb) : 0)); /* set GPIOn value */
++ 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++) {
++ if (priv->ser_id == MAX96705_ID || priv->ser_id == MAX96707_ID)
++ continue;
++
++ client->addr = priv->des_addr; /* MAX9286 I2C */
++ reg8_write(client, 0x00, 0xe0 | BIT(idx)); /* enable GMSL link for CAMx */
++ reg8_write(client, 0x0a, 0x11 << idx); /* enable reverse/forward control for CAMx */
++ usleep_range(5000, 5500); /* wait 2ms after any change of reverse channel settings */
++
++ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx I2C */
++ max9286_sensor_reset(client, client->addr, 0); /* sensor unreset using gpios. TODO: should be in imager driver */
++ }
++
++ 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->switchin | priv->links_mask); /* coax polarity, enable equalizer for CAMs */
++ reg8_write(client, 0x34, 0x22 | MAXIM_I2C_I2C_SPEED); /* disable artificial ACK, I2C speed set */
++ 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, lock_sts = 0, link_sts = 0;
++ int timeout = priv->timeout;
++ char timeout_str[40];
++ int ret = 0;
++
++ /* Reverse channel enable */
++ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */
++ 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 (;;) {
++ if (priv->him) {
++ /* HIM mode setup */
++ client->addr = 0x40; /* MAX9271-CAMx I2C */
++ reg8_write(client, 0x4d, 0xc0);
++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
++ 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 */
++ if (priv->bws) {
++ reg8_write(client, 0x07, 0x04 | (priv->pclk_rising_edge ? 0 : 0x10) |
++ (priv->dbl ? 0x80 : 0) |
++ (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS encoding enabled, DBL mode, BWS 24/32-bit */
++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
++ }
++ } else {
++ /* Legacy mode setup */
++ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */
++ reg8_write(client, 0x3f, 0x4f); /* enable custom reverse channel & first pulse length */
++ 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, 0x5f); /* enable reverse control channel programming (MAX96705-MAX96711 only) */
++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
++ if (priv->bws) {
++ reg8_write(client, 0x07, 0x04 | (priv->pclk_rising_edge ? 0 : 0x10) |
++ (priv->dbl ? 0x80 : 0) |
++ (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS encoding enabled, DBL mode, BWS 24/32-bit */
++ 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 || val == MAX96707_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 || val == MAX96707_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 (poc_trig) {
++ if (!IS_ERR(priv->poc_gpio[idx]) && (timeout % poc_trig == 0)) {
++ 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 */
++
++ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */
++ reg8_read(client, 0x27, &lock_sts); /* LOCK status */
++ reg8_read(client, 0x49, &link_sts); /* LINK status */
++
++ 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 lock_sts=%d link_sts=0x%x", priv->timeout - timeout, !!(lock_sts & 0x80), link_sts & (0x11 << idx));
++ 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 */
++ reg8_write(client, 0x12, ((priv->lanes - 1) << 6) |
++ (priv->dbl ? 0x30 : 0) |
++ (priv->dt & 0xf)); /* setup lanes, DBL mode, DataType */
++
++ /* 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, 0x04 | (priv->pclk_rising_edge ? 0 : 0x10) |
++ (priv->dbl ? 0x80 : 0) |
++ (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS encoding enabled, DBL mode, BWS 24/32-bit */
++ 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 || priv->ser_id == MAX96707_ID) {
++ switch (priv->dt) {
++ case YUV8_DT:
++ /* setup crossbar for YUV8/RAW8: reverse DVP bus */
++ reg8_write(client, 0x20, priv->cb[7]);
++ reg8_write(client, 0x21, priv->cb[6]);
++ reg8_write(client, 0x22, priv->cb[5]);
++ reg8_write(client, 0x23, priv->cb[4]);
++ reg8_write(client, 0x24, priv->cb[3]);
++ reg8_write(client, 0x25, priv->cb[2]);
++ reg8_write(client, 0x26, priv->cb[1]);
++ reg8_write(client, 0x27, priv->cb[0]);
++
++ /* this is second byte if DBL=1 */
++ reg8_write(client, 0x30, priv->cb[7] + 16);
++ reg8_write(client, 0x31, priv->cb[6] + 16);
++ reg8_write(client, 0x32, priv->cb[5] + 16);
++ reg8_write(client, 0x33, priv->cb[4] + 16);
++ reg8_write(client, 0x34, priv->cb[3] + 16);
++ reg8_write(client, 0x35, priv->cb[2] + 16);
++ reg8_write(client, 0x36, priv->cb[1] + 16);
++ reg8_write(client, 0x37, priv->cb[0] + 16);
++ break;
++ case RAW12_DT:
++ /* setup crossbar for RAW12: reverse DVP bus */
++ reg8_write(client, 0x20, priv->cb[11]);
++ reg8_write(client, 0x21, priv->cb[10]);
++ reg8_write(client, 0x22, priv->cb[9]);
++ reg8_write(client, 0x23, priv->cb[8]);
++ reg8_write(client, 0x24, priv->cb[7]);
++ reg8_write(client, 0x25, priv->cb[6]);
++ reg8_write(client, 0x26, priv->cb[5]);
++ reg8_write(client, 0x27, priv->cb[4]);
++ reg8_write(client, 0x28, priv->cb[3]);
++ reg8_write(client, 0x29, priv->cb[2]);
++ reg8_write(client, 0x2a, priv->cb[1]);
++ reg8_write(client, 0x2b, priv->cb[0]);
++
++ /* this is second byte if DBL=1 */
++ reg8_write(client, 0x30, priv->cb[11] + 16);
++ reg8_write(client, 0x31, priv->cb[10] + 16);
++ reg8_write(client, 0x32, priv->cb[9] + 16);
++ reg8_write(client, 0x33, priv->cb[8] + 16);
++ reg8_write(client, 0x34, priv->cb[7] + 16);
++ reg8_write(client, 0x35, priv->cb[6] + 16);
++ reg8_write(client, 0x36, priv->cb[5] + 16);
++ reg8_write(client, 0x37, priv->cb[4] + 16);
++ reg8_write(client, 0x38, priv->cb[3] + 16);
++ reg8_write(client, 0x39, priv->cb[2] + 16);
++ reg8_write(client, 0x3a, priv->cb[1] + 16);
++ reg8_write(client, 0x3b, priv->cb[0] + 16);
++
++ if (!priv->bws && priv->dbl)
++ dev_err(&client->dev, " BWS must be 27/32-bit for RAW12 in DBL mode\n");
++
++ break;
++ }
++
++ if (priv->hsgen) {
++ /* HS/VS pins map */
++ reg8_write(client, 0x3f, 0x10); /* HS (NC) */
++ reg8_write(client, 0x41, 0x10); /* DE (NC) */
++ if (priv->ser_id == MAX96705_ID)
++ reg8_write(client, 0x40, 15); /* VS (DIN13) */
++ if (priv->ser_id == MAX96707_ID)
++ reg8_write(client, 0x40, 13); /* VS (DIN13) */
++#if 0
++ /* following must come from imager */
++#define SENSOR_WIDTH (1280*2)
++#define HTS (1288*2)
++#define VTS 960
++#define HTS_DELAY 0x9
++ reg8_write(client, 0x4e, HTS_DELAY >> 16); /* HS delay */
++ reg8_write(client, 0x4f, (HTS_DELAY >> 8) & 0xff);
++ reg8_write(client, 0x50, HTS_DELAY & 0xff);
++ reg8_write(client, 0x54, SENSOR_WIDTH >> 8); /* HS high period */
++ reg8_write(client, 0x55, SENSOR_WIDTH & 0xff);
++ reg8_write(client, 0x56, (HTS - SENSOR_WIDTH) >> 8); /* HS low period */
++ reg8_write(client, 0x57, (HTS - SENSOR_WIDTH) & 0xff);
++ reg8_write(client, 0x58, VTS >> 8); /* HS count */
++ reg8_write(client, 0x59, VTS & 0xff );
++#endif
++ reg8_write(client, 0x43, 0x15); /* enable HS generator */
++ }
++ }
++
++ 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 */
++ max9286_write_remote_verify(client, idx, 0x04, conf_link ? 0x43 : 0x83);
++// 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 int max9286_reboot_notifier(struct notifier_block *nb, unsigned long event, void *buf)
++{
++ struct max9286_priv *priv = container_of(nb, struct max9286_priv, reboot_notifier);
++ int idx;
++
++ for (idx = 0; idx < priv->links; idx++) {
++ if (!IS_ERR(priv->poc_gpio[idx]))
++ gpiod_direction_output(priv->poc_gpio[idx], 0); /* POC power off */
++ }
++
++ return NOTIFY_OK;
++}
++
++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 int max9286_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
++{
++ return 0;
++}
++
++static int max9286_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
++{
++ struct max9286_priv *priv = v4l2_get_subdevdata(sd);
++ struct i2c_client *client = priv->client;
++ struct v4l2_captureparm *cp = &parms->parm.capture;
++
++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return -EINVAL;
++ if (cp->extendedmode != 0)
++ return -EINVAL;
++
++ if (priv->fps_denominator != cp->timeperframe.denominator ||
++ priv->fps_numerator != cp->timeperframe.numerator) {
++ int f_period;
++
++ f_period = priv->fsync_period * 30 * cp->timeperframe.numerator / cp->timeperframe.denominator;
++ reg8_write(client, 0x06, f_period & 0xff);
++ reg8_write(client, 0x07, (f_period >> 8) & 0xff);
++ reg8_write(client, 0x08, f_period >> 16);
++
++ priv->fps_numerator = cp->timeperframe.numerator;
++ priv->fps_denominator = cp->timeperframe.denominator;
++ }
++
++ return 0;
++}
++
++static struct v4l2_subdev_video_ops max9286_video_ops = {
++ .g_parm = max9286_g_parm,
++ .s_parm = max9286_s_parm,
++};
++
++static struct v4l2_subdev_ops max9286_subdev_ops = {
++ .core = &max9286_subdev_core_ops,
++ .video = &max9286_video_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, kstrdup(poc_name, GFP_KERNEL), 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;
++ if (of_property_read_u32(np, "maxim,bws", &priv->bws))
++ priv->bws = 0;
++ if (of_property_read_u32(np, "maxim,dbl", &priv->dbl))
++ priv->dbl = 1;
++ if (of_property_read_u32(np, "maxim,dt", &priv->dt))
++ priv->dt = 3;
++ if (of_property_read_u32(np, "maxim,hsgen", &priv->hsgen))
++ priv->hsgen = 0;
++ if (of_property_read_u32(np, "maxim,pclk", &priv->pclk))
++ priv->pclk = pclk;
++ if (of_property_read_u32(np, "maxim,switchin", &priv->switchin))
++ priv->switchin = 0;
++ if (of_property_read_u64(np, "maxim,crossbar", &priv->crossbar))
++ priv->crossbar = crossbar;
++
++ /* 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 (timeout_n)
++ priv->timeout = timeout_n;
++ if (poc_delay)
++ priv->poc_delay = poc_delay;
++ if (bws)
++ priv->bws = bws;
++ if (!dbl)
++ priv->dbl = dbl;
++ if (dt != 3)
++ priv->dt = dt;
++ if (hsgen)
++ priv->hsgen = hsgen;
++ if (pclk != 100)
++ priv->pclk = pclk;
++ if (switchin)
++ priv->switchin = switchin;
++
++ /* parse crossbar setup */
++ for (i = 0; i < 16; i++) {
++ priv->cb[i] = priv->crossbar % 16;
++ priv->crossbar /= 16;
++ }
++
++ for (i = 0; i < priv->links; i++) {
++ endpoint = of_graph_get_next_endpoint(np, endpoint);
++ if (!endpoint)
++ break;
++
++ if (of_property_read_u32(endpoint, "max9271-addr", &priv->max9271_addr_map[i])) {
++ of_node_put(endpoint);
++ dev_err(&client->dev, "max9271-addr not set\n");
++ return -EINVAL;
++ }
++
++ priv->sd_fwnode[i] = of_fwnode_handle(endpoint);
++ }
++
++ of_node_put(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;
++
++ 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*bpp*links/lanes */
++ priv->csi_rate = cpu_to_be32(priv->pclk * dt2bpp[priv->dt] * 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);
++ }
++
++ of_node_put(endpoint);
++}
++
++static const char *line_status[] =
++{
++ "BAT",
++ "GND",
++ "NORMAL",
++ "OPEN"
++};
++
++static ssize_t max9286_link_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ int i = -1;
++ u8 val = 0;
++ bool lenghterr, linebuffof, hlocked, prbsok, vsyncdet, configdet, videodet;
++ int lf;
++ u8 prbserr = 0, deterr = 0, correrr = 0;
++ struct i2c_client *client = to_i2c_client(dev);
++ struct max9286_priv *priv = i2c_get_clientdata(client);
++
++ if (!sscanf(attr->attr.name, "link_%d", &i))
++ return -EINVAL;
++
++ if ((i < 0) || (i > 3))
++ return -EINVAL;
++
++ reg8_read(client, 0x20, &val);
++ lf = (val >> (2 * i)) & 0x03;
++
++ reg8_read(client, 0x21, &val);
++ hlocked = !!(val & (1 << i));
++ prbsok = !!(val & (1 << (i + 4)));
++
++ reg8_read(client, 0x22, &val);
++ lenghterr = !!(val & (1 << i));
++ linebuffof = !!(val & (1 << (i + 4)));
++
++ reg8_read(client, 0x23 + i, &prbserr);
++ priv->prbserr[i] += prbserr;
++
++ reg8_read(client, 0x27, &val);
++ vsyncdet = !!(val & (1 << i));
++
++ reg8_read(client, 0x28 + i, &deterr);
++ priv->deterr[i] += deterr;
++
++ reg8_read(client, 0x2c + i, &correrr);
++ priv->correrr[i] += correrr;
++
++ reg8_read(client, 0x49, &val);
++ configdet = !!(val & (1 << i));
++ videodet = !!(val & (1 << (i + 4)));
++
++ return sprintf(buf, "LINK:%d LF:%s HLOCKED:%d PRBSOK:%d LINBUFFOF:%d"
++ " LENGHTERR:%d VSYNCDET:%d CONFIGDET:%d VIDEODET:%d"
++ " PRBSERR:%d(%d) DETEERR:%d(%d) CORRERR:%d(%d)\n",
++ i, line_status[lf], hlocked, prbsok, lenghterr,
++ linebuffof, vsyncdet, configdet, videodet,
++ priv->prbserr[i], prbserr,
++ priv->deterr[i], deterr,
++ priv->correrr[i], correrr);
++ return 0;
++}
++
++static DEVICE_ATTR(link_0, S_IRUGO, max9286_link_show, NULL);
++static DEVICE_ATTR(link_1, S_IRUGO, max9286_link_show, NULL);
++static DEVICE_ATTR(link_2, S_IRUGO, max9286_link_show, NULL);
++static DEVICE_ATTR(link_3, S_IRUGO, max9286_link_show, NULL);
++
++static struct attribute *max9286_attributes_links[] = {
++ &dev_attr_link_0.attr,
++ &dev_attr_link_1.attr,
++ &dev_attr_link_2.attr,
++ &dev_attr_link_3.attr,
++ NULL
++};
++
++static const struct attribute_group max9286_group = {
++ .attrs = max9286_attributes_links,
++};
++
++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;
++ priv->fps_numerator = 1;
++ priv->fps_denominator = 30;
++
++ 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;
++ }
++
++ priv->reboot_notifier.notifier_call = max9286_reboot_notifier;
++ err = register_reboot_notifier(&priv->reboot_notifier);
++ if (err) {
++ dev_err(&client->dev, "failed to register reboot notifier\n");
++ goto out;
++ }
++
++ err = sysfs_create_group(&client->dev.kobj,
++ &max9286_group);
++ if (err < 0)
++ dev_err(&client->dev, "Sysfs registration failed\n");
++out:
++ return err;
++}
++
++static int max9286_remove(struct i2c_client *client)
++{
++ struct max9286_priv *priv = i2c_get_clientdata(client);
++ int i;
++
++ sysfs_remove_group(&client->dev.kobj, &max9286_group);
++ unregister_reboot_notifier(&priv->reboot_notifier);
++
++ 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/gmsl/max9288.c b/drivers/media/i2c/soc_camera/gmsl/max9288.c
+new file mode 100644
+index 0000000..7de1f9e
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/gmsl/max9288.c
+@@ -0,0 +1,768 @@
++/*
++ * MAXIM max9288 GMSL driver
++ *
++ * Copyright (C) 2019 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-common.h>
++#include <media/v4l2-device.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 max9288_priv {
++ struct v4l2_subdev sd;
++ struct fwnode_handle *sd_fwnode;
++ int des_addr;
++ int lanes;
++ int csi_rate;
++ int pclk;
++ char pclk_rising_edge;
++ int gpio_resetb;
++ int active_low_resetb;
++ int him;
++ int hsync;
++ int vsync;
++ int timeout;
++ int poc_delay;
++ int bws;
++ int dbl;
++ int dt;
++ int hsgen;
++ int hts;
++ int vts;
++ int hts_delay;
++ struct i2c_client *client;
++ int max9271_addr;
++ int ser_id;
++ struct gpio_desc *poc_gpio; /* PoC power supply */
++};
++
++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 = 0;
++module_param(him, int, 0644);
++MODULE_PARM_DESC(him, " Use High-Immunity mode (default: leagacy mode)");
++
++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 timeout_n = 100;
++module_param(timeout_n, int, 0644);
++MODULE_PARM_DESC(timeout_n, " Timeout of link detection (default: 100 retries)");
++
++static int poc_delay = 50;
++module_param(poc_delay, int, 0644);
++MODULE_PARM_DESC(poc_delay, " Delay in ms after POC enable (default: 50 ms)");
++
++static int bws = 0;
++module_param(bws, int, 0644);
++MODULE_PARM_DESC(bws, " BWS mode (default: 0 - 24-bit gmsl packets)");
++
++static int dbl = 1;
++module_param(dbl, int, 0644);
++MODULE_PARM_DESC(dbl, " DBL mode (default: 1 - DBL mode enabled)");
++
++static int dt = 3;
++module_param(dt, int, 0644);
++MODULE_PARM_DESC(dt, " DataType (default: 3 - YUV8), 0 - RGB888, 5 - RAW8, 6 - RAW10, 7 - RAW12, 8 - RAW14");
++
++static int hsgen;
++module_param(hsgen, int, 0644);
++MODULE_PARM_DESC(hsgen, " Enable HS embedded generator (default: 0 - disabled)");
++
++static int pclk = 100;
++module_param(pclk, int, 0644);
++MODULE_PARM_DESC(pclk, " PCLK rate (default: 100MHz)");
++
++enum {
++ RGB888_DT = 0,
++ RGB565_DT,
++ RGB666_DT,
++ YUV8_DT, /* default */
++ YUV10_DT,
++ RAW8_DT,
++ RAW10_DT,
++ RAW12_DT,
++ RAW14_DT,
++};
++
++static int dt2bpp [9] = {
++ 24, /* RGB888 */
++ 16, /* RGB565 */
++ 18, /* RGB666 */
++ 8, /* YUV8 - default */
++ 10, /* YUV10 */
++ 8, /* RAW8/RAW16 */
++ 10, /* RAW10 */
++ 12, /* RAW12 */
++ 14, /* RAW14 */
++};
++
++static char* ser_name(int id)
++{
++ switch (id) {
++ case MAX9271_ID:
++ return "MAX9271";
++ case MAX96705_ID:
++ return "MAX96705";
++ case MAX96707_ID:
++ return "MAX96707";
++ default:
++ return "unknown";
++ }
++}
++
++static void max9288_write_remote_verify(struct i2c_client *client, u8 reg, u8 val)
++{
++ struct max9288_priv *priv = i2c_get_clientdata(client);
++ int timeout;
++
++ for (timeout = 0; timeout < 10; timeout++) {
++ u8 val2 = 0;
++
++ reg8_write(client, reg, val);
++ reg8_read(client, reg, &val2);
++ if (val2 == val)
++ break;
++
++ usleep_range(1000, 1500);
++ }
++
++ if (timeout >= 10)
++ dev_err(&client->dev, "timeout remote write acked\n");
++}
++
++
++static void max9288_preinit(struct i2c_client *client, int addr)
++{
++
++ struct max9288_priv *priv = i2c_get_clientdata(client);
++
++ client->addr = addr; /* MAX9288-CAMx I2C */
++ reg8_write(client, 0x04, 0x00); /* disable reverse control */
++ reg8_write(client, 0x16, (priv->him ? 0x80 : 0x00) |
++ 0x5a); /* high-immunity/legacy mode */
++}
++
++static void max9288_sensor_reset(struct i2c_client *client, int addr, int reset_on)
++{
++ struct max9288_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 int max9288_reverse_channel_setup(struct i2c_client *client)
++{
++ struct max9288_priv *priv = i2c_get_clientdata(client);
++ u8 val = 0, lock_sts = 0;
++ int timeout = priv->timeout;
++ char timeout_str[40];
++ int ret = 0;
++
++ /* Reverse channel enable */
++ client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
++ reg8_write(client, 0x1c, 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, 0x04, 0x03); /* enable reverse control */
++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
++
++ for (;;) {
++ if (priv->him) {
++ /* HIM mode setup */
++ client->addr = 0x40; /* MAX9271-CAMx I2C */
++ reg8_write(client, 0x4d, 0xc0);
++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
++ 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 */
++ if (priv->bws) {
++ reg8_write(client, 0x07, (priv->pclk_rising_edge ? 0 : 0x10) |
++ (priv->dbl ? 0x80 : 0) |
++ (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS disabled enabled, DBL mode, BWS 24/32-bit */
++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
++ }
++ } else {
++ /* Legacy mode setup */
++ client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
++ reg8_write(client, 0x13, 0x00);
++ reg8_write(client, 0x11, 0x42); /* enable custom reverse channel & first pulse length */
++ reg8_write(client, 0x0a, 0x0f); /* 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 */
++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
++ if (priv->bws) {
++ reg8_write(client, 0x07, (priv->pclk_rising_edge ? 0 : 0x10) |
++ (priv->dbl ? 0x80 : 0) |
++ (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS encoding disabled, DBL mode, BWS 24/32-bit */
++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
++ }
++ reg8_write(client, 0x97, 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; /* MAX9288-CAMx I2C */
++ reg8_write(client, 0x0a, 0x0c); /* first pulse length rise time changed from 300ns to 200ns, amplitude 100mV */
++ reg8_write(client, 0x13, 0x20); /* 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 || val == MAX96707_ID || --timeout == 0) {
++ priv->ser_id = val;
++ break;
++ }
++
++ /* Check if already initialized (after reboot/reset ?) */
++ client->addr = priv->max9271_addr; /* MAX9271-CAMx I2C */
++ reg8_read(client, 0x1e, &val); /* read max9271 ID */
++ if (val == MAX9271_ID || val == MAX96705_ID || val == MAX96707_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 (poc_trig) {
++ if (!IS_ERR(priv->poc_gpio) && (timeout % poc_trig == 0)) {
++ gpiod_direction_output(priv->poc_gpio, 0); /* POC power off */
++ mdelay(200);
++ gpiod_direction_output(priv->poc_gpio, 1); /* POC power on */
++ mdelay(priv->poc_delay);
++ }
++ }
++ }
++
++ max9288_sensor_reset(client, client->addr, 1); /* sensor reset */
++
++ client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
++ reg8_read(client, 0x04, &lock_sts); /* LOCK status */
++
++ if (!timeout) {
++ ret = -ETIMEDOUT;
++ goto out;
++ }
++
++out:
++ sprintf(timeout_str, "retries=%d lock_sts=%d", priv->timeout - timeout, !!(lock_sts & 0x80));
++ dev_info(&client->dev, "link %s %sat 0x%x %s %s\n", ser_name(priv->ser_id),
++ ret == -EADDRINUSE ? "already " : "", priv->max9271_addr,
++ ret == -ETIMEDOUT ? "not found: timeout GMSL link establish" : "",
++ priv->timeout - timeout ? timeout_str : "");
++
++ return ret;
++}
++
++static void max9288_initial_setup(struct i2c_client *client)
++{
++ struct max9288_priv *priv = i2c_get_clientdata(client);
++
++ /* Initial setup */
++ client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
++ reg8_write(client, 0x09, 0x40); /* Automatic pixel count enable */
++ reg8_write(client, 0x15, 0x70); /* Enable HV and DE tracking by register 0x69 */
++ reg8_write(client, 0x60, (priv->dbl ? 0x20 : 0) |
++ (priv->dt & 0xf)); /* VC=0, DBL mode, DataType */
++ reg8_write(client, 0x65, 0x47 | ((priv->lanes - 1) << 4)); /* setup CSI lanes, DE input is HS */
++
++ reg8_write(client, 0x08, 0x20); /* use D18/19 for HS/VS */
++ reg8_write(client, 0x14, (priv->vsync ? 0x80 : 0) | (priv->hsync ? 0x40 : 0)); /* setup HS/VS inversion */
++ reg8_write(client, 0x64, 0x0c); /* Drive HSTRAIL state for 120ns after the last payload bit */
++}
++
++static void max9288_gmsl_link_setup(struct i2c_client *client)
++{
++ struct max9288_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, (priv->pclk_rising_edge ? 0 : 0x10) |
++ (priv->dbl ? 0x80 : 0) |
++ (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS encoding disabled, DBL mode, BWS 24/32-bit */
++ 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 || priv->ser_id == MAX96707_ID) {
++ switch (priv->dt) {
++ case YUV8_DT:
++ /* setup crossbar for YUV8/RAW8: reverse DVP bus */
++ reg8_write(client, 0x20, 3);
++ reg8_write(client, 0x21, 4);
++ reg8_write(client, 0x22, 5);
++ reg8_write(client, 0x23, 6);
++ reg8_write(client, 0x24, 7);
++ reg8_write(client, 0x25, 0x40);
++ reg8_write(client, 0x26, 0x40);
++ if (priv->ser_id == MAX96705_ID) {
++ reg8_write(client, 0x27, 14); /* HS: D14->D18 */
++ reg8_write(client, 0x28, 15); /* VS: D15->D19 */
++ }
++ if (priv->ser_id == MAX96707_ID) {
++ reg8_write(client, 0x27, 14); /* HS: D14->D18, this is a virtual NC pin, hence it is D14 at HS */
++ reg8_write(client, 0x28, 13); /* VS: D13->D19 */
++ }
++ reg8_write(client, 0x29, 0x40);
++ reg8_write(client, 0x2A, 0x40);
++
++ /* this is second byte if DBL=1 */
++ reg8_write(client, 0x30, 0x10 + 0);
++ reg8_write(client, 0x31, 0x10 + 1);
++ reg8_write(client, 0x32, 0x10 + 2);
++ reg8_write(client, 0x33, 0x10 + 3);
++ reg8_write(client, 0x34, 0x10 + 4);
++ reg8_write(client, 0x35, 0x10 + 5);
++ reg8_write(client, 0x36, 0x10 + 6);
++ reg8_write(client, 0x37, 0x10 + 7);
++ reg8_write(client, 0x38, 0);
++ reg8_write(client, 0x39, 1);
++ reg8_write(client, 0x3A, 2);
++
++ reg8_write(client, 0x67, 0xC4); /* DBL_ALIGN_TO = 100b */
++
++ break;
++ case RAW12_DT:
++#if 0 /* Not supported yet */
++ /* setup crossbar for RAW12: reverse DVP bus */
++ reg8_write(client, 0x20, 11);
++ reg8_write(client, 0x21, 10);
++ reg8_write(client, 0x22, 9);
++ reg8_write(client, 0x23, 8);
++ reg8_write(client, 0x24, 7);
++ reg8_write(client, 0x25, 6);
++ reg8_write(client, 0x26, 5);
++ reg8_write(client, 0x27, 4);
++ reg8_write(client, 0x28, 3);
++ reg8_write(client, 0x29, 2);
++ reg8_write(client, 0x2a, 1);
++ reg8_write(client, 0x2b, 0);
++
++ /* this is second byte if DBL=1 */
++ reg8_write(client, 0x30, 27);
++ reg8_write(client, 0x31, 26);
++ reg8_write(client, 0x32, 25);
++ reg8_write(client, 0x33, 24);
++ reg8_write(client, 0x34, 23);
++ reg8_write(client, 0x35, 22);
++ reg8_write(client, 0x36, 21);
++ reg8_write(client, 0x37, 20);
++ reg8_write(client, 0x38, 19);
++ reg8_write(client, 0x39, 18);
++ reg8_write(client, 0x3a, 17);
++ reg8_write(client, 0x3b, 16);
++
++ if (!priv->bws && priv->dbl)
++ dev_err(&client->dev, " BWS must be 27/32-bit for RAW12 in DBL mode\n");
++#endif
++ break;
++ }
++
++ if (priv->hsgen) {
++ /* HS/VS pins map */
++ reg8_write(client, 0x3f, 0x10); /* HS (NC) */
++ reg8_write(client, 0x41, 0x10); /* DE (NC) */
++ if (priv->ser_id == MAX96705_ID)
++ reg8_write(client, 0x40, 15); /* VS (DIN13) */
++ if (priv->ser_id == MAX96707_ID)
++ reg8_write(client, 0x40, 13); /* VS (DIN13) */
++#if 0
++ /* following must come from imager */
++#define SENSOR_WIDTH (1280*2)
++#define HTS (1288*2)
++#define VTS 960
++#define HTS_DELAY 0x9
++ reg8_write(client, 0x4e, HTS_DELAY >> 16); /* HS delay */
++ reg8_write(client, 0x4f, (HTS_DELAY >> 8) & 0xff);
++ reg8_write(client, 0x50, HTS_DELAY & 0xff);
++ reg8_write(client, 0x54, SENSOR_WIDTH >> 8); /* HS high period */
++ reg8_write(client, 0x55, SENSOR_WIDTH & 0xff);
++ reg8_write(client, 0x56, (HTS - SENSOR_WIDTH) >> 8); /* HS low period */
++ reg8_write(client, 0x57, (HTS - SENSOR_WIDTH) & 0xff);
++ reg8_write(client, 0x58, VTS >> 8); /* HS count */
++ reg8_write(client, 0x59, VTS & 0xff );
++#endif
++ reg8_write(client, 0x43, 0x15); /* enable HS generator */
++ }
++ }
++
++ client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
++ reg8_write(client, 0x1c, 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 << 1); /* MAX9271-CAMx I2C new */
++ /* I2C addresse change */
++ reg8_write(client, 0x01, priv->des_addr << 1); /* MAX9288 I2C */
++ reg8_write(client, 0x00, priv->max9271_addr << 1); /* MAX9271-CAM0 I2C new */
++ usleep_range(2000, 2500); /* wait 2ms */
++ /* put MAX9271 in configuration link state */
++ client->addr = priv->max9271_addr; /* 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; /* MAX9288-CAMx I2C */
++ maxim_max927x_dump_regs(client);
++ client->addr = priv->max9271_addr; /* MAX9271-CAMx I2C new */
++ maxim_max927x_dump_regs(client);
++#endif
++}
++
++static int max9288_initialize(struct i2c_client *client)
++{
++ struct max9288_priv *priv = i2c_get_clientdata(client);
++
++ dev_info(&client->dev, "LANES=%d, PCLK edge=%s\n",
++ priv->lanes, priv->pclk_rising_edge ? "rising" : "falling");
++
++ max9288_preinit(client, priv->des_addr);
++ max9288_initial_setup(client);
++
++ if (!IS_ERR(priv->poc_gpio)) {
++ gpiod_direction_output(priv->poc_gpio, 1); /* POC power on */
++ mdelay(priv->poc_delay);
++ }
++
++ max9288_reverse_channel_setup(client);
++ max9288_gmsl_link_setup(client);
++
++ client->addr = priv->des_addr;
++
++ return 0;
++}
++
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++static int max9288_g_register(struct v4l2_subdev *sd,
++ struct v4l2_dbg_register *reg)
++{
++ struct max9288_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 max9288_s_register(struct v4l2_subdev *sd,
++ const struct v4l2_dbg_register *reg)
++{
++ struct max9288_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 max9288_s_power(struct v4l2_subdev *sd, int on)
++{
++ struct max9288_priv *priv = v4l2_get_subdevdata(sd);
++ struct i2c_client *client = priv->client;
++
++ client->addr = priv->max9271_addr; /* MAX9271-CAMx I2C new */
++ max9288_write_remote_verify(client, 0x04, on ? (conf_link ? 0x43 : 0x83) : 0x43); /* enable serial_link or conf_link */
++ usleep_range(2000, 2500); /* wait 2ms after changing reverse_control */
++ client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
++
++ return 0;
++}
++
++static struct v4l2_subdev_core_ops max9288_subdev_core_ops = {
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++ .g_register = max9288_g_register,
++ .s_register = max9288_s_register,
++#endif
++ .s_power = max9288_s_power,
++};
++
++static struct v4l2_subdev_ops max9288_subdev_ops = {
++ .core = &max9288_subdev_core_ops,
++};
++
++static int max9288_parse_dt(struct i2c_client *client)
++{
++ struct max9288_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;
++ int sensor_delay, gpio0 = 1, gpio1 = 1;
++ u8 val = 0;
++ char poc_name[10];
++
++ 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);
++
++ sprintf(poc_name, "POC%d", 0);
++ priv->poc_gpio = devm_gpiod_get_optional(&client->dev, kstrdup(poc_name, GFP_KERNEL), 0);
++
++ reg8_read(client, 0x1e, &val); /* read max9288 ID */
++ if (val != MAX9288_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, 0x06, (gpio1 << 3) | (gpio0 << 1));
++
++ 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);
++ 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,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;
++ if (of_property_read_u32(np, "maxim,bws", &priv->bws))
++ priv->bws = 0;
++ if (of_property_read_u32(np, "maxim,dbl", &priv->dbl))
++ priv->dbl = 1;
++ if (of_property_read_u32(np, "maxim,dt", &priv->dt))
++ priv->dt = 3;
++ if (of_property_read_u32(np, "maxim,hsgen", &priv->hsgen))
++ priv->hsgen = 0;
++ if (of_property_read_u32(np, "maxim,pclk", &priv->pclk))
++ priv->pclk = pclk;
++
++ /* module params override dts */
++ if (him)
++ priv->him = him;
++ 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 (timeout_n)
++ priv->timeout = timeout_n;
++ if (poc_delay)
++ priv->poc_delay = poc_delay;
++ if (bws)
++ priv->bws = bws;
++ if (!dbl)
++ priv->dbl = dbl;
++ if (dt != 3)
++ priv->dt = dt;
++ if (hsgen)
++ priv->hsgen = hsgen;
++ if (pclk != 100)
++ priv->pclk = pclk;
++
++ endpoint = of_graph_get_next_endpoint(np, endpoint);
++ if (endpoint) {
++ if (of_property_read_u32(endpoint, "max9271-addr", &priv->max9271_addr)) {
++ of_node_put(endpoint);
++ dev_err(&client->dev, "max9271-addr not set\n");
++ return -EINVAL;
++ }
++
++ priv->sd_fwnode = of_fwnode_handle(endpoint);
++ }
++
++ of_node_put(endpoint);
++ return 0;
++}
++
++static void max9288_setup_remote_endpoint(struct i2c_client *client)
++{
++ struct max9288_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;
++
++ 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*bpp/lanes */
++ priv->csi_rate = cpu_to_be32(priv->pclk * dt2bpp[priv->dt] / 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);
++ }
++
++ of_node_put(endpoint);
++}
++
++static int max9288_probe(struct i2c_client *client,
++ const struct i2c_device_id *did)
++{
++ struct max9288_priv *priv;
++ int err;
++
++ 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;
++
++ err = max9288_parse_dt(client);
++ if (err)
++ goto out;
++
++ err = max9288_initialize(client);
++ if (err < 0)
++ goto out;
++
++ max9288_setup_remote_endpoint(client);
++
++ v4l2_subdev_init(&priv->sd, &max9288_subdev_ops);
++ priv->sd.owner = client->dev.driver->owner;
++ priv->sd.dev = &client->dev;
++ v4l2_set_subdevdata(&priv->sd, priv);
++ priv->sd.fwnode = priv->sd_fwnode;
++
++ snprintf(priv->sd.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);
++ if (err < 0)
++ goto out;
++out:
++ return err;
++}
++
++static int max9288_remove(struct i2c_client *client)
++{
++ struct max9288_priv *priv = i2c_get_clientdata(client);
++
++ v4l2_async_unregister_subdev(&priv->sd);
++ v4l2_device_unregister_subdev(&priv->sd);
++
++ return 0;
++}
++
++static const struct of_device_id max9288_dt_ids[] = {
++ { .compatible = "maxim,max9288" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, max9288_dt_ids);
++
++static const struct i2c_device_id max9288_id[] = {
++ { "max9288", 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, max9288_id);
++
++static struct i2c_driver max9288_i2c_driver = {
++ .driver = {
++ .name = "max9288",
++ .of_match_table = of_match_ptr(max9288_dt_ids),
++ },
++ .probe = max9288_probe,
++ .remove = max9288_remove,
++ .id_table = max9288_id,
++};
++
++module_i2c_driver(max9288_i2c_driver);
++
++MODULE_DESCRIPTION("GMSL driver for MAX9288");
++MODULE_AUTHOR("Vladimir Barinov");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/i2c/soc_camera/imagers/Kconfig b/drivers/media/i2c/soc_camera/imagers/Kconfig
+new file mode 100644
+index 0000000..a6ccf62
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/imagers/Kconfig
+@@ -0,0 +1,5 @@
++config SOC_CAMERA_OV106XX
++ tristate "ov106xx camera support"
++ depends on I2C
++ help
++ This is a runtime detected GMSL1/GMSL2/FPDLink3 sensors driver
+diff --git a/drivers/media/i2c/soc_camera/imagers/Makefile b/drivers/media/i2c/soc_camera/imagers/Makefile
+new file mode 100644
+index 0000000..ca10bbc
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/imagers/Makefile
+@@ -0,0 +1,2 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_SOC_CAMERA_OV106XX) += dummy.o
+diff --git a/drivers/media/i2c/soc_camera/imagers/dummy.c b/drivers/media/i2c/soc_camera/imagers/dummy.c
+new file mode 100644
+index 0000000..d213fff
+--- /dev/null
++++ b/drivers/media/i2c/soc_camera/imagers/dummy.c
+@@ -0,0 +1,491 @@
++/*
++ * Dummy sensor camera driver
++ *
++ * Copyright (C) 2019 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>
++
++struct dummy_priv {
++ struct v4l2_subdev sd;
++ struct v4l2_ctrl_handler hdl;
++ struct media_pad pad;
++ struct v4l2_rect rect;
++ u8 id[6];
++ int max_width;
++ int max_height;
++ const char * media_bus_format;
++ int mbus_format;
++};
++
++static int width = 1920;
++module_param(width, int, 0644);
++MODULE_PARM_DESC(width, " width (default: 1920)");
++
++static int height = 1080;
++module_param(height, int, 0644);
++MODULE_PARM_DESC(height, " height (default: 1080)");
++
++static char *mbus = "yuyv";
++module_param(mbus, charp, 0644);
++MODULE_PARM_DESC(mbus, " MEDIA_BUS_FORMAT (default: YUYV)");
++
++static inline struct dummy_priv *to_dummy(const struct i2c_client *client)
++{
++ return container_of(i2c_get_clientdata(client), struct dummy_priv, sd);
++}
++
++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
++{
++ return &container_of(ctrl->handler, struct dummy_priv, hdl)->sd;
++}
++
++static int dummy_s_stream(struct v4l2_subdev *sd, int enable)
++{
++ return 0;
++}
++
++static int dummy_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 dummy_priv *priv = to_dummy(client);
++
++ if (format->pad)
++ return -EINVAL;
++
++ mf->width = priv->rect.width;
++ mf->height = priv->rect.height;
++ mf->code = priv->mbus_format;
++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
++ mf->field = V4L2_FIELD_NONE;
++
++ return 0;
++}
++
++static int dummy_set_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 dummy_priv *priv = to_dummy(client);
++
++ mf->code = priv->mbus_format;
++ 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 dummy_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct dummy_priv *priv = to_dummy(client);
++
++ if (code->pad || code->index > 0)
++ return -EINVAL;
++
++ code->code = priv->mbus_format;
++
++ return 0;
++}
++
++static int dummy_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct dummy_priv *priv = to_dummy(client);
++
++ memcpy(edid->edid, priv->id, 6);
++
++ edid->edid[6] = 0xff;
++ edid->edid[7] = client->addr;
++ edid->edid[8] = 'D' >> 8;
++ edid->edid[9] = 'Y' & 0xff;
++
++ return 0;
++}
++
++static int dummy_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 dummy_priv *priv = to_dummy(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 dummy_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 dummy_priv *priv = to_dummy(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 dummy_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 dummy_g_register(struct v4l2_subdev *sd,
++ struct v4l2_dbg_register *reg)
++{
++ reg->val = 0;
++ reg->size = sizeof(u16);
++
++ return 0;
++}
++
++static int dummy_s_register(struct v4l2_subdev *sd,
++ const struct v4l2_dbg_register *reg)
++{
++ return 0;
++}
++#endif
++
++static struct v4l2_subdev_core_ops dummy_core_ops = {
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++ .g_register = dummy_g_register,
++ .s_register = dummy_s_register,
++#endif
++};
++
++static int dummy_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++ 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_ANALOGUE_GAIN:
++ case V4L2_CID_EXPOSURE:
++ case V4L2_CID_HFLIP:
++ case V4L2_CID_VFLIP:
++ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
++ break;
++ }
++
++ return 0;
++}
++
++static const struct v4l2_ctrl_ops dummy_ctrl_ops = {
++ .s_ctrl = dummy_s_ctrl,
++};
++
++static struct v4l2_subdev_video_ops dummy_video_ops = {
++ .s_stream = dummy_s_stream,
++ .g_mbus_config = dummy_g_mbus_config,
++};
++
++static const struct v4l2_subdev_pad_ops dummy_subdev_pad_ops = {
++ .get_edid = dummy_get_edid,
++ .enum_mbus_code = dummy_enum_mbus_code,
++ .get_selection = dummy_get_selection,
++ .set_selection = dummy_set_selection,
++ .get_fmt = dummy_get_fmt,
++ .set_fmt = dummy_set_fmt,
++};
++
++static struct v4l2_subdev_ops dummy_subdev_ops = {
++ .core = &dummy_core_ops,
++ .video = &dummy_video_ops,
++ .pad = &dummy_subdev_pad_ops,
++};
++
++static void dummy_otp_id_read(struct i2c_client *client)
++{
++ struct dummy_priv *priv = to_dummy(client);
++
++ /* dummy camera id */
++ priv->id[0] = 'd';
++ priv->id[1] = 'u';
++ priv->id[2] = 'm';
++ priv->id[3] = 'm';
++ priv->id[4] = 'y';
++ priv->id[5] = '.';
++}
++
++static ssize_t dummy_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 dummy_priv *priv = to_dummy(client);
++
++ dummy_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_dummy, S_IRUGO, dummy_otp_id_show, NULL);
++
++static int dummy_initialize(struct i2c_client *client)
++{
++ struct dummy_priv *priv = to_dummy(client);
++
++ if (strcmp(priv->media_bus_format, "yuyv") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_YUYV8_2X8;
++ else if (strcmp(priv->media_bus_format, "uyvy") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_UYVY8_2X8;
++ else if (strcmp(priv->media_bus_format, "grey") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_Y8_1X8;
++ else if (strcmp(priv->media_bus_format, "rggb8") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SRGGB8_1X8;
++ else if (strcmp(priv->media_bus_format, "bggr8") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SBGGR8_1X8;
++ else if (strcmp(priv->media_bus_format, "grbg8") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SGRBG8_1X8;
++ else if (strcmp(priv->media_bus_format, "rggb12") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SRGGB12_1X12;
++ else if (strcmp(priv->media_bus_format, "bggr12") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SBGGR12_1X12;
++ else if (strcmp(priv->media_bus_format, "grbg12") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SGRBG12_1X12;
++ else if (strcmp(priv->media_bus_format, "rggb14") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SRGGB14_1X14;
++ else if (strcmp(priv->media_bus_format, "bggr14") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SBGGR14_1X14;
++ else if (strcmp(priv->media_bus_format, "grbg14") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SGRBG14_1X14;
++ else if (strcmp(priv->media_bus_format, "rggb16") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SRGGB16_1X16;
++ else if (strcmp(priv->media_bus_format, "bggr16") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SBGGR16_1X16;
++ else if (strcmp(priv->media_bus_format, "grbg16") == 0)
++ priv->mbus_format = MEDIA_BUS_FMT_SGRBG16_1X16;
++ else {
++ v4l_err(client, "failed to parse mbus format (%s)\n", priv->media_bus_format);
++ return -EINVAL;
++ }
++
++ /* Read OTP IDs */
++ dummy_otp_id_read(client);
++
++ dev_info(&client->dev, "Dummy camera sensor, res %dx%d, mbus %s, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n",
++ priv->max_width, priv->max_height, priv->media_bus_format, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]);
++
++ return 0;
++}
++
++static int dummy_parse_dt(struct device_node *np, struct dummy_priv *priv)
++{
++ if (of_property_read_u32(np, "dummy,width", &priv->max_width))
++ priv->max_width = width;
++
++ if (of_property_read_u32(np, "dummy,height", &priv->max_height))
++ priv->max_height = height;
++
++ if (of_property_read_string(np, "dummy,mbus", &priv->media_bus_format))
++ priv->media_bus_format = mbus;
++
++ /* module params override dts */
++ if (strcmp(mbus, "yuyv"))
++ priv->media_bus_format = mbus;
++ if (width != 1920)
++ priv->max_width = width;
++ if (height != 1080)
++ priv->max_height = height;
++
++ return 0;
++}
++
++static int dummy_probe(struct i2c_client *client,
++ const struct i2c_device_id *did)
++{
++ struct dummy_priv *priv;
++ int ret;
++
++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ v4l2_i2c_subdev_init(&priv->sd, client, &dummy_subdev_ops);
++ priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
++
++ v4l2_ctrl_handler_init(&priv->hdl, 4);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_BRIGHTNESS, 0, 16, 1, 7);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_CONTRAST, 0, 16, 1, 7);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_SATURATION, 0, 7, 1, 2);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_HUE, 0, 23, 1, 12);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_GAMMA, -128, 128, 1, 0);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_SHARPNESS, 0, 10, 1, 3);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_AUTOGAIN, 0, 1, 1, 0);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_GAIN, 1, 0x7ff, 1, 0x200);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_ANALOGUE_GAIN, 1, 0xe, 1, 0xa);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_EXPOSURE, 1, 0x600, 1, 0x144);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_ctrl_ops,
++ V4L2_CID_HFLIP, 0, 1, 1, 0);
++ v4l2_ctrl_new_std(&priv->hdl, &dummy_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 = dummy_parse_dt(client->dev.of_node, priv);
++ if (ret)
++ goto cleanup;
++
++ ret = dummy_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_dummy) != 0) {
++ dev_err(&client->dev, "sysfs otp_id entry creation failed\n");
++ goto cleanup;
++ }
++
++ return 0;
++
++cleanup:
++ media_entity_cleanup(&priv->sd.entity);
++ v4l2_ctrl_handler_free(&priv->hdl);
++ v4l2_device_unregister_subdev(&priv->sd);
++ v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
++ client->addr, client->adapter->name);
++ return ret;
++}
++
++static int dummy_remove(struct i2c_client *client)
++{
++ struct dummy_priv *priv = i2c_get_clientdata(client);
++
++ device_remove_file(&client->dev, &dev_attr_otp_id_dummy);
++ 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;
++}
++
++static const struct i2c_device_id dummy_id[] = {
++ { "dummy-camera", 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, dummy_id);
++
++static const struct of_device_id dummy_of_ids[] = {
++ { .compatible = "dummy-camera", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, dummy_of_ids);
++
++static struct i2c_driver dummy_i2c_driver = {
++ .driver = {
++ .name = "dummy-camera",
++ .of_match_table = dummy_of_ids,
++ },
++ .probe = dummy_probe,
++ .remove = dummy_remove,
++ .id_table = dummy_id,
++};
++module_i2c_driver(dummy_i2c_driver);
++
++MODULE_DESCRIPTION("Dummy SoC camera driver");
++MODULE_AUTHOR("Vladimir Barinov");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/i2c/soc_camera/max9286.c b/drivers/media/i2c/soc_camera/max9286.c
+deleted file mode 100644
+index 28bd3c3..0000000
+--- a/drivers/media/i2c/soc_camera/max9286.c
++++ /dev/null
+@@ -1,1071 +0,0 @@
+-/*
+- * 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/reboot.h>
+-#include <linux/videodev2.h>
+-
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-device.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;
+- int fps_numerator;
+- int fps_denominator;
+- int pclk;
+- char pclk_rising_edge;
+- int gpio_resetb;
+- int active_low_resetb;
+- int him;
+- int hsync;
+- int vsync;
+- int timeout;
+- int poc_delay;
+- int bws;
+- int dbl;
+- int dt;
+- int hsgen;
+- u64 crossbar;
+- char cb[16];
+- int hts;
+- int vts;
+- int hts_delay;
+- int imager_width;
+- atomic_t use_count;
+- u32 csi2_outord;
+- u32 switchin;
+- struct i2c_client *client;
+- int max9271_addr_map[4];
+- int ser_id;
+- struct gpio_desc *poc_gpio[4]; /* PoC power supply */
+- struct notifier_block reboot_notifier;
+-
+- /* link statistic */
+- int prbserr[4];
+- int deterr[4];
+- int correrr[4];
+-};
+-
+-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 timeout_n = 100;
+-module_param(timeout_n, int, 0644);
+-MODULE_PARM_DESC(timeout_n, " Timeout of link detection (default: 100 retries)");
+-
+-static int poc_delay = 50;
+-module_param(poc_delay, int, 0644);
+-MODULE_PARM_DESC(poc_delay, " Delay in ms after POC enable (default: 50 ms)");
+-
+-static int bws;
+-module_param(bws, int, 0644);
+-MODULE_PARM_DESC(bws, " BWS mode (default: 0 - 24-bit gmsl packets)");
+-
+-static int dbl = 1;
+-module_param(dbl, int, 0644);
+-MODULE_PARM_DESC(dbl, " DBL mode (default: 1 - DBL mode enabled)");
+-
+-static int dt = 3;
+-module_param(dt, int, 0644);
+-MODULE_PARM_DESC(dt, " DataType (default: 3 - YUV8), 0 - RGB888, 5 - RAW8, 6 - RAW10, 7 - RAW12, 8 - RAW14");
+-
+-static int hsgen;
+-module_param(hsgen, int, 0644);
+-MODULE_PARM_DESC(hsgen, " Enable HS embedded generator (default: 0 - disabled)");
+-
+-static int pclk = 100;
+-module_param(pclk, int, 0644);
+-MODULE_PARM_DESC(pclk, " PCLK rate (default: 100MHz)");
+-
+-static int switchin = 0;
+-module_param(switchin, int, 0644);
+-MODULE_PARM_DESC(switchin, " COAX SWITCH IN+ and IN- (default: 0 - not switched)");
+-
+-static unsigned long crossbar = 0xba9876543210;
+-module_param(crossbar, ulong, 0644);
+-MODULE_PARM_DESC(crossbar, " Crossbar setup (default: ba9876543210 - reversed)");
+-
+-enum {
+- RGB888_DT = 0,
+- RGB565_DT,
+- RGB666_DT,
+- YUV8_DT, /* default */
+- YUV10_DT,
+- RAW8_DT,
+- RAW10_DT,
+- RAW12_DT,
+- RAW14_DT,
+-};
+-
+-static int dt2bpp [9] = {
+- 24, /* RGB888 */
+- 16, /* RGB565 */
+- 18, /* RGB666 */
+- 8, /* YUV8 - default */
+- 10, /* YUV10 */
+- 8, /* RAW8/RAW16 */
+- 10, /* RAW10 */
+- 12, /* RAW12 */
+- 14, /* RAW14 */
+-};
+-
+-static char* ser_name(int id)
+-{
+- switch (id) {
+- case MAX9271_ID:
+- return "MAX9271";
+- case MAX96705_ID:
+- return "MAX96705";
+- case MAX96707_ID:
+- return "MAX96707";
+- default:
+- return "unknown";
+- }
+-}
+-
+-static void max9286_write_remote_verify(struct i2c_client *client, int idx, u8 reg, u8 val)
+-{
+- struct max9286_priv *priv = i2c_get_clientdata(client);
+- int timeout;
+-
+- for (timeout = 0; timeout < 10; timeout++) {
+- int tmp_addr;
+- u8 sts = 0;
+- u8 val2 = 0;
+-
+- reg8_write(client, reg, val);
+-
+- tmp_addr = client->addr;
+- client->addr = priv->des_addr; /* MAX9286-CAMx I2C */
+- reg8_read(client, 0x70, &sts);
+- client->addr = tmp_addr;
+- if (sts & BIT(idx)) /* if ACKed */ {
+- reg8_read(client, reg, &val2);
+- if (val2 == val)
+- break;
+- }
+-
+- usleep_range(1000, 1500);
+- }
+-
+- if (timeout >= 10)
+- dev_err(&client->dev, "timeout remote write acked\n");
+-}
+-
+-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, 0x1b, priv->switchin); /* coax polarity (default - normal) */
+- reg8_write(client, 0x1c, (priv->him ? 0xf0 : 0x00) |
+- (priv->bws ? 0x05 : 0x04)); /* high-immunity/legacy mode, BWS 24bit */
+-}
+-
+-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;
+-
+- if (priv->active_low_resetb)
+- reset_on = !reset_on;
+-
+- /* sensor reset/unreset using serializer gpio */
+- client->addr = addr;
+- reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | (reset_on ? BIT(priv->gpio_resetb) : 0)); /* set GPIOn value */
+- 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++) {
+- if (priv->ser_id == MAX96705_ID || priv->ser_id == MAX96707_ID)
+- continue;
+-
+- client->addr = priv->des_addr; /* MAX9286 I2C */
+- reg8_write(client, 0x00, 0xe0 | BIT(idx)); /* enable GMSL link for CAMx */
+- reg8_write(client, 0x0a, 0x11 << idx); /* enable reverse/forward control for CAMx */
+- usleep_range(5000, 5500); /* wait 2ms after any change of reverse channel settings */
+-
+- client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx I2C */
+- max9286_sensor_reset(client, client->addr, 0); /* sensor unreset using gpios. TODO: should be in imager driver */
+- }
+-
+- 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->switchin | priv->links_mask); /* coax polarity, 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, lock_sts = 0, link_sts = 0;
+- int timeout = priv->timeout;
+- char timeout_str[40];
+- int ret = 0;
+-
+- /* Reverse channel enable */
+- client->addr = priv->des_addr; /* MAX9286-CAMx I2C */
+- 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 (;;) {
+- if (priv->him) {
+- /* HIM mode setup */
+- client->addr = 0x40; /* MAX9271-CAMx I2C */
+- reg8_write(client, 0x4d, 0xc0);
+- usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
+- 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 */
+- if (priv->bws) {
+- reg8_write(client, 0x07, 0x04 | (priv->pclk_rising_edge ? 0 : 0x10) |
+- (priv->dbl ? 0x80 : 0) |
+- (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS encoding enabled, DBL mode, BWS 24/32-bit */
+- usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
+- }
+- } else {
+- /* Legacy mode setup */
+- client->addr = priv->des_addr; /* MAX9286-CAMx I2C */
+- reg8_write(client, 0x3f, 0x4f); /* enable custom reverse channel & first pulse length */
+- 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, 0x5f); /* enable reverse control channel programming (MAX96705-MAX96711 only) */
+- usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
+- if (priv->bws) {
+- reg8_write(client, 0x07, 0x04 | (priv->pclk_rising_edge ? 0 : 0x10) |
+- (priv->dbl ? 0x80 : 0) |
+- (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS encoding enabled, DBL mode, BWS 24/32-bit */
+- 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 || val == MAX96707_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 || val == MAX96707_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 (poc_trig) {
+- if (!IS_ERR(priv->poc_gpio[idx]) && (timeout % poc_trig == 0)) {
+- 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 */
+-
+- client->addr = priv->des_addr; /* MAX9286-CAMx I2C */
+- reg8_read(client, 0x27, &lock_sts); /* LOCK status */
+- reg8_read(client, 0x49, &link_sts); /* LINK status */
+-
+- 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 lock_sts=%d link_sts=0x%x", priv->timeout - timeout, !!(lock_sts & 0x80), link_sts & (0x11 << idx));
+- 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 */
+- reg8_write(client, 0x12, ((priv->lanes - 1) << 6) |
+- (priv->dbl ? 0x30 : 0) |
+- (priv->dt & 0xf)); /* setup lanes, DBL mode, DataType */
+-
+- /* 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, 0x04 | (priv->pclk_rising_edge ? 0 : 0x10) |
+- (priv->dbl ? 0x80 : 0) |
+- (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS encoding enabled, DBL mode, BWS 24/32-bit */
+- 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 || priv->ser_id == MAX96707_ID) {
+- switch (priv->dt) {
+- case YUV8_DT:
+- /* setup crossbar for YUV8/RAW8: reverse DVP bus */
+- reg8_write(client, 0x20, priv->cb[7]);
+- reg8_write(client, 0x21, priv->cb[6]);
+- reg8_write(client, 0x22, priv->cb[5]);
+- reg8_write(client, 0x23, priv->cb[4]);
+- reg8_write(client, 0x24, priv->cb[3]);
+- reg8_write(client, 0x25, priv->cb[2]);
+- reg8_write(client, 0x26, priv->cb[1]);
+- reg8_write(client, 0x27, priv->cb[0]);
+-
+- /* this is second byte if DBL=1 */
+- reg8_write(client, 0x30, priv->cb[7] + 16);
+- reg8_write(client, 0x31, priv->cb[6] + 16);
+- reg8_write(client, 0x32, priv->cb[5] + 16);
+- reg8_write(client, 0x33, priv->cb[4] + 16);
+- reg8_write(client, 0x34, priv->cb[3] + 16);
+- reg8_write(client, 0x35, priv->cb[2] + 16);
+- reg8_write(client, 0x36, priv->cb[1] + 16);
+- reg8_write(client, 0x37, priv->cb[0] + 16);
+- break;
+- case RAW12_DT:
+- /* setup crossbar for RAW12: reverse DVP bus */
+- reg8_write(client, 0x20, priv->cb[11]);
+- reg8_write(client, 0x21, priv->cb[10]);
+- reg8_write(client, 0x22, priv->cb[9]);
+- reg8_write(client, 0x23, priv->cb[8]);
+- reg8_write(client, 0x24, priv->cb[7]);
+- reg8_write(client, 0x25, priv->cb[6]);
+- reg8_write(client, 0x26, priv->cb[5]);
+- reg8_write(client, 0x27, priv->cb[4]);
+- reg8_write(client, 0x28, priv->cb[3]);
+- reg8_write(client, 0x29, priv->cb[2]);
+- reg8_write(client, 0x2a, priv->cb[1]);
+- reg8_write(client, 0x2b, priv->cb[0]);
+-
+- /* this is second byte if DBL=1 */
+- reg8_write(client, 0x30, priv->cb[11] + 16);
+- reg8_write(client, 0x31, priv->cb[10] + 16);
+- reg8_write(client, 0x32, priv->cb[9] + 16);
+- reg8_write(client, 0x33, priv->cb[8] + 16);
+- reg8_write(client, 0x34, priv->cb[7] + 16);
+- reg8_write(client, 0x35, priv->cb[6] + 16);
+- reg8_write(client, 0x36, priv->cb[5] + 16);
+- reg8_write(client, 0x37, priv->cb[4] + 16);
+- reg8_write(client, 0x38, priv->cb[3] + 16);
+- reg8_write(client, 0x39, priv->cb[2] + 16);
+- reg8_write(client, 0x3a, priv->cb[1] + 16);
+- reg8_write(client, 0x3b, priv->cb[0] + 16);
+-
+- if (!priv->bws && priv->dbl)
+- dev_err(&client->dev, " BWS must be 27/32-bit for RAW12 in DBL mode\n");
+-
+- break;
+- }
+-
+- if (priv->hsgen) {
+- /* HS/VS pins map */
+- reg8_write(client, 0x3f, 0x10); /* HS (NC) */
+- reg8_write(client, 0x41, 0x10); /* DE (NC) */
+- if (priv->ser_id == MAX96705_ID)
+- reg8_write(client, 0x40, 15); /* VS (DIN13) */
+- if (priv->ser_id == MAX96707_ID)
+- reg8_write(client, 0x40, 13); /* VS (DIN13) */
+-#if 0
+- /* following must come from imager */
+-#define SENSOR_WIDTH (1280*2)
+-#define HTS (1288*2)
+-#define VTS 960
+-#define HTS_DELAY 0x9
+- reg8_write(client, 0x4e, HTS_DELAY >> 16); /* HS delay */
+- reg8_write(client, 0x4f, (HTS_DELAY >> 8) & 0xff);
+- reg8_write(client, 0x50, HTS_DELAY & 0xff);
+- reg8_write(client, 0x54, SENSOR_WIDTH >> 8); /* HS high period */
+- reg8_write(client, 0x55, SENSOR_WIDTH & 0xff);
+- reg8_write(client, 0x56, (HTS - SENSOR_WIDTH) >> 8); /* HS low period */
+- reg8_write(client, 0x57, (HTS - SENSOR_WIDTH) & 0xff);
+- reg8_write(client, 0x58, VTS >> 8); /* HS count */
+- reg8_write(client, 0x59, VTS & 0xff );
+-#endif
+- reg8_write(client, 0x43, 0x15); /* enable HS generator */
+- }
+- }
+-
+- 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 */
+- max9286_write_remote_verify(client, idx, 0x04, conf_link ? 0x43 : 0x83);
+-// 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 int max9286_reboot_notifier(struct notifier_block *nb, unsigned long event, void *buf)
+-{
+- struct max9286_priv *priv = container_of(nb, struct max9286_priv, reboot_notifier);
+- int idx;
+-
+- for (idx = 0; idx < priv->links; idx++) {
+- if (!IS_ERR(priv->poc_gpio[idx]))
+- gpiod_direction_output(priv->poc_gpio[idx], 0); /* POC power off */
+- }
+-
+- return NOTIFY_OK;
+-}
+-
+-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 int max9286_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+-{
+- return 0;
+-}
+-
+-static int max9286_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+-{
+- struct max9286_priv *priv = v4l2_get_subdevdata(sd);
+- struct i2c_client *client = priv->client;
+- struct v4l2_captureparm *cp = &parms->parm.capture;
+-
+- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+- if (cp->extendedmode != 0)
+- return -EINVAL;
+-
+- if (priv->fps_denominator != cp->timeperframe.denominator ||
+- priv->fps_numerator != cp->timeperframe.numerator) {
+- int f_period;
+-
+- f_period = priv->fsync_period * 30 * cp->timeperframe.numerator / cp->timeperframe.denominator;
+- reg8_write(client, 0x06, f_period & 0xff);
+- reg8_write(client, 0x07, (f_period >> 8) & 0xff);
+- reg8_write(client, 0x08, f_period >> 16);
+-
+- priv->fps_numerator = cp->timeperframe.numerator;
+- priv->fps_denominator = cp->timeperframe.denominator;
+- }
+-
+- return 0;
+-}
+-
+-static struct v4l2_subdev_video_ops max9286_video_ops = {
+- .g_parm = max9286_g_parm,
+- .s_parm = max9286_s_parm,
+-};
+-
+-static struct v4l2_subdev_ops max9286_subdev_ops = {
+- .core = &max9286_subdev_core_ops,
+- .video = &max9286_video_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, kstrdup(poc_name, GFP_KERNEL), 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;
+- if (of_property_read_u32(np, "maxim,bws", &priv->bws))
+- priv->bws = 0;
+- if (of_property_read_u32(np, "maxim,dbl", &priv->dbl))
+- priv->dbl = 1;
+- if (of_property_read_u32(np, "maxim,dt", &priv->dt))
+- priv->dt = 3;
+- if (of_property_read_u32(np, "maxim,hsgen", &priv->hsgen))
+- priv->hsgen = 0;
+- if (of_property_read_u32(np, "maxim,pclk", &priv->pclk))
+- priv->pclk = pclk;
+- if (of_property_read_u32(np, "maxim,switchin", &priv->switchin))
+- priv->switchin = 0;
+- if (of_property_read_u64(np, "maxim,crossbar", &priv->crossbar))
+- priv->crossbar = crossbar;
+-
+- /* 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 (timeout_n)
+- priv->timeout = timeout_n;
+- if (poc_delay)
+- priv->poc_delay = poc_delay;
+- if (bws)
+- priv->bws = bws;
+- if (!dbl)
+- priv->dbl = dbl;
+- if (dt != 3)
+- priv->dt = dt;
+- if (hsgen)
+- priv->hsgen = hsgen;
+- if (pclk != 100)
+- priv->pclk = pclk;
+- if (switchin)
+- priv->switchin = switchin;
+-
+- /* parse crossbar setup */
+- for (i = 0; i < 16; i++) {
+- priv->cb[i] = priv->crossbar % 16;
+- priv->crossbar /= 16;
+- }
+-
+- for (i = 0; i < priv->links; i++) {
+- endpoint = of_graph_get_next_endpoint(np, endpoint);
+- if (!endpoint)
+- break;
+-
+- if (of_property_read_u32(endpoint, "max9271-addr", &priv->max9271_addr_map[i])) {
+- of_node_put(endpoint);
+- dev_err(&client->dev, "max9271-addr not set\n");
+- return -EINVAL;
+- }
+-
+- priv->sd_fwnode[i] = of_fwnode_handle(endpoint);
+- }
+-
+- of_node_put(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;
+-
+- 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*bpp*links/lanes */
+- priv->csi_rate = cpu_to_be32(priv->pclk * dt2bpp[priv->dt] * 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);
+- }
+-
+- of_node_put(endpoint);
+-}
+-
+-static const char *line_status[] =
+-{
+- "BAT",
+- "GND",
+- "NORMAL",
+- "OPEN"
+-};
+-
+-static ssize_t max9286_link_show(struct device *dev,
+- struct device_attribute *attr, char *buf)
+-{
+- int i = -1;
+- u8 val = 0;
+- bool lenghterr, linebuffof, hlocked, prbsok, vsyncdet, configdet, videodet;
+- int lf;
+- u8 prbserr = 0, deterr = 0, correrr = 0;
+- struct i2c_client *client = to_i2c_client(dev);
+- struct max9286_priv *priv = i2c_get_clientdata(client);
+-
+- if (!sscanf(attr->attr.name, "link_%d", &i))
+- return -EINVAL;
+-
+- if ((i < 0) || (i > 3))
+- return -EINVAL;
+-
+- reg8_read(client, 0x20, &val);
+- lf = (val >> (2 * i)) & 0x03;
+-
+- reg8_read(client, 0x21, &val);
+- hlocked = !!(val & (1 << i));
+- prbsok = !!(val & (1 << (i + 4)));
+-
+- reg8_read(client, 0x22, &val);
+- lenghterr = !!(val & (1 << i));
+- linebuffof = !!(val & (1 << (i + 4)));
+-
+- reg8_read(client, 0x23 + i, &prbserr);
+- priv->prbserr[i] += prbserr;
+-
+- reg8_read(client, 0x27, &val);
+- vsyncdet = !!(val & (1 << i));
+-
+- reg8_read(client, 0x28 + i, &deterr);
+- priv->deterr[i] += deterr;
+-
+- reg8_read(client, 0x2c + i, &correrr);
+- priv->correrr[i] += correrr;
+-
+- reg8_read(client, 0x49, &val);
+- configdet = !!(val & (1 << i));
+- videodet = !!(val & (1 << (i + 4)));
+-
+- return sprintf(buf, "LINK:%d LF:%s HLOCKED:%d PRBSOK:%d LINBUFFOF:%d"
+- " LENGHTERR:%d VSYNCDET:%d CONFIGDET:%d VIDEODET:%d"
+- " PRBSERR:%d(%d) DETEERR:%d(%d) CORRERR:%d(%d)\n",
+- i, line_status[lf], hlocked, prbsok, lenghterr,
+- linebuffof, vsyncdet, configdet, videodet,
+- priv->prbserr[i], prbserr,
+- priv->deterr[i], deterr,
+- priv->correrr[i], correrr);
+- return 0;
+-}
+-
+-static DEVICE_ATTR(link_0, S_IRUGO, max9286_link_show, NULL);
+-static DEVICE_ATTR(link_1, S_IRUGO, max9286_link_show, NULL);
+-static DEVICE_ATTR(link_2, S_IRUGO, max9286_link_show, NULL);
+-static DEVICE_ATTR(link_3, S_IRUGO, max9286_link_show, NULL);
+-
+-static struct attribute *max9286_attributes_links[] = {
+- &dev_attr_link_0.attr,
+- &dev_attr_link_1.attr,
+- &dev_attr_link_2.attr,
+- &dev_attr_link_3.attr,
+- NULL
+-};
+-
+-static const struct attribute_group max9286_group = {
+- .attrs = max9286_attributes_links,
+-};
+-
+-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;
+- priv->fps_numerator = 1;
+- priv->fps_denominator = 30;
+-
+- 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;
+- }
+-
+- priv->reboot_notifier.notifier_call = max9286_reboot_notifier;
+- err = register_reboot_notifier(&priv->reboot_notifier);
+- if (err) {
+- dev_err(&client->dev, "failed to register reboot notifier\n");
+- goto out;
+- }
+-
+- err = sysfs_create_group(&client->dev.kobj,
+- &max9286_group);
+- if (err < 0)
+- dev_err(&client->dev, "Sysfs registration failed\n");
+-out:
+- return err;
+-}
+-
+-static int max9286_remove(struct i2c_client *client)
+-{
+- struct max9286_priv *priv = i2c_get_clientdata(client);
+- int i;
+-
+- sysfs_remove_group(&client->dev.kobj, &max9286_group);
+- unregister_reboot_notifier(&priv->reboot_notifier);
+-
+- 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/max9288.c b/drivers/media/i2c/soc_camera/max9288.c
+deleted file mode 100644
+index 4840795..0000000
+--- a/drivers/media/i2c/soc_camera/max9288.c
++++ /dev/null
+@@ -1,768 +0,0 @@
+-/*
+- * MAXIM max9288 GMSL driver
+- *
+- * Copyright (C) 2019 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-common.h>
+-#include <media/v4l2-device.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 max9288_priv {
+- struct v4l2_subdev sd;
+- struct fwnode_handle *sd_fwnode;
+- int des_addr;
+- int lanes;
+- int csi_rate;
+- int pclk;
+- char pclk_rising_edge;
+- int gpio_resetb;
+- int active_low_resetb;
+- int him;
+- int hsync;
+- int vsync;
+- int timeout;
+- int poc_delay;
+- int bws;
+- int dbl;
+- int dt;
+- int hsgen;
+- int hts;
+- int vts;
+- int hts_delay;
+- struct i2c_client *client;
+- int max9271_addr;
+- int ser_id;
+- struct gpio_desc *poc_gpio; /* PoC power supply */
+-};
+-
+-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 = 0;
+-module_param(him, int, 0644);
+-MODULE_PARM_DESC(him, " Use High-Immunity mode (default: leagacy mode)");
+-
+-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 timeout_n = 100;
+-module_param(timeout_n, int, 0644);
+-MODULE_PARM_DESC(timeout_n, " Timeout of link detection (default: 100 retries)");
+-
+-static int poc_delay = 50;
+-module_param(poc_delay, int, 0644);
+-MODULE_PARM_DESC(poc_delay, " Delay in ms after POC enable (default: 50 ms)");
+-
+-static int bws = 0;
+-module_param(bws, int, 0644);
+-MODULE_PARM_DESC(bws, " BWS mode (default: 0 - 24-bit gmsl packets)");
+-
+-static int dbl = 1;
+-module_param(dbl, int, 0644);
+-MODULE_PARM_DESC(dbl, " DBL mode (default: 1 - DBL mode enabled)");
+-
+-static int dt = 3;
+-module_param(dt, int, 0644);
+-MODULE_PARM_DESC(dt, " DataType (default: 3 - YUV8), 0 - RGB888, 5 - RAW8, 6 - RAW10, 7 - RAW12, 8 - RAW14");
+-
+-static int hsgen;
+-module_param(hsgen, int, 0644);
+-MODULE_PARM_DESC(hsgen, " Enable HS embedded generator (default: 0 - disabled)");
+-
+-static int pclk = 100;
+-module_param(pclk, int, 0644);
+-MODULE_PARM_DESC(pclk, " PCLK rate (default: 100MHz)");
+-
+-enum {
+- RGB888_DT = 0,
+- RGB565_DT,
+- RGB666_DT,
+- YUV8_DT, /* default */
+- YUV10_DT,
+- RAW8_DT,
+- RAW10_DT,
+- RAW12_DT,
+- RAW14_DT,
+-};
+-
+-static int dt2bpp [9] = {
+- 24, /* RGB888 */
+- 16, /* RGB565 */
+- 18, /* RGB666 */
+- 8, /* YUV8 - default */
+- 10, /* YUV10 */
+- 8, /* RAW8/RAW16 */
+- 10, /* RAW10 */
+- 12, /* RAW12 */
+- 14, /* RAW14 */
+-};
+-
+-static char* ser_name(int id)
+-{
+- switch (id) {
+- case MAX9271_ID:
+- return "MAX9271";
+- case MAX96705_ID:
+- return "MAX96705";
+- case MAX96707_ID:
+- return "MAX96707";
+- default:
+- return "unknown";
+- }
+-}
+-
+-static void max9288_write_remote_verify(struct i2c_client *client, u8 reg, u8 val)
+-{
+- struct max9288_priv *priv = i2c_get_clientdata(client);
+- int timeout;
+-
+- for (timeout = 0; timeout < 10; timeout++) {
+- u8 val2 = 0;
+-
+- reg8_write(client, reg, val);
+- reg8_read(client, reg, &val2);
+- if (val2 == val)
+- break;
+-
+- usleep_range(1000, 1500);
+- }
+-
+- if (timeout >= 10)
+- dev_err(&client->dev, "timeout remote write acked\n");
+-}
+-
+-
+-static void max9288_preinit(struct i2c_client *client, int addr)
+-{
+-
+- struct max9288_priv *priv = i2c_get_clientdata(client);
+-
+- client->addr = addr; /* MAX9288-CAMx I2C */
+- reg8_write(client, 0x04, 0x00); /* disable reverse control */
+- reg8_write(client, 0x16, (priv->him ? 0x80 : 0x00) |
+- 0x5a); /* high-immunity/legacy mode */
+-}
+-
+-static void max9288_sensor_reset(struct i2c_client *client, int addr, int reset_on)
+-{
+- struct max9288_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 int max9288_reverse_channel_setup(struct i2c_client *client)
+-{
+- struct max9288_priv *priv = i2c_get_clientdata(client);
+- u8 val = 0, lock_sts = 0;
+- int timeout = priv->timeout;
+- char timeout_str[40];
+- int ret = 0;
+-
+- /* Reverse channel enable */
+- client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
+- reg8_write(client, 0x1c, 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, 0x04, 0x03); /* enable reverse control */
+- usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
+-
+- for (;;) {
+- if (priv->him) {
+- /* HIM mode setup */
+- client->addr = 0x40; /* MAX9271-CAMx I2C */
+- reg8_write(client, 0x4d, 0xc0);
+- usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
+- 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 */
+- if (priv->bws) {
+- reg8_write(client, 0x07, (priv->pclk_rising_edge ? 0 : 0x10) |
+- (priv->dbl ? 0x80 : 0) |
+- (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS disabled enabled, DBL mode, BWS 24/32-bit */
+- usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
+- }
+- } else {
+- /* Legacy mode setup */
+- client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
+- reg8_write(client, 0x13, 0x00);
+- reg8_write(client, 0x11, 0x42); /* enable custom reverse channel & first pulse length */
+- reg8_write(client, 0x0a, 0x0f); /* 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 */
+- usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
+- if (priv->bws) {
+- reg8_write(client, 0x07, (priv->pclk_rising_edge ? 0 : 0x10) |
+- (priv->dbl ? 0x80 : 0) |
+- (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS encoding disabled, DBL mode, BWS 24/32-bit */
+- usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */
+- }
+- reg8_write(client, 0x97, 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; /* MAX9288-CAMx I2C */
+- reg8_write(client, 0x0a, 0x0c); /* first pulse length rise time changed from 300ns to 200ns, amplitude 100mV */
+- reg8_write(client, 0x13, 0x20); /* 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 || val == MAX96707_ID || --timeout == 0) {
+- priv->ser_id = val;
+- break;
+- }
+-
+- /* Check if already initialized (after reboot/reset ?) */
+- client->addr = priv->max9271_addr; /* MAX9271-CAMx I2C */
+- reg8_read(client, 0x1e, &val); /* read max9271 ID */
+- if (val == MAX9271_ID || val == MAX96705_ID || val == MAX96707_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 (poc_trig) {
+- if (!IS_ERR(priv->poc_gpio) && (timeout % poc_trig == 0)) {
+- gpiod_direction_output(priv->poc_gpio, 0); /* POC power off */
+- mdelay(200);
+- gpiod_direction_output(priv->poc_gpio, 1); /* POC power on */
+- mdelay(priv->poc_delay);
+- }
+- }
+- }
+-
+- max9288_sensor_reset(client, client->addr, 1); /* sensor reset */
+-
+- client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
+- reg8_read(client, 0x04, &lock_sts); /* LOCK status */
+-
+- if (!timeout) {
+- ret = -ETIMEDOUT;
+- goto out;
+- }
+-
+-out:
+- sprintf(timeout_str, "retries=%d lock_sts=%d", priv->timeout - timeout, !!(lock_sts & 0x80));
+- dev_info(&client->dev, "link %s %sat 0x%x %s %s\n", ser_name(priv->ser_id),
+- ret == -EADDRINUSE ? "already " : "", priv->max9271_addr,
+- ret == -ETIMEDOUT ? "not found: timeout GMSL link establish" : "",
+- priv->timeout - timeout ? timeout_str : "");
+-
+- return ret;
+-}
+-
+-static void max9288_initial_setup(struct i2c_client *client)
+-{
+- struct max9288_priv *priv = i2c_get_clientdata(client);
+-
+- /* Initial setup */
+- client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
+- reg8_write(client, 0x09, 0x40); /* Automatic pixel count enable */
+- reg8_write(client, 0x15, 0x70); /* Enable HV and DE tracking by register 0x69 */
+- reg8_write(client, 0x60, (priv->dbl ? 0x20 : 0) |
+- (priv->dt & 0xf)); /* VC=0, DBL mode, DataType */
+- reg8_write(client, 0x65, 0x47 | ((priv->lanes - 1) << 4)); /* setup CSI lanes, DE input is HS */
+-
+- reg8_write(client, 0x08, 0x20); /* use D18/19 for HS/VS */
+- reg8_write(client, 0x14, (priv->vsync ? 0x80 : 0) | (priv->hsync ? 0x40 : 0)); /* setup HS/VS inversion */
+- reg8_write(client, 0x64, 0x0c); /* Drive HSTRAIL state for 120ns after the last payload bit */
+-}
+-
+-static void max9288_gmsl_link_setup(struct i2c_client *client)
+-{
+- struct max9288_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, (priv->pclk_rising_edge ? 0 : 0x10) |
+- (priv->dbl ? 0x80 : 0) |
+- (priv->bws ? 0x20 : 0)); /* RAW/YUV, PCLK edge, HS/VS encoding disabled, DBL mode, BWS 24/32-bit */
+- 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 || priv->ser_id == MAX96707_ID) {
+- switch (priv->dt) {
+- case YUV8_DT:
+- /* setup crossbar for YUV8/RAW8: reverse DVP bus */
+- reg8_write(client, 0x20, 3);
+- reg8_write(client, 0x21, 4);
+- reg8_write(client, 0x22, 5);
+- reg8_write(client, 0x23, 6);
+- reg8_write(client, 0x24, 7);
+- reg8_write(client, 0x25, 0x40);
+- reg8_write(client, 0x26, 0x40);
+- if (priv->ser_id == MAX96705_ID) {
+- reg8_write(client, 0x27, 14); /* HS: D14->D18 */
+- reg8_write(client, 0x28, 15); /* VS: D15->D19 */
+- }
+- if (priv->ser_id == MAX96707_ID) {
+- reg8_write(client, 0x27, 14); /* HS: D14->D18, this is a virtual NC pin, hence it is D14 at HS */
+- reg8_write(client, 0x28, 13); /* VS: D13->D19 */
+- }
+- reg8_write(client, 0x29, 0x40);
+- reg8_write(client, 0x2A, 0x40);
+-
+- /* this is second byte if DBL=1 */
+- reg8_write(client, 0x30, 0x10 + 0);
+- reg8_write(client, 0x31, 0x10 + 1);
+- reg8_write(client, 0x32, 0x10 + 2);
+- reg8_write(client, 0x33, 0x10 + 3);
+- reg8_write(client, 0x34, 0x10 + 4);
+- reg8_write(client, 0x35, 0x10 + 5);
+- reg8_write(client, 0x36, 0x10 + 6);
+- reg8_write(client, 0x37, 0x10 + 7);
+- reg8_write(client, 0x38, 0);
+- reg8_write(client, 0x39, 1);
+- reg8_write(client, 0x3A, 2);
+-
+- reg8_write(client, 0x67, 0xC4); /* DBL_ALIGN_TO = 100b */
+-
+- break;
+- case RAW12_DT:
+-#if 0 /* Not supported yet */
+- /* setup crossbar for RAW12: reverse DVP bus */
+- reg8_write(client, 0x20, 11);
+- reg8_write(client, 0x21, 10);
+- reg8_write(client, 0x22, 9);
+- reg8_write(client, 0x23, 8);
+- reg8_write(client, 0x24, 7);
+- reg8_write(client, 0x25, 6);
+- reg8_write(client, 0x26, 5);
+- reg8_write(client, 0x27, 4);
+- reg8_write(client, 0x28, 3);
+- reg8_write(client, 0x29, 2);
+- reg8_write(client, 0x2a, 1);
+- reg8_write(client, 0x2b, 0);
+-
+- /* this is second byte if DBL=1 */
+- reg8_write(client, 0x30, 27);
+- reg8_write(client, 0x31, 26);
+- reg8_write(client, 0x32, 25);
+- reg8_write(client, 0x33, 24);
+- reg8_write(client, 0x34, 23);
+- reg8_write(client, 0x35, 22);
+- reg8_write(client, 0x36, 21);
+- reg8_write(client, 0x37, 20);
+- reg8_write(client, 0x38, 19);
+- reg8_write(client, 0x39, 18);
+- reg8_write(client, 0x3a, 17);
+- reg8_write(client, 0x3b, 16);
+-
+- if (!priv->bws && priv->dbl)
+- dev_err(&client->dev, " BWS must be 27/32-bit for RAW12 in DBL mode\n");
+-#endif
+- break;
+- }
+-
+- if (priv->hsgen) {
+- /* HS/VS pins map */
+- reg8_write(client, 0x3f, 0x10); /* HS (NC) */
+- reg8_write(client, 0x41, 0x10); /* DE (NC) */
+- if (priv->ser_id == MAX96705_ID)
+- reg8_write(client, 0x40, 15); /* VS (DIN13) */
+- if (priv->ser_id == MAX96707_ID)
+- reg8_write(client, 0x40, 13); /* VS (DIN13) */
+-#if 0
+- /* following must come from imager */
+-#define SENSOR_WIDTH (1280*2)
+-#define HTS (1288*2)
+-#define VTS 960
+-#define HTS_DELAY 0x9
+- reg8_write(client, 0x4e, HTS_DELAY >> 16); /* HS delay */
+- reg8_write(client, 0x4f, (HTS_DELAY >> 8) & 0xff);
+- reg8_write(client, 0x50, HTS_DELAY & 0xff);
+- reg8_write(client, 0x54, SENSOR_WIDTH >> 8); /* HS high period */
+- reg8_write(client, 0x55, SENSOR_WIDTH & 0xff);
+- reg8_write(client, 0x56, (HTS - SENSOR_WIDTH) >> 8); /* HS low period */
+- reg8_write(client, 0x57, (HTS - SENSOR_WIDTH) & 0xff);
+- reg8_write(client, 0x58, VTS >> 8); /* HS count */
+- reg8_write(client, 0x59, VTS & 0xff );
+-#endif
+- reg8_write(client, 0x43, 0x15); /* enable HS generator */
+- }
+- }
+-
+- client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
+- reg8_write(client, 0x1c, 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 << 1); /* MAX9271-CAMx I2C new */
+- /* I2C addresse change */
+- reg8_write(client, 0x01, priv->des_addr << 1); /* MAX9288 I2C */
+- reg8_write(client, 0x00, priv->max9271_addr << 1); /* MAX9271-CAM0 I2C new */
+- usleep_range(2000, 2500); /* wait 2ms */
+- /* put MAX9271 in configuration link state */
+- client->addr = priv->max9271_addr; /* 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; /* MAX9288-CAMx I2C */
+- maxim_max927x_dump_regs(client);
+- client->addr = priv->max9271_addr; /* MAX9271-CAMx I2C new */
+- maxim_max927x_dump_regs(client);
+-#endif
+-}
+-
+-static int max9288_initialize(struct i2c_client *client)
+-{
+- struct max9288_priv *priv = i2c_get_clientdata(client);
+-
+- dev_info(&client->dev, "LANES=%d, PCLK edge=%s\n",
+- priv->lanes, priv->pclk_rising_edge ? "rising" : "falling");
+-
+- max9288_preinit(client, priv->des_addr);
+- max9288_initial_setup(client);
+-
+- if (!IS_ERR(priv->poc_gpio)) {
+- gpiod_direction_output(priv->poc_gpio, 1); /* POC power on */
+- mdelay(priv->poc_delay);
+- }
+-
+- max9288_reverse_channel_setup(client);
+- max9288_gmsl_link_setup(client);
+-
+- client->addr = priv->des_addr;
+-
+- return 0;
+-}
+-
+-#ifdef CONFIG_VIDEO_ADV_DEBUG
+-static int max9288_g_register(struct v4l2_subdev *sd,
+- struct v4l2_dbg_register *reg)
+-{
+- struct max9288_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 max9288_s_register(struct v4l2_subdev *sd,
+- const struct v4l2_dbg_register *reg)
+-{
+- struct max9288_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 max9288_s_power(struct v4l2_subdev *sd, int on)
+-{
+- struct max9288_priv *priv = v4l2_get_subdevdata(sd);
+- struct i2c_client *client = priv->client;
+-
+- client->addr = priv->max9271_addr; /* MAX9271-CAMx I2C new */
+- max9288_write_remote_verify(client, 0x04, on ? (conf_link ? 0x43 : 0x83) : 0x43); /* enable serial_link or conf_link */
+- usleep_range(2000, 2500); /* wait 2ms after changing reverse_control */
+- client->addr = priv->des_addr; /* MAX9288-CAMx I2C */
+-
+- return 0;
+-}
+-
+-static struct v4l2_subdev_core_ops max9288_subdev_core_ops = {
+-#ifdef CONFIG_VIDEO_ADV_DEBUG
+- .g_register = max9288_g_register,
+- .s_register = max9288_s_register,
+-#endif
+- .s_power = max9288_s_power,
+-};
+-
+-static struct v4l2_subdev_ops max9288_subdev_ops = {
+- .core = &max9288_subdev_core_ops,
+-};
+-
+-static int max9288_parse_dt(struct i2c_client *client)
+-{
+- struct max9288_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;
+- int sensor_delay, gpio0 = 1, gpio1 = 1;
+- u8 val = 0;
+- char poc_name[10];
+-
+- 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);
+-
+- sprintf(poc_name, "POC%d", 0);
+- priv->poc_gpio = devm_gpiod_get_optional(&client->dev, kstrdup(poc_name, GFP_KERNEL), 0);
+-
+- reg8_read(client, 0x1e, &val); /* read max9288 ID */
+- if (val != MAX9288_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, 0x06, (gpio1 << 3) | (gpio0 << 1));
+-
+- 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);
+- 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,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;
+- if (of_property_read_u32(np, "maxim,bws", &priv->bws))
+- priv->bws = 0;
+- if (of_property_read_u32(np, "maxim,dbl", &priv->dbl))
+- priv->dbl = 1;
+- if (of_property_read_u32(np, "maxim,dt", &priv->dt))
+- priv->dt = 3;
+- if (of_property_read_u32(np, "maxim,hsgen", &priv->hsgen))
+- priv->hsgen = 0;
+- if (of_property_read_u32(np, "maxim,pclk", &priv->pclk))
+- priv->pclk = pclk;
+-
+- /* module params override dts */
+- if (him)
+- priv->him = him;
+- 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 (timeout_n)
+- priv->timeout = timeout_n;
+- if (poc_delay)
+- priv->poc_delay = poc_delay;
+- if (bws)
+- priv->bws = bws;
+- if (!dbl)
+- priv->dbl = dbl;
+- if (dt != 3)
+- priv->dt = dt;
+- if (hsgen)
+- priv->hsgen = hsgen;
+- if (pclk != 100)
+- priv->pclk = pclk;
+-
+- endpoint = of_graph_get_next_endpoint(np, endpoint);
+- if (endpoint) {
+- if (of_property_read_u32(endpoint, "max9271-addr", &priv->max9271_addr)) {
+- of_node_put(endpoint);
+- dev_err(&client->dev, "max9271-addr not set\n");
+- return -EINVAL;
+- }
+-
+- priv->sd_fwnode = of_fwnode_handle(endpoint);
+- }
+-
+- of_node_put(endpoint);
+- return 0;
+-}
+-
+-static void max9288_setup_remote_endpoint(struct i2c_client *client)
+-{
+- struct max9288_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;
+-
+- 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*bpp/lanes */
+- priv->csi_rate = cpu_to_be32(priv->pclk * dt2bpp[priv->dt] / 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);
+- }
+-
+- of_node_put(endpoint);
+-}
+-
+-static int max9288_probe(struct i2c_client *client,
+- const struct i2c_device_id *did)
+-{
+- struct max9288_priv *priv;
+- int err;
+-
+- 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;
+-
+- err = max9288_parse_dt(client);
+- if (err)
+- goto out;
+-
+- err = max9288_initialize(client);
+- if (err < 0)
+- goto out;
+-
+- max9288_setup_remote_endpoint(client);
+-
+- v4l2_subdev_init(&priv->sd, &max9288_subdev_ops);
+- priv->sd.owner = client->dev.driver->owner;
+- priv->sd.dev = &client->dev;
+- v4l2_set_subdevdata(&priv->sd, priv);
+- priv->sd.fwnode = priv->sd_fwnode;
+-
+- snprintf(priv->sd.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);
+- if (err < 0)
+- goto out;
+-out:
+- return err;
+-}
+-
+-static int max9288_remove(struct i2c_client *client)
+-{
+- struct max9288_priv *priv = i2c_get_clientdata(client);
+-
+- v4l2_async_unregister_subdev(&priv->sd);
+- v4l2_device_unregister_subdev(&priv->sd);
+-
+- return 0;
+-}
+-
+-static const struct of_device_id max9288_dt_ids[] = {
+- { .compatible = "maxim,max9288" },
+- {},
+-};
+-MODULE_DEVICE_TABLE(of, max9288_dt_ids);
+-
+-static const struct i2c_device_id max9288_id[] = {
+- { "max9288", 0 },
+- { }
+-};
+-MODULE_DEVICE_TABLE(i2c, max9288_id);
+-
+-static struct i2c_driver max9288_i2c_driver = {
+- .driver = {
+- .name = "max9288",
+- .of_match_table = of_match_ptr(max9288_dt_ids),
+- },
+- .probe = max9288_probe,
+- .remove = max9288_remove,
+- .id_table = max9288_id,
+-};
+-
+-module_i2c_driver(max9288_i2c_driver);
+-
+-MODULE_DESCRIPTION("GMSL driver for MAX9288");
+-MODULE_AUTHOR("Vladimir Barinov");
+-MODULE_LICENSE("GPL");
+diff --git a/drivers/media/i2c/soc_camera/ti9x4.c b/drivers/media/i2c/soc_camera/ti9x4.c
+deleted file mode 100644
+index 340e61e..0000000
+--- a/drivers/media/i2c/soc_camera/ti9x4.c
++++ /dev/null
+@@ -1,745 +0,0 @@
+- /*
+- * 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/reboot.h>
+-#include <linux/videodev2.h>
+-
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-clk.h>
+-#include <media/v4l2-device.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 fs_time;
+- int fps_numerator;
+- int fps_denominator;
+- int is_coax;
+- int dvp_bus;
+- int dvp_lsb;
+- 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;
+- int vc_map;
+- int csi_map;
+- int gpio[4];
+- struct gpio_desc *pwen; /* chip power en */
+- struct gpio_desc *poc_gpio[4]; /* PoC power supply */
+- struct v4l2_clk *ref_clk; /* ref clock */
+- struct notifier_block reboot_notifier;
+-};
+-
+-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 dvp_lsb = 0;
+-module_param(dvp_lsb, int, 0644);
+-MODULE_PARM_DESC(dvp_lsb, " DVP 8-bit LSB/MSB selection (default: DVP 8-bit MSB)");
+-
+-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)");
+-
+-static int vc_map = 0x3210;
+-module_param(vc_map, int, 0644);
+-MODULE_PARM_DESC(vc_map, " CSI VC MAP (default: 0xe4 - linear map VCx=LINKx)");
+-
+-static int csi_map = 0;
+-module_param(csi_map, int, 0644);
+-MODULE_PARM_DESC(csi_map, " CSI TX MAP (default: 0 - forwarding of all links to CSI0)");
+-
+-static int gpio0 = 0, gpio1 = 0, gpio2 = 0, gpio3 = 0;
+-module_param(gpio0, int, 0644);
+-MODULE_PARM_DESC(gpio0, " GPIO0 function select (default: GPIO0 low level)");
+-module_param(gpio1, int, 0644);
+-MODULE_PARM_DESC(gpio1, " GPIO1 function select (default: GPIO1 low level)");
+-module_param(gpio2, int, 0644);
+-MODULE_PARM_DESC(gpio2, " GPIO2 function select (default: GPIO2 low level)");
+-module_param(gpio3, int, 0644);
+-MODULE_PARM_DESC(gpio3, " GPIO3 function select (default: GPIO3 low level)");
+-
+-#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, 0x0d, 0xb9); /* VDDIO 3.3V */
+- switch (priv->csi_rate) {
+- case 1600: /* REFCLK = 25MHZ */
+- case 1500: /* REFCLK = 23MHZ */
+- case 1450: /* REFCLK = 22.5MHZ */
+- reg8_write(client, 0x1f, 0x00); /* CSI rate 1.5/1.6Gbps */
+- break;
+- case 1200: /* REFCLK = 25MHZ */
+- case 1100: /* REFCLK = 22.5MHZ */
+- reg8_write(client, 0x1f, 0x01); /* CSI rate 1.1/1.2Gbps */
+- break;
+- case 800: /* REFCLK = 25MHZ */
+- case 700: /* REFCLK = 22.5MHZ */
+- reg8_write(client, 0x1f, 0x02); /* CSI rate 700/800Mbps */
+- break;
+- case 400: /* REFCLK = 25MHZ */
+- case 350: /* REFCLK = 22.5MHZ */
+- reg8_write(client, 0x1f, 0x03); /* CSI rate 350/400Mbps */
+- break;
+- default:
+- dev_err(&client->dev, "unsupported CSI rate %d\n", priv->csi_rate);
+- }
+-
+- switch (priv->csi_rate) {
+- case 1600:
+- case 1200:
+- case 800:
+- case 400:
+- /* FrameSync setup for REFCLK=25MHz, FPS=30: period_counts=1/FPS/12mks=1/30/12e-6=2777 -> HI=2, LO=2775 */
+- priv->fs_time = 2790;
+- break;
+- case 1500:
+- /* FrameSync setup for REFCLK=23MHz, FPS=30: period_counts=1/FPS/13.043mks=1/30/13.043e-6=2556 -> HI=2, LO=2554 */
+- priv->fs_time = 2570;
+- break;
+- case 1450:
+- case 1100:
+- case 700:
+- case 350:
+- /* FrameSync setup for REFCLK=22.5MHz, FPS=30: period_counts=1/FPS/13.333mks=1/30/13.333e-6=2500 -> HI=2, LO=2498 */
+- priv->fs_time = 2513;
+- break;
+- default:
+- priv->fs_time = 0;
+- dev_err(&client->dev, "unsupported CSI rate %d\n", priv->csi_rate);
+- }
+-
+- if (strcmp(priv->forwarding_mode, "round-robin") == 0) {
+- reg8_write(client, 0x21, 0x03); /* Round Robin forwarding enable for CSI0/CSI1 */
+- } else if (strcmp(priv->forwarding_mode, "synchronized") == 0) {
+- reg8_write(client, 0x21, 0x54); /* Basic Syncronized forwarding enable (FrameSync must be enabled!!) for CSI0/CSI1 */
+- }
+-
+- reg8_write(client, 0x32, 0x03); /* Select TX for CSI0/CSI1, RX for CSI0 */
+- 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 | priv->csi_map); /* 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
+- reg8_write(client, 0x19, 2 >> 8); /* FrameSync high time MSB */
+- reg8_write(client, 0x1a, 2 & 0xff); /* FrameSync high time LSB */
+- reg8_write(client, 0x1b, priv->fs_time >> 8); /* FrameSync low time MSB */
+- reg8_write(client, 0x1c, priv->fs_time & 0xff); /* FrameSync low time LSB */
+- reg8_write(client, 0x18, 0x00); /* Disable FrameSync - must be enabled after all cameras are set up */
+-#endif
+-}
+-
+-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, 0x30 << 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 |= (priv->dvp_lsb ? 0xC0 : 0x80); /* RAW10 as 8-bit prosessing using LSB/MSB 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, ((priv->vc_map >> (idx * 4)) << 6) | 0x1e); /* CSI data type: yuv422 8-bit, assign VC */
+- reg8_write(client, 0x71, ((priv->vc_map >> (idx * 4)) << 6) | 0x2c); /* CSI data type: RAW12, assign VC */
+- reg8_write(client, 0xbc, 0x00); /* Setup minimal time between FV and LV to 3 PCLKs */
+- reg8_write(client, 0x72, priv->vc_map >> (idx * 4)); /* CSI VC MAP */
+-}
+-
+-static int ti9x4_initialize(struct i2c_client *client)
+-{
+- struct ti9x4_priv *priv = i2c_get_clientdata(client);
+- int idx, timeout;
+- u8 port_sts1[4] = {0, 0, 0, 0}, port_sts2[4] = {0, 0, 0, 0};
+-
+- 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;
+-
+- /* check lock status */
+- for (timeout = 500 / priv->links; timeout > 0; timeout--) {
+- for (idx = 0; idx < priv->links; idx++) {
+- if ((port_sts1[idx] & 0x1) && (port_sts2[idx] & 0x4))
+- continue;
+-
+- reg8_write(client, 0x4c, (idx << 4) | (1 << idx)); /* Select RX port number */
+- usleep_range(1000, 1500); /* wait 1ms */
+- reg8_read(client, 0x4d, &port_sts1[idx]); /* Lock status */
+- reg8_read(client, 0x4e, &port_sts2[idx]); /* Freq stable */
+- }
+- }
+-
+- if (!timeout)
+- dev_info(&client->dev, "Receiver lock status [%d,%d,%d,%d]\n",
+- (port_sts1[0] & 0x1) && (port_sts2[0] & 0x4),
+- (port_sts1[1] & 0x1) && (port_sts2[1] & 0x4),
+- (port_sts1[2] & 0x1) && (port_sts2[2] & 0x4),
+- (port_sts1[3] & 0x1) && (port_sts2[3] & 0x4));
+-
+- if (priv->poc_delay)
+- mdelay(100);
+-
+- for (idx = 0; idx < priv->links; idx++) {
+- reg8_write(client, 0x4c, (idx << 4) | (1 << idx)); /* Select RX port number */
+- usleep_range(1000, 1500); /* wait 1ms */
+-
+- /*
+- * Enable only FSIN for remote gpio, all permanent states (0 or 1) setup on serializer side:
+- * this avoids intermittent remote gpio noise (f.e. reset or spuriouse fsin) caused by
+- * unstable/bad link, hence unstable backchannel
+- */
+- client->addr = priv->ti9x3_addr_map[idx]; /* TI9X3 I2C addr */
+- switch (priv->ser_id) {
+- case TI913_ID:
+- reg8_write(client, 0x0d, 0x55); /* Enable remote GPIO0/1 */
+- reg8_write(client, 0x11, 0x10); /* I2C high pulse width */
+- reg8_write(client, 0x12, 0x10); /* I2C low pulse width */
+- break;
+- case TI953_ID:
+- reg8_write(client, 0x0d, (priv->gpio[0] & 0x1) << 0 |
+- (priv->gpio[1] & 0x1) << 1 |
+- (priv->gpio[2] & 0x1) << 2 |
+- (priv->gpio[3] & 0x1) << 3 |
+- (priv->gpio[0] & 0x2) << 3 |
+- (priv->gpio[1] & 0x2) << 4 |
+- (priv->gpio[2] & 0x2) << 5 |
+- (priv->gpio[3] & 0x2) << 6); /* Enable FSIN remote GPIOs and set local constant gpios */
+- reg8_write(client, 0x0e, (!!priv->gpio[0] << 4) |
+- (!!priv->gpio[1] << 5) |
+- (!!priv->gpio[2] << 6) |
+- (!!priv->gpio[3] << 7)); /* Enable serializer GPIOs only for output */
+- reg8_write(client, 0x0b, 0x10); /* I2C high pulse width */
+- reg8_write(client, 0x0c, 0x10); /* I2C low pulse width */
+- break;
+- }
+- client->addr = priv->des_addr;
+-
+- reg8_write(client, 0x6e, 0x88 | (priv->gpio[1] << 4) | priv->gpio[0]); /* Remote GPIO1/GPIO0 setup */
+- reg8_write(client, 0x6f, 0x88 | (priv->gpio[3] << 4) | priv->gpio[2]); /* Remote GPIO3/GPIO2 setup */
+- }
+-
+- 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 | priv->csi_map); /* enable port forwarding to CSI */
+- } else {
+- if (atomic_dec_return(&priv->use_count) == 0)
+- reg8_write(client, 0x20, 0xf0 | priv->csi_map); /* 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 */
+- reg8_write(client, 0x18, 0x01); /* Enable FrameSync, HI/LO mode, Frame clock from port0 */
+-// reg8_write(client, 0x18, 0x80); /* Enable FrameSync, Frame clock is external */
+-
+- return 0;
+-}
+-
+-static int ti9x4_reboot_notifier(struct notifier_block *nb, unsigned long event, void *buf)
+-{
+- struct ti9x4_priv *priv = container_of(nb, struct ti9x4_priv, reboot_notifier);
+- int idx;
+-
+- for (idx = 0; idx < priv->links; idx++) {
+- if (!IS_ERR(priv->poc_gpio[idx]))
+- gpiod_direction_output(priv->poc_gpio[idx], 0); /* POC power off */
+- }
+-
+- return NOTIFY_OK;
+-}
+-
+-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 int ti9x4_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+-{
+- return 0;
+-}
+-
+-static int ti9x4_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+-{
+- struct ti9x4_priv *priv = v4l2_get_subdevdata(sd);
+- struct i2c_client *client = priv->client;
+- struct v4l2_captureparm *cp = &parms->parm.capture;
+-
+- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+- if (cp->extendedmode != 0)
+- return -EINVAL;
+-
+- if (priv->fps_denominator != cp->timeperframe.denominator ||
+- priv->fps_numerator != cp->timeperframe.numerator) {
+- int f_time;
+-
+- f_time = priv->fs_time * 30 * cp->timeperframe.numerator / cp->timeperframe.denominator;
+- reg8_write(client, 0x1b, f_time >> 8); /* FrameSync low time MSB */
+- reg8_write(client, 0x1c, f_time & 0xff); /* FrameSync low time LSB */
+-
+- priv->fps_denominator = cp->timeperframe.denominator;
+- priv->fps_numerator = cp->timeperframe.numerator;
+- }
+-
+- return 0;
+-}
+-
+-static struct v4l2_subdev_video_ops ti9x4_video_ops = {
+- .g_parm = ti9x4_g_parm,
+- .s_parm = ti9x4_s_parm,
+-};
+-
+-static struct v4l2_subdev_ops ti9x4_subdev_ops = {
+- .core = &ti9x4_subdev_core_ops,
+- .video = &ti9x4_video_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 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 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;
+-
+- priv->ref_clk = v4l2_clk_get(&client->dev, "ref_clk");
+- if (!IS_ERR(priv->ref_clk)) {
+- dev_info(&client->dev, "ref_clk = %luKHz", v4l2_clk_get_rate(priv->ref_clk) / 1000);
+- v4l2_clk_enable(priv->ref_clk);
+- }
+-
+- priv->pwen = devm_gpiod_get(&client->dev, NULL, GPIOF_OUT_INIT_HIGH);
+- if (!IS_ERR(priv->pwen)) {
+- mdelay(5);
+- gpiod_direction_output(priv->pwen, 0);
+- mdelay(5);
+- }
+-
+- for (i = 0; i < 4; i++) {
+- sprintf(name, "POC%d", i);
+- priv->poc_gpio[i] = devm_gpiod_get_optional(&client->dev, kstrdup(name, GFP_KERNEL), 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_bool(np, "ti,dvp_lsb"))
+- priv->dvp_lsb = 1;
+- else
+- priv->dvp_lsb = 0;
+- if (of_property_read_u32(np, "ti,hsync", &priv->hsync))
+- priv->hsync = 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 = 10;
+- if (of_property_read_u32(np, "ti,vc-map", &priv->vc_map))
+- priv->vc_map = 0x3210;
+- for (i = 0; i < 4; i++) {
+- sprintf(name, "ti,gpio%d", i);
+- if (of_property_read_u32(np, name, &priv->gpio[i]))
+- priv->gpio[i] = 0;
+- }
+-
+- /*
+- * CSI forwarding of all links is to CSI0 by default.
+- * Decide if any link will be forwarded to CSI1 instead CSI0
+- */
+- prop = of_find_property(np, "ti,csi1-links", NULL);
+- if (prop) {
+- const __be32 *link = NULL;
+- u32 v;
+-
+- for (i = 0; i < 4; i++) {
+- link = of_prop_next_u32(prop, link, &v);
+- if (!link)
+- break;
+- priv->csi_map |= BIT(v);
+- }
+- } else {
+- priv->csi_map = 0;
+- }
+-
+- /* module params override dts */
+- if (is_stp)
+- priv->is_coax = 0;
+- if (dvp_bus != 8)
+- priv->dvp_bus = dvp_bus;
+- if (dvp_lsb)
+- priv->dvp_lsb = dvp_lsb;
+- 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;
+- if (vc_map != 0x3210)
+- priv->vc_map = vc_map;
+- if (csi_map)
+- priv->csi_map = csi_map;
+- if (gpio0)
+- priv->gpio[0] = gpio0;
+- if (gpio1)
+- priv->gpio[1] = gpio1;
+- if (gpio2)
+- priv->gpio[2] = gpio2;
+- if (gpio3)
+- priv->gpio[3] = gpio3;
+-
+- for (i = 0; ; i++) {
+- endpoint = of_graph_get_next_endpoint(np, endpoint);
+- if (!endpoint)
+- break;
+-
+- if (i < priv->links) {
+- if (of_property_read_u32(endpoint, "ti9x3-addr", &priv->ti9x3_addr_map[i])) {
+- of_node_put(endpoint);
+- 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);
+- }
+-
+- of_node_put(endpoint);
+- 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);
+- priv->fps_numerator = 1;
+- priv->fps_denominator = 30;
+-
+- 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;
+- }
+-
+- priv->reboot_notifier.notifier_call = ti9x4_reboot_notifier;
+- err = register_reboot_notifier(&priv->reboot_notifier);
+- if (err)
+- dev_err(&client->dev, "failed to register reboot notifier\n");
+-
+-out:
+- return err;
+-}
+-
+-static int ti9x4_remove(struct i2c_client *client)
+-{
+- struct ti9x4_priv *priv = i2c_get_clientdata(client);
+- int i;
+-
+- unregister_reboot_notifier(&priv->reboot_notifier);
+-
+- 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
+deleted file mode 100644
+index 6825f8a..0000000
+--- a/drivers/media/i2c/soc_camera/ti9x4.h
++++ /dev/null
+@@ -1,204 +0,0 @@
+-/*
+- * 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_read_n(struct i2c_client *client, u16 reg, u8 *val, int n)
+-{
+- 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, val, n);
+- if (ret == n)
+- break;
+- }
+- }
+-
+- if (ret < 0) {
+- dev_dbg(&client->dev,
+- "read fail: chip 0x%x registers 0x%x-0x%x: %d\n",
+- client->addr, reg, reg + n, ret);
+- }
+-
+- return ret < 0 ? ret : 0;
+-}
+-
+-static inline int reg16_write_n(struct i2c_client *client, u16 reg, const u8* val, int n)
+-{
+- int ret, retries;
+- u8 buf[2 + n];
+-
+- buf[0] = reg >> 8;
+- buf[1] = reg & 0xff;
+- memcpy(&buf[2], val, n);
+-
+- for (retries = MAXIM_NUM_RETRIES; retries; retries--) {
+- ret = i2c_master_send(client, buf, 2 + n);
+- if (ret == 2 + n)
+- break;
+- }
+-
+- if (ret < 0) {
+- dev_dbg(&client->dev,
+- "write fail: chip 0x%x register 0x%x-0x%x: %d\n",
+- client->addr, reg, reg + n, 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 */
+-
+--
+2.7.4
+