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