From 1c7d6584a7811b7785ae5c1e378f14b5ba0971cf Mon Sep 17 00:00:00 2001 From: takeshi_hoshina Date: Mon, 2 Nov 2020 11:07:33 +0900 Subject: basesystem-jj recipes --- ...ove-gmsl-fpdlink-drivers-to-separate-fold.patch | 6794 ++++++++++++++++++++ 1 file changed, 6794 insertions(+) create mode 100644 bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0492-media-i2c-move-gmsl-fpdlink-drivers-to-separate-fold.patch (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') 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 +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 +--- + 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 +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#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 +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-#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 + -- cgit 1.2.3-korg