aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2019-10-02 07:00:32 +0530
committerManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2019-10-02 07:00:32 +0530
commite114f53738daf78bea1dc95853a00e729c69f1fe (patch)
tree7d954ebcac117261a73b08b8c1af6225f536d103
parente8c85dbeab3ea56f2cd48ed4b83dc65f2edc890b (diff)
download96b-common-visioon-wip.tar.gz
Initial commitvisioon-wip
-rw-r--r--arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi165
-rw-r--r--drivers/media/i2c/Kconfig11
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/ar0231.c335
4 files changed, 512 insertions, 0 deletions
diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
index 46feedf7c989..5d8523b08c86 100644
--- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
@@ -221,6 +221,130 @@
/* On Low speed expansion */
label = "LS-I2C0";
status = "okay";
+
+ gmsl-deserializer@6a {
+ compatible = "maxim,max9286";
+ reg = <0x6a>;
+ poc-supply = <&camera_vdddo_1v8>;
+ enable-gpios = <&msmgpio 34 GPIO_ACTIVE_HIGH>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ max9286_in0: endpoint {
+ remote-endpoint = <&ar0231_out0>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ max9286_in1: endpoint {
+ remote-endpoint = <&ar0231_out1>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ max9286_in2: endpoint {
+ remote-endpoint = <&ar0231_out2>;
+ };
+ };
+
+ port@3 {
+ reg = <3>;
+ max9286_in3: endpoint {
+ remote-endpoint = <&ar0231_out3>;
+ };
+ };
+
+ port@4 {
+ reg = <4>;
+ max9286_out: endpoint {
+ clock-lanes = <1>;
+ data-lanes = <0 2 3 4>;
+ remote-endpoint = <&csiphy0_ep>;
+ };
+ };
+ };
+
+ i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ camera@50 {
+ compatible = "sensing,ar0231";
+ reg = <0x50>;
+
+ port {
+ ar0231_out0: endpoint {
+ remote-endpoint = <&max9286_in0>;
+ };
+ };
+
+ };
+ };
+
+ i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ camera@51 {
+ compatible = "sensing,ar0231";
+ reg = <0x51>;
+
+ port {
+ ar0231_out1: endpoint {
+ remote-endpoint = <&max9286_in1>;
+ };
+ };
+
+ };
+ };
+
+ i2c@2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <2>;
+
+ camera@52 {
+ compatible = "sensing,ar0231";
+ reg = <0x52>;
+
+ port {
+ ar0231_out2: endpoint {
+ remote-endpoint = <&max9286_in2>;
+ };
+ };
+
+ };
+ };
+
+ i2c@3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <3>;
+
+ camera@53 {
+ compatible = "sensing,ar0231";
+ reg = <0x53>;
+
+ port {
+ ar0231_out3: endpoint {
+ remote-endpoint = <&max9286_in3>;
+ };
+ };
+
+ };
+ };
+ };
};
i2c@78b8000 {
@@ -502,6 +626,47 @@
wcnss@a21b000 {
status = "okay";
};
+
+ camera_vdddo_1v8: fixedregulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "camera_vdddo";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ camera_vdda_2v8: fixedregulator@1 {
+ compatible = "regulator-fixed";
+ regulator-name = "camera_vdda";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ };
+
+ camera_vddd_1v5: fixedregulator@2 {
+ compatible = "regulator-fixed";
+ regulator-name = "camera_vddd";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-always-on;
+ };
+
+ camss@1b00000 {
+ status = "okay";
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ csiphy0_ep: endpoint {
+ clock-lanes = <1>;
+ data-lanes = <0 2 3 4>;
+ remote-endpoint = <&max9286_out>;
+ status = "okay";
+ };
+ };
+ };
+ };
};
usb2513 {
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index e3f0c4b68b94..2326957433d2 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -1022,6 +1022,17 @@ config VIDEO_RDACM20
This camera should be used in conjunction with a GMSL
deserialiser such as the MAX9286.
+config VIDEO_AR0231
+ tristate "IMI RDACM20 camera support"
+ depends on I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ help
+ This driver supports the IMI RDACM20 GMSL camera, used in
+ ADAS systems.
+
+ This camera should be used in conjunction with a GMSL
+ deserialiser such as the MAX9286.
+
comment "Flash devices"
config VIDEO_ADP1653
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 6428f6322a3e..695c9cef7da2 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -114,5 +114,6 @@ obj-$(CONFIG_VIDEO_IMX319) += imx319.o
obj-$(CONFIG_VIDEO_IMX355) += imx355.o
obj-$(CONFIG_VIDEO_MAX9286) += max9286.o
obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o
+obj-$(CONFIG_VIDEO_AR0231) += ar0231.o
obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/ar0231.c b/drivers/media/i2c/ar0231.c
new file mode 100644
index 000000000000..e8221a9c1b78
--- /dev/null
+++ b/drivers/media/i2c/ar0231.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Sensing AR0231 GMSL Camera Driver
+ *
+ */
+
+/*
+ * The camera module is the combination of AR0231 + AP0202 + MAX96705
+ */
+
+#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 1280
+#define MAX96705_HEIGHT 800
+#define MAX96705_FORMAT MEDIA_BUS_FMT_UYVY8_2X8
+
+struct ar0231_device {
+ struct i2c_client *client;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler ctrls;
+};
+
+static inline struct ar0231_device *sd_to_ar0231(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ar0231_device, sd);
+}
+
+static inline struct ar0231_device *i2c_to_ar0231(struct i2c_client *client)
+{
+ return sd_to_ar0231(i2c_get_clientdata(client));
+}
+
+static int __max96705_write(struct ar0231_device *dev, u16 reg, u8 val)
+{
+ u8 buf[3] = { reg >> 8, reg & 0xff, val };
+ int ret;
+
+ dev_dbg(&dev->client->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
+
+ ret = i2c_master_send(dev->client, buf, 3);
+ return ret < 0 ? ret : 0;
+}
+
+static int max96705_write(struct ar0231_device *dev, u16 reg, u8 val)
+{
+ int ret;
+
+ ret = __max96705_write(dev, reg, val);
+ if (ret < 0)
+ dev_err(&dev->client->dev,
+ "%s: register 0x%04x write failed (%d)\n",
+ __func__, reg, ret);
+
+ return ret;
+}
+
+static int ar0231_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ /* 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 ar0231_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 ar0231_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_RAW;
+ mf->field = V4L2_FIELD_NONE;
+ mf->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ mf->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ mf->xfer_func = V4L2_XFER_FUNC_NONE;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ar0231_video_ops = {
+ .s_stream = ar0231_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ar0231_subdev_pad_ops = {
+ .enum_mbus_code = ar0231_enum_mbus_code,
+ .get_fmt = ar0231_get_fmt,
+ .set_fmt = ar0231_get_fmt,
+};
+
+static struct v4l2_subdev_ops ar0231_subdev_ops = {
+ .video = &ar0231_video_ops,
+ .pad = &ar0231_subdev_pad_ops,
+};
+/*
+static void max9286_configure(struct ar0231_device *dev)
+{
+ max9286_write(dev, 0x0a, 0xff);
+ max9286_write(dev, 0x34, 0xb6);
+ usleep_range(5000, 8000);
+ max9286_write(dev, 0x15, 0x03);
+ usleep_range(5000, 8000);
+ max9286_write(dev, 0x12, 0xf3);
+ usleep_range(5000, 8000);
+ max9286_write(dev, 0x00, 0x81);
+ usleep_range(10000, 20000);
+ max9286_write(dev, 0x63, 0x00);
+ usleep_range(10000, 20000);
+ max9286_write(dev, 0x64, 0x00);
+ usleep_range(10000, 20000);
+ max9286_write(dev, 0x1c, 0xf4);
+}
+*/
+
+static int max96705_configure_address(struct ar0231_device *dev, u8 addr)
+{
+ int ret;
+
+ /* Change the MAX9271 I2C address. */
+ ret = max96705_write(dev, 0x00, addr << 1);
+ if (ret < 0) {
+ dev_err(&dev->client->dev,
+ "MAX9271 I2C address change failed (%d)\n", ret);
+ return ret;
+ }
+ dev->client->addr = addr;
+ usleep_range(3500, 5000);
+
+ return 0;
+}
+
+static void max96705_configure(struct ar0231_device *dev)
+{
+ /*
+ * FIXME: The sleep range defined here is very conservative
+ * since we don't know how much is exactly needed
+ */
+
+ max96705_write(dev, 0x04, 0x47);
+ max96705_write(dev, 0x07, 0x84);
+ usleep_range(5000, 8000);
+
+ /* Reset the serializer */
+ max96705_write(dev, 0x0e, 0x02);
+ max96705_write(dev, 0x0f, 0x00);
+ usleep_range(10000, 20000);
+ max96705_write(dev, 0x0f, 0x02);
+ usleep_range(10000, 20000);
+}
+
+static int ar0231_initialize(struct ar0231_device *dev)
+{
+ u32 addr;
+ int ret;
+
+ ret = of_property_read_u32(dev->client->dev.of_node, "reg",
+ &addr);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Invalid DT reg property\n");
+ return -EINVAL;
+ }
+
+ /*
+ * FIXME: The MAX96705 boots at a default address that we will change to
+ * the address specified in DT. Set the client address back to the
+ * default for initial communication.
+ */
+ dev->client->addr = MAX96705_I2C_ADDRESS;
+
+ /* Configure the serializer */
+ max96705_configure(dev);
+ /* Configure the de-serializer */
+// max9286_configure(dev);
+
+// max9286_write(dev, 0x34, 0x36);
+ max96705_write(dev, 0x04, 0x87);
+ usleep_range(3500, 5000);
+// max9286_write(dev, 0x0c, 0x91);
+
+ return max96705_configure_address(dev, addr);
+}
+
+static int ar0231_probe(struct i2c_client *client)
+{
+ struct ar0231_device *dev;
+ struct fwnode_handle *ep;
+ int ret;
+
+ dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->client = client;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ /* Initialize the hardware. */
+ ret = ar0231_initialize(dev);
+ if (ret < 0)
+ goto error;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ /* Initialize and register the subdevice. */
+ v4l2_i2c_subdev_init(&dev->sd, client, &ar0231_subdev_ops);
+ dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ v4l2_ctrl_handler_init(&dev->ctrls, 1);
+
+ v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, 500000000,
+ 500000000, 1, 500000000);
+
+ v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_LINK_FREQ, 100000000,
+ 100000000, 1, 100000000);
+
+ dev->sd.ctrl_handler = &dev->ctrls;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ ret = dev->ctrls.error;
+ if (ret)
+ goto error_free_ctrls;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ dev->pad.flags = MEDIA_PAD_FL_SOURCE;
+ dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
+ if (ret < 0)
+ goto error_free_ctrls;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
+ if (!ep) {
+ dev_err(&client->dev,
+ "Unable to get endpoint in node %pOF\n",
+ client->dev.of_node);
+ ret = -ENOENT;
+ goto error_free_ctrls;
+ }
+ dev->sd.fwnode = ep;
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ ret = v4l2_async_register_subdev(&dev->sd);
+ if (ret)
+ goto error_put_node;
+
+ pr_info("%s: %d registered\n", __func__, __LINE__);
+
+ return 0;
+
+error_put_node:
+ fwnode_handle_put(ep);
+error_free_ctrls:
+ v4l2_ctrl_handler_free(&dev->ctrls);
+error:
+ media_entity_cleanup(&dev->sd.entity);
+
+ dev_err(&client->dev, "probe failed\n");
+
+ return ret;
+}
+
+static int ar0231_remove(struct i2c_client *client)
+{
+ struct ar0231_device *dev = i2c_to_ar0231(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);
+ return 0;
+}
+
+static void ar0231_shutdown(struct i2c_client *client)
+{
+ struct ar0231_device *dev = i2c_to_ar0231(client);
+
+ /* make sure stream off during shutdown (reset/reboot) */
+ ar0231_s_stream(&dev->sd, 0);
+}
+
+static const struct of_device_id ar0231_of_ids[] = {
+ { .compatible = "sensing,ar0231", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ar0231_of_ids);
+
+static struct i2c_driver ar0231_i2c_driver = {
+ .driver = {
+ .name = "ar0231",
+ .of_match_table = ar0231_of_ids,
+ },
+ .probe_new = ar0231_probe,
+ .remove = ar0231_remove,
+ .shutdown = ar0231_shutdown,
+};
+
+module_i2c_driver(ar0231_i2c_driver);
+
+MODULE_DESCRIPTION("GMSL Camera driver for AR0231");
+MODULE_AUTHOR("Manivannan Sadhasivam");
+MODULE_LICENSE("GPL");