aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2019-10-14 18:20:44 +0530
committerManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2019-10-14 18:20:44 +0530
commit74cf5f664050963e5da61035af1ea8e6c3cf09f8 (patch)
tree7e8f687ad6e62ea6cfaccc9355841c38c4f7c6e5
parent8bdd343f96dc838eefba94a051e84c6db4d1c55d (diff)
download96b-common-vision-mez.tar.gz
[WIP] Initial support for Vision Mezzaninevision-mez
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-rw-r--r--arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi19
-rw-r--r--drivers/media/i2c/Kconfig7
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/vision.c422
4 files changed, 446 insertions, 3 deletions
diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
index aac1da4f1d3c..a061501d5963 100644
--- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
@@ -71,6 +71,20 @@
/* On Low speed expansion */
label = "LS-I2C0";
status = "okay";
+
+ vision: camera-sensor@6a {
+ compatible = "sensing,vision";
+ reg = <0x6a>;
+ camera-reg = <0x51>;
+
+ port {
+ vision_ep: endpoint {
+ clock-lanes = <1>;
+ data-lanes = <0 2 3 4>;
+ remote-endpoint = <&csiphy0_ep>;
+ };
+ };
+ };
};
i2c@78b8000 {
@@ -439,9 +453,8 @@
reg = <0>;
csiphy0_ep: endpoint {
clock-lanes = <1>;
- data-lanes = <0 2>;
- remote-endpoint = <&ov5645_ep>;
- status = "disabled";
+ data-lanes = <0 2 3 4>;
+ remote-endpoint = <&vision_ep>;
};
};
port@1 {
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 9a551a11ee09..ae28faff1705 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -677,6 +677,13 @@ config VIDEO_OV13858
This is a Video4Linux2 sensor-level driver for the OmniVision
OV13858 camera.
+config VIDEO_VISION
+ tristate "VISION GMSL camera support"
+ depends on I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ help
+ This driver supports the Vision board based on GMSL cameras.
+
config VIDEO_VS6624
tristate "ST VS6624 sensor support"
depends on VIDEO_V4L2 && I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 02fa38f60b34..507b8af21ce4 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o
obj-$(CONFIG_VIDEO_ADV7511) += adv7511.o
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
+obj-$(CONFIG_VIDEO_VISION) += vision.o
obj-$(CONFIG_VIDEO_BT819) += bt819.o
obj-$(CONFIG_VIDEO_BT856) += bt856.o
obj-$(CONFIG_VIDEO_BT866) += bt866.o
diff --git a/drivers/media/i2c/vision.c b/drivers/media/i2c/vision.c
new file mode 100644
index 000000000000..512625b5372d
--- /dev/null
+++ b/drivers/media/i2c/vision.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The driver is the combination of AR0231 + AP0202 + MAX96705 + MAX9286
+ */
+
+#include <linux/delay.h>
+#include <linux/fwnode.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#define MAX96705_I2C_ADDRESS 0x40
+
+#define MAX96705_WIDTH 1920
+#define MAX96705_HEIGHT 1080
+#define MAX96705_FORMAT MEDIA_BUS_FMT_YUYV8_2X8
+
+struct vision_device {
+ struct i2c_client *client; /* Client is MAX9286 */
+ struct i2c_client *max96705;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler ctrls;
+};
+
+static inline struct vision_device *sd_to_vision(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct vision_device, sd);
+}
+
+static inline struct vision_device *i2c_to_vision(struct i2c_client *client)
+{
+ return sd_to_vision(i2c_get_clientdata(client));
+}
+
+static int max9286_read(struct vision_device *dev, u8 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(dev->client, reg);
+ if (ret < 0)
+ dev_err(&dev->client->dev,
+ "%s: register 0x%02x read failed (%d)\n",
+ __func__, reg, ret);
+
+ return ret;
+}
+
+static int max9286_write(struct vision_device *dev, u8 reg, u8 val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(dev->client, reg, val);
+ if (ret < 0)
+ dev_err(&dev->client->dev,
+ "%s: register 0x%02x write failed (%d)\n",
+ __func__, reg, ret);
+
+ return ret;
+}
+
+static int max96705_write(struct vision_device *dev, u8 reg, u8 val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(dev->max96705, reg, val);
+ if (ret < 0)
+ dev_err(&dev->max96705->dev,
+ "%s: register 0x%02x write failed (%d)\n",
+ __func__, reg, ret);
+
+ return ret;
+}
+
+static int vision_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vision_device *dev = sd_to_vision(sd);
+
+ /* Nothing yet */
+ if (enable) {
+ pr_info("Enabling\n");
+ max9286_write(dev, 0x15, 0x1b);
+ } else {
+ pr_info("Disabling\n");
+ max9286_write(dev, 0x15, 0x03);
+ }
+
+ return 0;
+}
+
+static int vision_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 = MAX96705_FORMAT;
+
+ return 0;
+}
+
+static int vision_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+
+ if (format->pad)
+ return -EINVAL;
+
+ mf->width = MAX96705_WIDTH;
+ mf->height = MAX96705_HEIGHT;
+ mf->code = MAX96705_FORMAT;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops vision_video_ops = {
+ .s_stream = vision_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops vision_subdev_pad_ops = {
+ .enum_mbus_code = vision_enum_mbus_code,
+ .get_fmt = vision_get_fmt,
+ .set_fmt = vision_get_fmt,
+};
+
+static struct v4l2_subdev_ops vision_subdev_ops = {
+ .video = &vision_video_ops,
+ .pad = &vision_subdev_pad_ops,
+};
+
+static int max9286_configure(struct vision_device *dev)
+{
+ int ret;
+
+ ret = max9286_write(dev, 0x0a, 0xff);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to configure MAX9286\n");
+ return ret;
+ }
+
+ ret = max9286_write(dev, 0x34, 0xb6);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to configure MAX9286\n");
+ return ret;
+ }
+
+ usleep_range(5000, 8000);
+ ret = max9286_write(dev, 0x15, 0x03);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to configure MAX9286\n");
+ return ret;
+ }
+
+ usleep_range(5000, 8000);
+ ret = max9286_write(dev, 0x12, 0xf3);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to configure MAX9286\n");
+ return ret;
+ }
+
+ usleep_range(5000, 8000);
+ ret = max9286_write(dev, 0x00, 0x81);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to configure MAX9286\n");
+ return ret;
+ }
+
+ usleep_range(10000, 20000);
+ ret = max9286_write(dev, 0x63, 0x00);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to configure MAX9286\n");
+ return ret;
+ }
+
+ usleep_range(10000, 20000);
+ ret = max9286_write(dev, 0x64, 0x00);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to configure MAX9286\n");
+ return ret;
+ }
+
+ usleep_range(10000, 20000);
+
+ return max9286_write(dev, 0x1c, 0xf4);
+}
+
+static int max96705_configure_address(struct vision_device *dev, u8 addr)
+{
+ int ret;
+
+ /* Change the MAX96705 I2C address. */
+ ret = max96705_write(dev, 0x00, addr << 1);
+ if (ret < 0) {
+ dev_err(&dev->max96705->dev,
+ "MAX96705 I2C address change failed (%d)\n", ret);
+ return ret;
+ }
+ dev->max96705->addr = addr;
+ usleep_range(3500, 5000);
+
+ return 0;
+}
+
+static int max96705_configure(struct vision_device *dev)
+{
+ int ret;
+
+ /*
+ * FIXME: The sleep range defined here is very conservative
+ * since we don't know how much is exactly needed
+ */
+
+ ret = max96705_write(dev, 0x04, 0x47);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to write MAX96705\n");
+ return ret;
+ }
+
+ ret = max96705_write(dev, 0x07, 0x84);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to write MAX96705\n");
+ return ret;
+ }
+
+ usleep_range(5000, 8000);
+
+ /* Reset the serializer */
+ ret = max96705_write(dev, 0x0e, 0x02);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to write MAX96705\n");
+ return ret;
+ }
+/*
+ ret = max96705_write(dev, 0x0f, 0x00);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to write MAX96705\n");
+ return ret;
+ }
+
+ usleep_range(10000, 20000);
+ ret = max96705_write(dev, 0x0f, 0x02);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to write MAX96705\n");
+ return ret;
+ }
+*/
+ usleep_range(10000, 20000);
+
+ return 0;
+}
+
+static int vision_initialize(struct vision_device *dev)
+{
+ u32 addr;
+ int ret;
+
+ ret = of_property_read_u32(dev->client->dev.of_node, "camera-reg",
+ &addr);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Invalid DT reg property\n");
+ return ret;
+ }
+
+ /* Configure the de-serializer */
+ ret = max9286_configure(dev);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to configure MAX9286\n");
+ return ret;
+ }
+
+ pr_info("Configured MAX9286!");
+
+ /* Configure the serializer */
+ ret = max96705_configure(dev);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to configure MAX96705\n");
+ return ret;
+ }
+
+ pr_info("Configured MAX96705!");
+
+ ret = max9286_write(dev, 0x34, 0x36);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to write MAX9286\n");
+ return ret;
+ }
+
+ ret = max96705_write(dev, 0x04, 0x87);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to write MAX96705\n");
+ return ret;
+ }
+
+ usleep_range(3500, 5000);
+ ret = max9286_write(dev, 0x0c, 0x91);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Unable to write MAX9286\n");
+ return ret;
+ }
+
+// return max96705_configure_address(dev, addr);
+ return 0;
+}
+
+static int vision_probe(struct i2c_client *client)
+{
+ struct vision_device *dev;
+ struct fwnode_handle *ep;
+ int ret;
+
+ dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->client = client;
+
+ /* Create the dummy I2C client for the MAX96705. */
+ dev->max96705 = i2c_new_dummy(client->adapter, MAX96705_I2C_ADDRESS);
+ if (!dev->max96705) {
+ ret = -ENXIO;
+ goto error;
+ }
+
+ /* Initialize the hardware. */
+ ret = vision_initialize(dev);
+ if (ret < 0)
+ goto error;
+
+ v4l2_ctrl_handler_init(&dev->ctrls, 1);
+
+ v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, 100000000,
+ 100000000, 1, 100000000);
+
+ dev->sd.ctrl_handler = &dev->ctrls;
+
+ ret = dev->ctrls.error;
+ if (ret)
+ goto error_free_ctrls;
+
+ v4l2_i2c_subdev_init(&dev->sd, client, &vision_subdev_ops);
+ dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ dev->pad.flags = MEDIA_PAD_FL_SOURCE;
+ dev->sd.dev = &client->dev;
+ dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
+ if (ret < 0)
+ goto error_free_ctrls;
+
+ ret = v4l2_async_register_subdev(&dev->sd);
+ if (ret)
+ goto error_free_ctrls;
+
+ pr_info("Vision driver registered\n");
+
+ return 0;
+
+error_free_ctrls:
+ v4l2_ctrl_handler_free(&dev->ctrls);
+error:
+ media_entity_cleanup(&dev->sd.entity);
+ if (dev->max96705)
+ i2c_unregister_device(dev->max96705);
+
+ dev_err(&client->dev, "probe failed\n");
+
+ return ret;
+}
+
+static int vision_remove(struct i2c_client *client)
+{
+ struct vision_device *dev = i2c_to_vision(client);
+
+ fwnode_handle_put(dev->sd.fwnode);
+ v4l2_async_unregister_subdev(&dev->sd);
+ v4l2_ctrl_handler_free(&dev->ctrls);
+ media_entity_cleanup(&dev->sd.entity);
+ i2c_unregister_device(dev->max96705);
+
+ return 0;
+}
+
+static void vision_shutdown(struct i2c_client *client)
+{
+ struct vision_device *dev = i2c_to_vision(client);
+
+ /* make sure stream off during shutdown (reset/reboot) */
+ vision_s_stream(&dev->sd, 0);
+}
+
+static const struct of_device_id vision_of_ids[] = {
+ { .compatible = "sensing,vision", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vision_of_ids);
+
+static struct i2c_driver vision_i2c_driver = {
+ .driver = {
+ .name = "vision",
+ .of_match_table = vision_of_ids,
+ },
+ .probe_new = vision_probe,
+ .remove = vision_remove,
+ .shutdown = vision_shutdown,
+};
+
+module_i2c_driver(vision_i2c_driver);
+
+MODULE_DESCRIPTION("GMSL Camera driver for AR0231");
+MODULE_AUTHOR("Manivannan Sadhasivam");
+MODULE_LICENSE("GPL");