diff options
Diffstat (limited to 'bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0178-lvds-add-OV2311-imager.patch')
-rw-r--r-- | bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0178-lvds-add-OV2311-imager.patch | 935 |
1 files changed, 935 insertions, 0 deletions
diff --git a/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0178-lvds-add-OV2311-imager.patch b/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0178-lvds-add-OV2311-imager.patch new file mode 100644 index 00000000..9f773f70 --- /dev/null +++ b/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0178-lvds-add-OV2311-imager.patch @@ -0,0 +1,935 @@ +From 5c4b2dfb06a275237855fe86e6d3d7952a75d013 Mon Sep 17 00:00:00 2001 +From: Vladimir Barinov <vladimir.barinov@cogentembedded.com> +Date: Fri, 7 Jun 2019 20:51:35 +0300 +Subject: [PATCH 4/5] lvds: add OV2311 imager + +This add OV2311 lvds imager + +Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com> +--- + drivers/media/i2c/soc_camera/ov106xx.c | 13 +- + drivers/media/i2c/soc_camera/ov2311.c | 637 +++++++++++++++++++++++++++++++++ + drivers/media/i2c/soc_camera/ov2311.h | 217 +++++++++++ + 3 files changed, 866 insertions(+), 1 deletion(-) + create mode 100644 drivers/media/i2c/soc_camera/ov2311.c + create mode 100644 drivers/media/i2c/soc_camera/ov2311.h + +diff --git a/drivers/media/i2c/soc_camera/ov106xx.c b/drivers/media/i2c/soc_camera/ov106xx.c +index 36aa88a..70067d7 100644 +--- a/drivers/media/i2c/soc_camera/ov106xx.c ++++ b/drivers/media/i2c/soc_camera/ov106xx.c +@@ -28,6 +28,7 @@ + #include "ox03a.c" + #include "isx016.c" + #include "isx019.c" ++#include "ov2311.c" + + static enum { + ID_OV10635, +@@ -49,12 +50,13 @@ static enum { + ID_OX03A, + ID_ISX016, + ID_ISX019, ++ ID_OV2311, + } chip_id; + + static int ov106xx_probe(struct i2c_client *client, + const struct i2c_device_id *did) + { +- int ret; ++ int ret = -1; + chip_id = -EINVAL; + + ret = ar0231_probe(client, did); +@@ -135,6 +137,12 @@ static int ov106xx_probe(struct i2c_client *client, + goto out; + } + ++ ret = ov2311_probe(client, did); ++ if (!ret) { ++ chip_id = ID_OV2311; ++ goto out; ++ } ++ + ret = imx390_probe(client, did); + if (!ret) { + chip_id = ID_IMX390; +@@ -237,6 +245,9 @@ static int ov106xx_remove(struct i2c_client *client) + case ID_ISX019: + isx019_remove(client); + break; ++ case ID_OV2311: ++ ov2311_remove(client); ++ break; + }; + + return 0; +diff --git a/drivers/media/i2c/soc_camera/ov2311.c b/drivers/media/i2c/soc_camera/ov2311.c +new file mode 100644 +index 0000000..06e57dd +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov2311.c +@@ -0,0 +1,637 @@ ++/* ++ * OmniVision ov2311 sensor camera driver ++ * ++ * Copyright (C) 2015-2019 Cogent Embedded, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include <linux/delay.h> ++#include <linux/init.h> ++#include <linux/i2c.h> ++#include <linux/module.h> ++#include <linux/of_graph.h> ++#include <linux/videodev2.h> ++ ++#include <media/soc_camera.h> ++#include <media/v4l2-common.h> ++#include <media/v4l2-ctrls.h> ++ ++#include "max9286.h" ++#include "ov2311.h" ++ ++#define OV2311_I2C_ADDR 0x60 ++ ++#define OV2311_PID 0x300a ++#define OV2311_VER 0x300b ++#define OV2311_REV 0x300c ++#define OV2311_VERSION_REG 0x2311 ++ ++#define OV2311_MEDIA_BUS_FMT MEDIA_BUS_FMT_Y8_1X8 ++ ++struct ov2311_priv { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct media_pad pad; ++ struct v4l2_rect rect; ++ int subsampling; ++ int fps_denominator; ++ int init_complete; ++ u8 id[6]; ++ int dvp_order; ++ /* serializers */ ++ int max9286_addr; ++ int max9271_addr; ++ int ti9x4_addr; ++ int ti9x3_addr; ++ int port; ++ int gpio_resetb; ++ int gpio_fsin; ++}; ++ ++static inline struct ov2311_priv *to_ov2311(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct ov2311_priv, sd); ++} ++ ++static inline struct v4l2_subdev *ov2311_to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct ov2311_priv, hdl)->sd; ++} ++ ++static void ov2311_s_port(struct i2c_client *client, int fwd_en) ++{ ++ struct ov2311_priv *priv = to_ov2311(client); ++ int tmp_addr; ++ ++ if (priv->max9286_addr) { ++ tmp_addr = client->addr; ++ client->addr = priv->max9286_addr; /* Deserializer I2C address */ ++ reg8_write(client, 0x0a, fwd_en ? 0x11 << priv->port : 0); /* Enable/disable reverse/forward control for this port */ ++ usleep_range(5000, 5500); /* wait 5ms */ ++ client->addr = tmp_addr; ++ }; ++} ++ ++static int ov2311_set_regs(struct i2c_client *client, ++ const struct ov2311_reg *regs, int nr_regs) ++{ ++ int i; ++ ++ for (i = 0; i < nr_regs; i++) { ++ if (regs[i].reg == OV2311_DELAY) { ++ mdelay(regs[i].val); ++ continue; ++ } ++ ++ if (reg16_write(client, regs[i].reg, regs[i].val)) { ++ usleep_range(100, 150); /* wait 100ns */ ++ reg16_write(client, regs[i].reg, regs[i].val); ++ } ++ } ++ ++ return 0; ++} ++ ++static int ov2311_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ return 0; ++} ++ ++static int ov2311_set_window(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov2311_priv *priv = to_ov2311(client); ++ ++ dev_dbg(&client->dev, "L=%d T=%d %dx%d\n", priv->rect.left, priv->rect.top, priv->rect.width, priv->rect.height); ++#if 0 ++ /* setup resolution */ ++ reg16_write(client, 0x3808, priv->rect.width >> 8); ++ reg16_write(client, 0x3809, priv->rect.width & 0xff); ++ reg16_write(client, 0x380a, priv->rect.height >> 8); ++ reg16_write(client, 0x380b, priv->rect.height & 0xff); ++ ++ /* horiz isp windowstart */ ++ reg16_write(client, 0x3810, priv->rect.left >> 8); ++ reg16_write(client, 0x3811, priv->rect.left & 0xff); ++ reg16_write(client, 0x3812, priv->rect.top >> 8); ++ reg16_write(client, 0x3813, priv->rect.top & 0xff); ++#endif ++ return 0; ++}; ++ ++static int ov2311_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 ov2311_priv *priv = to_ov2311(client); ++ ++ if (format->pad) ++ return -EINVAL; ++ ++ mf->width = priv->rect.width; ++ mf->height = priv->rect.height; ++ mf->code = OV2311_MEDIA_BUS_FMT; ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int ov2311_set_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) ++{ ++ struct v4l2_mbus_framefmt *mf = &format->format; ++ ++ mf->code = OV2311_MEDIA_BUS_FMT; ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ mf->field = V4L2_FIELD_NONE; ++ ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) ++ cfg->try_fmt = *mf; ++ ++ return 0; ++} ++ ++static int ov2311_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->pad || code->index > 0) ++ return -EINVAL; ++ ++ code->code = OV2311_MEDIA_BUS_FMT; ++ ++ return 0; ++} ++ ++static int ov2311_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov2311_priv *priv = to_ov2311(client); ++ ++ memcpy(edid->edid, priv->id, 6); ++ ++ edid->edid[6] = 0xff; ++ edid->edid[7] = client->addr; ++ edid->edid[8] = OV2311_VERSION_REG >> 8; ++ edid->edid[9] = OV2311_VERSION_REG & 0xff; ++ ++ return 0; ++} ++ ++static int ov2311_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 ov2311_priv *priv = to_ov2311(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 > OV2311_MAX_WIDTH) || ++ (rect->top + rect->height > OV2311_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; ++ ++ ov2311_set_window(sd); ++ ++ return 0; ++} ++ ++static int ov2311_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 ov2311_priv *priv = to_ov2311(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 = OV2311_MAX_WIDTH; ++ sel->r.height = OV2311_MAX_HEIGHT; ++ return 0; ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ sel->r.left = 0; ++ sel->r.top = 0; ++ sel->r.width = OV2311_MAX_WIDTH; ++ sel->r.height = OV2311_MAX_HEIGHT; ++ return 0; ++ case V4L2_SEL_TGT_CROP: ++ sel->r = priv->rect; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int ov2311_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 ov2311_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ u8 val = 0; ++ ++ ret = reg16_read(client, (u16)reg->reg, &val); ++ if (ret < 0) ++ return ret; ++ ++ reg->val = val; ++ reg->size = sizeof(u16); ++ ++ return 0; ++} ++ ++static int ov2311_s_register(struct v4l2_subdev *sd, ++ const struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return reg16_write(client, (u16)reg->reg, (u8)reg->val); ++} ++#endif ++ ++static struct v4l2_subdev_core_ops ov2311_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov2311_g_register, ++ .s_register = ov2311_s_register, ++#endif ++}; ++ ++static int ov2311_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = ov2311_to_sd(ctrl); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov2311_priv *priv = to_ov2311(client); ++ int ret = 0; ++ u8 val = 0; ++ ++ if (!priv->init_complete) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ case V4L2_CID_CONTRAST: ++ case V4L2_CID_SATURATION: ++ case V4L2_CID_HUE: ++ case V4L2_CID_GAMMA: ++ break; ++ case V4L2_CID_GAIN: ++ reg16_write(client, 0x350A, ctrl->val / 0x3ff); // COARSE: 4.10 format ++ reg16_write(client, 0x350B, (ctrl->val % 0x3ff) >> 2); // FINE: 4.10 format ++ reg16_write(client, 0x350C, (ctrl->val % 0x3ff) << 6); // FINE: 4.10 format ++ break; ++ case V4L2_CID_ANALOGUE_GAIN: ++ reg16_write(client, 0x3508, ctrl->val / 0xf); // COARSE: 5.4 format ++ reg16_write(client, 0x3509, (ctrl->val % 0xf) << 4); // FINE: 5.4 format ++ break; ++ case V4L2_CID_EXPOSURE: ++ reg16_write(client, 0x3501, ctrl->val >> 8); ++ reg16_write(client, 0x3502, ctrl->val & 0xff); ++ break; ++ case V4L2_CID_HFLIP: ++ reg16_read(client, 0x3821, &val); ++ val &= ~0x04; ++ val |= (ctrl->val ? 0x04 : 0); ++ reg16_write(client, 0x3821, val); ++ break; ++ case V4L2_CID_VFLIP: ++ reg16_read(client, 0x3820, &val); ++ val &= ~0x44; ++ val |= (ctrl->val ? 0x44 : 0); ++ reg16_write(client, 0x3820, val); ++ break; ++ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: ++ ret = 0; ++ break; ++ } ++ ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops ov2311_ctrl_ops = { ++ .s_ctrl = ov2311_s_ctrl, ++}; ++ ++static struct v4l2_subdev_video_ops ov2311_video_ops = { ++ .s_stream = ov2311_s_stream, ++ .g_mbus_config = ov2311_g_mbus_config, ++}; ++ ++static const struct v4l2_subdev_pad_ops ov2311_subdev_pad_ops = { ++ .get_edid = ov2311_get_edid, ++ .enum_mbus_code = ov2311_enum_mbus_code, ++ .get_selection = ov2311_get_selection, ++ .set_selection = ov2311_set_selection, ++ .get_fmt = ov2311_get_fmt, ++ .set_fmt = ov2311_set_fmt, ++}; ++ ++static struct v4l2_subdev_ops ov2311_subdev_ops = { ++ .core = &ov2311_core_ops, ++ .video = &ov2311_video_ops, ++ .pad = &ov2311_subdev_pad_ops, ++}; ++ ++static void ov2311_otp_id_read(struct i2c_client *client) ++{ ++ struct ov2311_priv *priv = to_ov2311(client); ++ int i; ++ ++ reg16_write(client, 0x100, 1); ++ reg16_write(client, 0x3d81, 1); ++ usleep_range(25000, 25500); /* wait 25 ms */ ++ ++ for (i = 0; i < 6; i++) { ++ /* first 6 bytes are equal on all ov2311 */ ++ reg16_read(client, 0x7000 + i + 6, &priv->id[i]); ++ } ++ ++ reg16_write(client, 0x100, 0); ++} ++ ++static ssize_t ov2311_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 ov2311_priv *priv = to_ov2311(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_ov2311, S_IRUGO, ov2311_otp_id_show, NULL); ++ ++static int ov2311_initialize(struct i2c_client *client) ++{ ++ struct ov2311_priv *priv = to_ov2311(client); ++ u16 pid; ++ u8 val = 0, rev = 0; ++ int ret = 0; ++ int tmp_addr = 0; ++ ++ ov2311_s_port(client, 1); ++ ++ /* check and show product ID and manufacturer ID */ ++ reg16_read(client, OV2311_PID, &val); ++ pid = val; ++ reg16_read(client, OV2311_VER, &val); ++ pid = (pid << 8) | val; ++ ++ if (pid != OV2311_VERSION_REG) { ++ dev_dbg(&client->dev, "Product ID error %x\n", pid); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ tmp_addr = client->addr; ++ if (priv->ti9x4_addr) { ++ client->addr = priv->ti9x3_addr; /* Serializer I2C address */ ++ reg8_write(client, 0x02, 0x13); /* MIPI 2-lanes */ ++ } ++ client->addr = tmp_addr; ++ ++ /* check revision */ ++ reg16_read(client, OV2311_REV, &rev); ++ /* Read OTP IDs */ ++ ov2311_otp_id_read(client); ++ /* Program wizard registers */ ++ ov2311_set_regs(client, ov2311_regs_wizard_r1c, ARRAY_SIZE(ov2311_regs_wizard_r1c)); ++ ++ dev_info(&client->dev, "ov2311 PID %x (rev %x), res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n", ++ pid, rev, OV2311_MAX_WIDTH, OV2311_MAX_HEIGHT, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); ++out: ++ ov2311_s_port(client, 0); ++ ++ return ret; ++} ++ ++static int ov2311_parse_dt(struct device_node *np, struct ov2311_priv *priv) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); ++ int i; ++ struct device_node *endpoint = NULL, *rendpoint = NULL; ++ int tmp_addr = 0; ++ ++ for (i = 0; ; i++) { ++ endpoint = of_graph_get_next_endpoint(np, endpoint); ++ if (!endpoint) ++ break; ++ ++ of_node_put(endpoint); ++ ++ of_property_read_u32(endpoint, "dvp-order", &priv->dvp_order); ++ ++ rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0); ++ if (!rendpoint) ++ continue; ++ ++ if (!of_property_read_u32(rendpoint, "max9271-addr", &priv->max9271_addr) && ++ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->max9286_addr) && ++ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) ++ break; ++ ++ if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) && ++ !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti9x4") && ++ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti9x4_addr) && ++ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) ++ break; ++ } ++ ++ if (!priv->max9286_addr && !priv->ti9x4_addr) { ++ dev_err(&client->dev, "deserializer does not present for OV2311\n"); ++ return -EINVAL; ++ } ++ ++ ov2311_s_port(client, 1); ++ ++ /* setup I2C translator address */ ++ tmp_addr = client->addr; ++ if (priv->max9286_addr) { ++ client->addr = priv->max9271_addr; /* Serializer I2C address */ ++ ++ reg8_write(client, 0x09, tmp_addr << 1); /* Sensor translated I2C address */ ++ reg8_write(client, 0x0A, OV2311_I2C_ADDR << 1); /* Sensor native I2C address */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ }; ++ ++ if (priv->ti9x4_addr) { ++ client->addr = priv->ti9x4_addr; /* Deserializer I2C address */ ++ ++ reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ reg8_write(client, 0x65, tmp_addr << 1); /* Sensor translated I2C address */ ++ reg8_write(client, 0x5d, OV2311_I2C_ADDR << 1); /* Sensor native I2C address */ ++ } ++ client->addr = tmp_addr; ++ ++ return 0; ++} ++ ++static int ov2311_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct ov2311_priv *priv; ++ struct v4l2_ctrl *ctrl; ++ int ret; ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&priv->sd, client, &ov2311_subdev_ops); ++ priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; ++ priv->rect.left = 0; ++ priv->rect.top = 0; ++ priv->rect.width = OV2311_MAX_WIDTH; ++ priv->rect.height = OV2311_MAX_HEIGHT; ++ priv->fps_denominator = 30; ++ ++ v4l2_ctrl_handler_init(&priv->hdl, 4); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x30); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 4, 1, 2); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 0xff, 1, 0xff); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_HUE, 0, 255, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_GAMMA, 0, 0xffff, 1, 0x233); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_GAIN, 0, 0x3ff*4, 1, 0x3ff); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_ANALOGUE_GAIN, 0, 0xf*5, 1, 0xf); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_EXPOSURE, 0, 0x580, 1, 0x57c); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov2311_ctrl_ops, ++ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 9); ++ if (ctrl) ++ ctrl->flags &= ~V4L2_CTRL_FLAG_READ_ONLY; ++ priv->sd.ctrl_handler = &priv->hdl; ++ ++ ret = priv->hdl.error; ++ if (ret) ++ goto cleanup; ++ ++ v4l2_ctrl_handler_setup(&priv->hdl); ++ ++ priv->pad.flags = MEDIA_PAD_FL_SOURCE; ++ priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR; ++ ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad); ++ if (ret < 0) ++ goto cleanup; ++ ++ ret = ov2311_parse_dt(client->dev.of_node, priv); ++ if (ret) ++ goto cleanup; ++ ++ ret = ov2311_initialize(client); ++ if (ret < 0) ++ goto cleanup; ++ ++ ret = v4l2_async_register_subdev(&priv->sd); ++ if (ret) ++ goto cleanup; ++ ++ if (device_create_file(&client->dev, &dev_attr_otp_id_ov2311) != 0) { ++ dev_err(&client->dev, "sysfs otp_id entry creation failed\n"); ++ goto cleanup; ++ } ++ ++ priv->init_complete = 1; ++ ++ return 0; ++ ++cleanup: ++ media_entity_cleanup(&priv->sd.entity); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ v4l2_device_unregister_subdev(&priv->sd); ++#ifdef CONFIG_SOC_CAMERA_OV2311 ++ v4l_err(client, "failed to probe @ 0x%02x (%s)\n", ++ client->addr, client->adapter->name); ++#endif ++ return ret; ++} ++ ++static int ov2311_remove(struct i2c_client *client) ++{ ++ struct ov2311_priv *priv = i2c_get_clientdata(client); ++ ++ device_remove_file(&client->dev, &dev_attr_otp_id_ov2311); ++ v4l2_async_unregister_subdev(&priv->sd); ++ media_entity_cleanup(&priv->sd.entity); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ v4l2_device_unregister_subdev(&priv->sd); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_SOC_CAMERA_OV2311 ++static const struct i2c_device_id ov2311_id[] = { ++ { "ov2311", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ov2311_id); ++ ++static const struct of_device_id ov2311_of_ids[] = { ++ { .compatible = "ovti,ov2311", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ov2311_of_ids); ++ ++static struct i2c_driver ov2311_i2c_driver = { ++ .driver = { ++ .name = "ov2311", ++ .of_match_table = ov2311_of_ids, ++ }, ++ .probe = ov2311_probe, ++ .remove = ov2311_remove, ++ .id_table = ov2311_id, ++}; ++ ++module_i2c_driver(ov2311_i2c_driver); ++ ++MODULE_DESCRIPTION("SoC Camera driver for OV2311"); ++MODULE_AUTHOR("Vladimir Barinov"); ++MODULE_LICENSE("GPL"); ++#endif +diff --git a/drivers/media/i2c/soc_camera/ov2311.h b/drivers/media/i2c/soc_camera/ov2311.h +new file mode 100644 +index 0000000..3a56b0b +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov2311.h +@@ -0,0 +1,217 @@ ++/* ++ * OmniVision ov2311 sensor camera wizard 1600x130@30/GREY8/MIPI ++ * ++ * Copyright (C) 2015-2017 Cogent Embedded, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++//#define OV2311_DISPLAY_PATTERN ++//#define OV2311_FSIN_ENABLE ++ ++#define OV2311_MAX_WIDTH 1600 ++#define OV2311_MAX_HEIGHT 1300 ++ ++#define OV2311_DELAY 0xffff ++ ++#define OV2311_SENSOR_WIDTH 1616 ++#define OV2311_SENSOR_HEIGHT 1316 ++ ++#define OV2311_X_START ((OV2311_SENSOR_WIDTH - OV2311_MAX_WIDTH) / 2) ++#define OV2311_Y_START ((OV2311_SENSOR_HEIGHT - OV2311_MAX_HEIGHT) / 2) ++#define OV2311_X_END (OV2311_X_START + OV2311_MAX_WIDTH - 1) ++#define OV2311_Y_END (OV2311_Y_START + OV2311_MAX_HEIGHT - 1) ++ ++struct ov2311_reg { ++ u16 reg; ++ u8 val; ++}; ++ ++/* R1600x1300 RAW10 MIPI 60fps */ ++static const struct ov2311_reg ov2311_regs_wizard_r1c[] = { ++{0x0103, 0x01}, ++{0x0100, 0x00}, ++{0x010c, 0x02}, ++{0x010b, 0x01}, ++{0x0300, 0x01}, ++{0x0302, 0x32}, ++{0x0303, 0x00}, ++{0x0304, 0x03}, ++{0x0305, 0x02}, ++{0x0306, 0x01}, ++{0x030d, 0x5a}, ++{0x030e, 0x04}, ++{0x3001, 0x02}, ++{0x3004, 0x00}, ++{0x3005, 0x00}, ++{0x3006, 0x0a}, ++{0x3011, 0x0d}, ++{0x3014, 0x04}, ++{0x301c, 0xf0}, ++{0x3020, 0x20}, ++{0x302c, 0x00}, ++{0x302d, 0x00}, ++{0x302e, 0x00}, ++{0x302f, 0x03}, ++{0x3030, 0x10}, ++{0x303f, 0x03}, ++{0x3103, 0x00}, ++{0x3106, 0x08}, ++{0x31ff, 0x01}, ++{0x3501, 0x05}, ++{0x3502, 0x7c}, ++{0x3506, 0x00}, ++{0x3507, 0x00}, ++{0x3620, 0x67}, ++{0x3633, 0x78}, ++{0x3662, 0x65}, ++{0x3664, 0xb0}, ++{0x3666, 0x70}, ++{0x3670, 0x68}, ++{0x3674, 0x10}, ++{0x3675, 0x00}, ++{0x367e, 0x90}, ++{0x3680, 0x84}, ++{0x36a2, 0x04}, ++{0x36a3, 0x80}, ++{0x36b0, 0x00}, ++{0x3700, 0x35}, ++{0x3704, 0x39}, ++{0x370a, 0x50}, ++{0x3712, 0x00}, ++{0x3713, 0x02}, ++{0x3778, 0x00}, ++{0x379b, 0x01}, ++{0x379c, 0x10}, ++{0x3800, 0x00}, ++{0x3801, 0x00}, ++{0x3802, 0x00}, ++{0x3803, 0x00}, ++{0x3804, 0x06}, ++{0x3805, 0x4f}, ++{0x3806, 0x05}, ++{0x3807, 0x23}, ++{0x3808, OV2311_MAX_WIDTH >> 8}, ++{0x3809, OV2311_MAX_WIDTH & 0xff}, ++{0x380a, OV2311_MAX_HEIGHT >> 8}, ++{0x380b, OV2311_MAX_HEIGHT & 0xff}, ++{0x380c, 0x03}, ++{0x380d, 0xa8}, ++{0x380e, 0x05}, ++{0x380f, 0x88}, ++{0x3810, OV2311_X_START >> 8}, ++{0x3811, OV2311_X_START & 0xff}, ++{0x3812, OV2311_Y_START >> 8}, ++{0x3813, OV2311_X_START & 0xff}, ++{0x3814, 0x11}, ++{0x3815, 0x11}, ++{0x3816, 0x00}, ++{0x3817, 0x01}, ++{0x3818, 0x00}, ++{0x3819, 0x05}, ++{0x3820, 0x00}, ++{0x3821, 0x00}, ++{0x382b, 0x5a}, ++{0x382c, 0x0a}, ++{0x382d, 0xf8}, ++{0x3881, 0x44}, ++{0x3882, 0x02}, ++{0x3883, 0x8c}, ++{0x3885, 0x07}, ++{0x389d, 0x03}, ++{0x38a6, 0x00}, ++{0x38a7, 0x01}, ++{0x38b3, 0x07}, ++{0x38b1, 0x00}, ++{0x38e5, 0x02}, ++{0x38e7, 0x00}, ++{0x38e8, 0x00}, ++{0x3910, 0xff}, ++{0x3911, 0xff}, ++{0x3912, 0x08}, ++{0x3913, 0x00}, ++{0x3914, 0x00}, ++{0x3915, 0x00}, ++{0x391c, 0x00}, ++{0x3920, 0xff}, ++{0x3921, 0x80}, ++{0x3922, 0x00}, ++{0x3923, 0x00}, ++{0x3924, 0x05}, ++{0x3925, 0x00}, ++{0x3926, 0x00}, ++{0x3927, 0x00}, ++{0x3928, 0x1a}, ++{0x392d, 0x03}, ++{0x392e, 0xa8}, ++{0x392f, 0x08}, ++{0x4001, 0x00}, ++{0x4003, 0x40}, ++{0x4008, 0x04}, ++{0x4009, 0x1b}, ++{0x400c, 0x04}, ++{0x400d, 0x1b}, ++{0x4010, 0xf4}, ++{0x4011, 0x00}, ++{0x4016, 0x00}, ++{0x4017, 0x04}, ++{0x4042, 0x11}, ++{0x4043, 0x70}, ++{0x4045, 0x00}, ++{0x4409, 0x5f}, ++{0x4509, 0x00}, ++{0x450b, 0x00}, ++{0x4600, 0x00}, ++{0x4601, 0xa0}, ++{0x4708, 0x09}, ++{0x470c, 0x81}, ++{0x4710, 0x06}, ++{0x4711, 0x00}, ++{0x4800, 0x00}, ++{0x481f, 0x30}, ++{0x4837, 0x14}, ++{0x4f00, 0x00}, ++{0x4f07, 0x00}, ++{0x4f08, 0x03}, ++{0x4f09, 0x08}, ++{0x4f0c, 0x05}, ++{0x4f0d, 0xb4}, ++{0x4f10, 0x00}, ++{0x4f11, 0x00}, ++{0x4f12, 0x07}, ++{0x4f13, 0xe2}, ++{0x5000, 0x9f}, ++{0x5001, 0x20}, ++{0x5026, 0x00}, ++{0x5c00, 0x00}, ++{0x5c01, 0x2c}, ++{0x5c02, 0x00}, ++{0x5c03, 0x7f}, ++{0x5e00, 0x00}, ++{0x5e01, 0x41}, ++{0x38b1, 0x02}, ++{0x3880, 0x00}, ++ ++#if 1 /* Y8 mode */ ++{0x3016, 0xF1}, ++{0x0100, 0x01}, ++{0x4814, 0x6A}, //; dt_man en, both embed/image data type are 0x2A ++{0x3218, 0x32}, ++{0x3216, 0x01}, ++{0x3208, 0x04}, ++{0x3D81, 0x01}, ++{0x4605, 0x02}, ++{0x4816, 0x0A}, ++{0x3208, 0x14}, ++{0x3662, 0x67}, //; [1] raw8 ++{0x366F, 0x1A}, //; [6] MSB ++//{0x3674, 0x11}, //; [0] embed_en, add embed data before normal image ++{0x3674, 0x10}, //; [0] embed_dis, add embed data before normal image ++{0x3016, 0xF0}, ++#endif ++ ++{0x0100, 0x01}, ++}; +-- +2.7.4 + |