linux-renesas-soc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v7 0/6] RDACM20 and RDACM21 GMSL camera support
@ 2020-02-16 10:50 Jacopo Mondi
  2020-02-16 10:51 ` [PATCH v7 1/6] media: i2c: max9286: Disable overlap window Jacopo Mondi
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Jacopo Mondi @ 2020-02-16 10:50 UTC (permalink / raw)
  To: kieran.bingham+renesas, laurent.pinchart, niklas.soderlund
  Cc: Jacopo Mondi, linux-renesas-soc

Hello,
   this patch series introduces support for the RDACM20 and RDACM21 camera
modules.

Both cameras use a Maxim MAX9271 serializer, and the associated driver is
here proposed as a library module in order to maximize code use from the
camera module drivers.

RDACM20 support has been proposed several times already, associated with
MAX9286 support (hence the v7 in the patch series).

RDACM21 is a new addition, whose support required several changes in the
rdacm20 driver compared to its v6, particularly the split of max9271 handling
to a library module and reworking of DT bindings description to support both
sensors.

In order to capture from RDACM21 driver a few changes on the deserializer side
are required. All changes to the max9286 driver are based on the most recently
sent v7 from Kieran. Particularly change 1/6 disables overlap window interval
calculation, which help achieving frame synchronization with RDACM21. The change
is not disruptive for RDACM20 and could be considered for inclusion. Patches
4/6 and 5/6 instead modify the max9286 behavior to better support communication
with RDACM21, but interfere with RDACM20 operations, making the deserializer
driver not usable for both cameras. These two changes (4/5 in particular) are
the only reasons why the patches have not yet been sent to linux-media for
collection as we need to discuss how handle differences in the reverse channel
setup configuration to support different camera modules.

Finally, patch 6/6 introduces the new rdacm21 driver, based on the BSP version
available at
https://github.com/CogentEmbedded/meta-rcar/tree/v3.21.0/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas

Testing has been performed by capturing from 2 RDACM20 cameras (reverting
patche 4/6 and 5/6) and from 2 RDACM21 cameras on Eagle V3M.

Changelog is reported per-patch if the patch, and a testing branch is available
at
git:://jmondi.org/linux #gmsl/jmondi/platform/rdacm21

Thanks
   j

Jacopo Mondi (6):
  media: i2c: max9286: Disable overlap window
  dt-bindings: media: i2c: Add bindings for IMI RDACM2x
  media: i2c: Add driver for RDACM20 camera module
  media: i2c: max9286: Adjust reverse channel amplitude
  media: i2c: max9286: Adjust image format size
  media: i2c: Add driver for RDACM21 camera module

 .../bindings/media/i2c/imi,rdacm2x-gmsl.yaml  | 161 +++
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 MAINTAINERS                                   |  24 +
 drivers/media/i2c/Kconfig                     |  22 +
 drivers/media/i2c/Makefile                    |   4 +
 drivers/media/i2c/max9271.c                   | 338 +++++++
 drivers/media/i2c/max9271.h                   | 217 ++++
 drivers/media/i2c/max9286.c                   |  18 +-
 drivers/media/i2c/rdacm20-ov10635.h           | 953 ++++++++++++++++++
 drivers/media/i2c/rdacm20.c                   | 398 ++++++++
 drivers/media/i2c/rdacm21.c                   | 516 ++++++++++
 11 files changed, 2647 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
 create mode 100644 drivers/media/i2c/max9271.c
 create mode 100644 drivers/media/i2c/max9271.h
 create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
 create mode 100644 drivers/media/i2c/rdacm20.c
 create mode 100644 drivers/media/i2c/rdacm21.c

--
2.25.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v7 1/6] media: i2c: max9286: Disable overlap window
  2020-02-16 10:50 [PATCH v7 0/6] RDACM20 and RDACM21 GMSL camera support Jacopo Mondi
@ 2020-02-16 10:51 ` Jacopo Mondi
  2020-02-16 10:51 ` [PATCH v7 2/6] dt-bindings: media: i2c: Add bindings for IMI RDACM2x Jacopo Mondi
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Jacopo Mondi @ 2020-02-16 10:51 UTC (permalink / raw)
  To: kieran.bingham+renesas, laurent.pinchart, niklas.soderlund
  Cc: Jacopo Mondi, linux-renesas-soc

Disable the overlap window. This settings is briefly documented in the
chip manual, but it probably concur in the frame syncronization timing
calculations.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>

---
v7:
  - new patch required to capture from RDACM21
---
 drivers/media/i2c/max9286.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index b5b8d4919c78..ed5dcae6b363 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1015,6 +1015,10 @@ static int max9286_setup(struct max9286_priv *priv)
 	max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
 		      MAX9286_FSYNCMETH_AUTO);
 
+	/* Disable overlap window. */
+	max9286_write(priv, 0x63, 0);
+	max9286_write(priv, 0x64, 0);
+
 	/* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */
 	max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
 		      MAX9286_HVSRC_D14);
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v7 2/6] dt-bindings: media: i2c: Add bindings for IMI RDACM2x
  2020-02-16 10:50 [PATCH v7 0/6] RDACM20 and RDACM21 GMSL camera support Jacopo Mondi
  2020-02-16 10:51 ` [PATCH v7 1/6] media: i2c: max9286: Disable overlap window Jacopo Mondi
@ 2020-02-16 10:51 ` Jacopo Mondi
  2020-02-16 10:51 ` [PATCH v7 3/6] media: i2c: Add driver for RDACM20 camera module Jacopo Mondi
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Jacopo Mondi @ 2020-02-16 10:51 UTC (permalink / raw)
  To: kieran.bingham+renesas, laurent.pinchart, niklas.soderlund
  Cc: Jacopo Mondi, linux-renesas-soc

The IMI RDACM20 and IMI RDACM21 are Gigabit Multimedia Serial Link (GMSL) camera
capable of transmitting video and I2C control messages on a coax cable
physical link for automotive applications.

Document their device tree bindings.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

---
v2:
 - Provide imi vendor prefix
 - Fix minor spelling

v3:
 - update binding descriptions

v4:
 - No change

v5:
 - Specify optional third reg address for the MCU

v7:
 [Jacopo]
 - Rename to imi,rdacm2x-gmsl.yaml
 - Exand bindings to describe RDACM21
---
 .../bindings/media/i2c/imi,rdacm2x-gmsl.yaml  | 161 ++++++++++++++++++
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 2 files changed, 163 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml

diff --git a/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml b/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
new file mode 100644
index 000000000000..8476e99b46b5
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
@@ -0,0 +1,161 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+# Copyright (C) 2019 Renesas Electronics Corp.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/imi,rdacm2x-gmsl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title:  IMI D&D RDACM20 and RDACM21 Automotive Camera Platforms
+
+maintainers:
+  - Jacopo Mondi <jacopo+renesas@jmondi.org>
+  - Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
+  - Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
+  - Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
+
+description: -|
+  The IMI D&D RDACM20 and RDACM21 are GMSL-compatible camera designed for
+  automotive applications.
+
+  The RDACM20 camera module encloses a Maxim Integrated MAX9271 GMSL serializer,
+  coupled with an OV10635 image sensor and an embedded MCU. Both the MCU and
+  the image sensor are connected to the serializer local I2C bus and are
+  accessible by the host SoC by direct addressing.
+
+  The RDACM21 camera module encloses the same serializer, coupled with an
+  OV10640 image sensor and an OV490 ISP. Only the OV490 ISP is interfaced to
+  the serializer local I2C bus while the image sensor is not accessible from
+  the host SoC.
+
+  They both connect to a remote GMSL endpoint through a coaxial cable.
+
+                                                   IMI RDACM20
+  +---------------+                        +--------------------------------+
+  |      GMSL     |   <- Video Stream      |       <- Video--------\        |
+  |               |< === GMSL Link ====== >|MAX9271<- I2C bus-> <-->OV10635 |
+  | de-serializer |   <- I2C messages ->   |                   \<-->MCU     |
+  +---------------+                        +--------------------------------+
+
+                                                   IMI RDACM21
+  +---------------+                        +--------------------------------+
+  |      GMSL     |   <- Video Stream      |       <- Video--------\        |
+  |               |< === GMSL Link ====== >|MAX9271<- I2C bus-> <-->OV490   |
+  |               |   <- I2C messages ->   |                          |     |
+  | de-serializer |                        |          OV10640 <-------|     |
+  +---------------+                        +--------------------------------+
+
+  Both camera modules serialize video data generated by the embedded camera
+  sensor on the GMSL serial channel to a remote GMSL de-serializer. They also
+  receive and transmit I2C messages encapsulated and transmitted on the GMSL
+  bidirectional control channel.
+
+  All I2C traffic received on the GMSL link not directed to the serializer is
+  propagated on the local I2C bus to the remote device there connected. All the
+  I2C traffic generated on the local I2C bus not directed to the serializer is
+  propagated to the remote de-serializer encapsulated in the GMSL control
+  channel.
+
+  The RDACM20 and RDACM21 DT node should be a direct child of the GMSL
+  deserializer's I2C bus corresponding to the GMSL link that the camera is
+  attached to.
+
+properties:
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+  compatible:
+    oneOf:
+      - items:
+        - enum:
+          - imi,rdacm20
+          - imi,rdacm21
+
+  reg:
+    description: -|
+      I2C device addresses, the first to be assigned to the serializer, the
+      following ones to be assigned to the remote devices.
+
+      For RDACM20 the second entry of the property is assigned to the
+      OV10635 image sensor and the optional third one to the embedded MCU.
+
+      For RDACM21 the second entry is assigned to the OV490 ISP and the optional
+      third one ignored.
+
+    minItems: 2
+    maxItems: 3
+
+  port:
+    type: object
+    additionalProperties: false
+    description: -|
+      Connection to the remote GMSL endpoint are modelled using the OF graph
+      bindings in accordance with the video interface bindings defined in
+      Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+      The device node contains a single "port" child node with a single
+      "endpoint" sub-device.
+
+    properties:
+      endpoint:
+        type: object
+        additionalProperties: false
+
+        properties:
+          remote-endpoint:
+            description: -|
+              phandle to the remote GMSL endpoint sub-node in the remote node
+              port.
+            maxItems: 1
+
+        required:
+          - remote-endpoint
+
+    required:
+      - endpoint
+
+required:
+  - compatible
+  - reg
+  - port
+
+examples:
+  - |
+    i2c@e66d8000 {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      reg = <0 0xe66d8000 0 0x40>;
+
+      camera@31 {
+        compatible = "imi,rdacm20";
+        reg = <0x31>, <0x41>, <0x51>;
+
+        port {
+          rdacm20_out0: endpoint {
+            remote-endpoint = <&max9286_in0>;
+          };
+        };
+      };
+    };
+
+  - |
+    i2c@e66d8000 {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      reg = <0 0xe66d8000 0 0x40>;
+
+      camera@31 {
+        compatible = "imi,rdacm21";
+        reg = <0x31>, <0x41>;
+
+        port {
+          rdacm21_out0: endpoint {
+            remote-endpoint = <&max9286_in0>;
+          };
+        };
+      };
+    };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 9e67944bec9c..8566acae8cc5 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -435,6 +435,8 @@ patternProperties:
     description: Ingenieurburo Fur Ic-Technologie (I/F/I)
   "^ilitek,.*":
     description: ILI Technology Corporation (ILITEK)
+  "^imi,.*":
+    description: Integrated Micro-Electronics Inc.
   "^img,.*":
     description: Imagination Technologies Ltd.
   "^incircuit,.*":
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v7 3/6] media: i2c: Add driver for RDACM20 camera module
  2020-02-16 10:50 [PATCH v7 0/6] RDACM20 and RDACM21 GMSL camera support Jacopo Mondi
  2020-02-16 10:51 ` [PATCH v7 1/6] media: i2c: max9286: Disable overlap window Jacopo Mondi
  2020-02-16 10:51 ` [PATCH v7 2/6] dt-bindings: media: i2c: Add bindings for IMI RDACM2x Jacopo Mondi
@ 2020-02-16 10:51 ` Jacopo Mondi
  2020-02-16 10:51 ` [PATCH v7 4/6] media: i2c: max9286: Adjust reverse channel amplitude Jacopo Mondi
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Jacopo Mondi @ 2020-02-16 10:51 UTC (permalink / raw)
  To: kieran.bingham+renesas, laurent.pinchart, niklas.soderlund
  Cc: Jacopo Mondi, linux-renesas-soc, Laurent Pinchart, Niklas Söderlund

The RDACM20 is a GMSL camera supporting 1280x800 resolution images
developed by IMI based on an Omnivision 10635 sensor and a Maxim MAX9271
GMSL serializer.

The GMSL link carries power, control (I2C) and video data over a
single coax cable.

The driver is split between the camera module driver (rdacm20.c) and
the serializer library driver (max9271.c) as the same serializer is
used in multiple camera modules. The usage of a library maximises code
reuse and reduces duplications.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

---
v2:
 - Fix MAINTAINERS entry

v3:
 - Use new V4L2_MBUS_CSI2_DPHY bus type
 - Remove 'always zero' error print
 - Fix module description

v5:
 - use sleep rather than busy loops for 10 ms delays
 - Return ov10635_set_regs directly
 - Use devm_kzalloc instead of kzalloc in probe()
 - Or in the flags: dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE
 - Ensure v4l2_ctrl_handler_free() is called
 - rdacm20_probe converted to use .probe_new and drop i2c device id
   tables
 - Remove rdacm20_g_mbus_config

v7:
 [Jacopo]
 - Break out max9271 from rdacm20 driver
 - Rework max9271 library to support rdacm21 as well as rdacm20
 - Adjust rdacm20 driver to use the new library
 [Kieran]
 - Use new i2c_new_dummy_device API
---
 MAINTAINERS                         |  12 +
 drivers/media/i2c/Kconfig           |  11 +
 drivers/media/i2c/Makefile          |   2 +
 drivers/media/i2c/max9271.c         | 338 ++++++++++
 drivers/media/i2c/max9271.h         | 217 +++++++
 drivers/media/i2c/rdacm20-ov10635.h | 953 ++++++++++++++++++++++++++++
 drivers/media/i2c/rdacm20.c         | 398 ++++++++++++
 7 files changed, 1931 insertions(+)
 create mode 100644 drivers/media/i2c/max9271.c
 create mode 100644 drivers/media/i2c/max9271.h
 create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
 create mode 100644 drivers/media/i2c/rdacm20.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 748607edd238..5633864d847b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14048,6 +14048,18 @@ S:	Supported
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev
 F:	tools/testing/selftests/rcutorture
 
+RDACM20 Camera Sensor
+M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
+M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
+M:	Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
+M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/rdacm2x-gmsl.yaml
+F:	drivers/media/i2c/rdacm20*
+F:	drivers/media/i2c/max9271.c
+F:	drivers/media/i2c/max9271.h
+
 RDC R-321X SoC
 M:	Florian Fainelli <florian@openwrt.org>
 S:	Maintained
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 32a4deb90617..8ec4a1915f8c 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -1047,6 +1047,17 @@ config VIDEO_DW9807_VCM
 
 endif
 
+config VIDEO_RDACM20
+	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"
 
 if MEDIA_CAMERA_SUPPORT
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 8896cf8bfc4f..693b7d742827 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -118,5 +118,7 @@ obj-$(CONFIG_VIDEO_IMX319)	+= imx319.o
 obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
 obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
 obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
+rdacm20-camera_module-objs	:= rdacm20.o max9271.o
+obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20-camera_module.o
 
 obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c
new file mode 100644
index 000000000000..9ddebe88585a
--- /dev/null
+++ b/drivers/media/i2c/max9271.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017-2020 Jacopo Mondi
+ * Copyright (C) 2017-2018 Kieran Bingham
+ * Copyright (C) 2017-2018 Laurent Pinchart
+ * Copyright (C) 2017-2018 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ * This file exports functions to control Maxim MAX9271 GMSL serializer
+ * chip. This is not a self-contained driver, as MAX9271 is usually embedded in
+ * camera modules with at least one image sensor and optional additional
+ * components, such as uController units or ISPs/DSPs.
+ *
+ * Driver for the camera modules (ie rdacm20/21) are expected to use functions
+ * exported from this library file to maximize code re-use across drivers.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+
+#include "max9271.h"
+
+static int max9271_read(struct max9271_device *dev, u8 reg)
+{
+	int ret;
+
+	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
+
+	ret = i2c_smbus_read_byte_data(dev->client, reg);
+	if (ret < 0)
+		dev_dbg(&dev->client->dev,
+			"%s: register 0x%02x read failed (%d)\n",
+			__func__, reg, ret);
+
+	return ret;
+}
+
+static int max9271_write(struct max9271_device *dev, u8 reg, u8 val)
+{
+	int ret;
+
+	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
+
+	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;
+}
+
+/*
+ * max9271_pclk_detect() - Detect valid pixel clock from image sensor
+ *
+ * Wait up to 10ms for a valid pixel clock.
+ *
+ * Returns 0 for success, < 0 for pixel clock not properly detected
+ */
+static int max9271_pclk_detect(struct max9271_device *dev)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < 100; i++) {
+		ret = max9271_read(dev, 0x15);
+		if (ret < 0)
+			return ret;
+
+		if (ret & MAX9271_PCLKDET)
+			return 0;
+
+		usleep_range(50, 100);
+	}
+
+	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
+	return -EIO;
+}
+
+int max9271_set_serial_link(struct max9271_device *dev, bool enable)
+{
+	int ret;
+	u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN;
+
+	if (enable) {
+		ret = max9271_pclk_detect(dev);
+		if (ret)
+			return ret;
+
+		val |= MAX9271_SEREN;
+
+	} else {
+		val |= MAX9271_CLINKEN;
+	}
+
+	max9271_write(dev, 0x04, val);
+	usleep_range(2000, 2500);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_serial_link);
+
+int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config)
+{
+	int ret;
+
+	ret = max9271_write(dev, 0x0d, i2c_config);
+	if (ret)
+		return ret;
+
+	/* The delay required after an I2C bus configuration change is not
+	 * characterized in the serializer manual. Sleep up to 5msec to
+	 * stay safe.
+	 */
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_configure_i2c);
+
+int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
+{
+	int ret = max9271_read(dev, 0x08);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Enable or disable reverse channel high threshold to increase
+	 * immunity to power supply noise.
+	 */
+	max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
+	usleep_range(2000, 2500);
+
+	return 0;
+}
+
+int max9271_configure_gmsl_link(struct max9271_device *dev)
+{
+	/*
+	 * Disable the serial link and enable the configuration link to allow
+	 * the control channel to operate in a low-speed mode in the absence of
+	 * the serial link clock.
+	 */
+	max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
+		      MAX9271_FWDCCEN);
+
+	/*
+	 * The serializer temporarily disables the reverse control channel for
+	 * 350µs after starting/stopping the forward serial link, but the
+	 * deserializer synchronization time isn't clearly documented.
+	 *
+	 * According to the serializer datasheet we should wait 3ms, while
+	 * according to the deserializer datasheet we should wait 5ms.
+	 *
+	 * Short delays here appear to show bit-errors in the writes following.
+	 * Therefore a conservative delay seems best here.
+	 */
+	usleep_range(5000, 8000);
+
+	/*
+	 * Configure the GMSL link:
+	 *
+	 * - Double input mode, high data rate, 24-bit mode
+	 * - Latch input data on PCLKIN rising edge
+	 * - Enable HS/VS encoding
+	 * - 1-bit parity error detection
+	 */
+	max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
+		      MAX9271_EDC_1BIT_PARITY);
+	usleep_range(5000, 8000);
+
+	/*
+	 * Adjust spread spectrum to +4% and auto-detect pixel clock
+	 * and serial link rate.
+	 */
+	max9271_write(dev, 0x02, MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
+		      MAX9271_PCLK_AUTODETECT | MAX9271_SERIAL_AUTODETECT);
+	usleep_range(5000, 8000);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_configure_gmsl_link);
+
+int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+	int ret = max9271_read(dev, 0x0f);
+	if (ret < 0)
+		return 0;
+
+	ret |= gpio_mask;
+	ret = max9271_write(dev, 0x0f, ret);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret);
+		return ret;
+	}
+
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_gpio);
+
+int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+	int ret = max9271_read(dev, 0x0f);
+	if (ret < 0)
+		return 0;
+
+	ret &= ~gpio_mask;
+	ret = max9271_write(dev, 0x0f, ret);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret);
+		return ret;
+	}
+
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_gpio);
+
+int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+	int ret = max9271_read(dev, 0x0f);
+	if (ret < 0)
+		return 0;
+
+	/* BIT(0) reserved: GPO is always enabled. */
+	ret |= gpio_mask | BIT(0);
+	ret = max9271_write(dev, 0x0e, ret);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret);
+		return ret;
+	}
+
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_enable_gpios);
+
+int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+	int ret = max9271_read(dev, 0x0f);
+	if (ret < 0)
+		return 0;
+
+	/* BIT(0) reserved: GPO cannot be disabled */
+	ret &= (~gpio_mask | BIT(0));
+	ret = max9271_write(dev, 0x0e, ret);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "Failed to disable gpio (%d)\n", ret);
+		return ret;
+	}
+
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_enable_gpios);
+
+int max9271_verify_id(struct max9271_device *dev)
+{
+	int ret;
+
+	ret = max9271_read(dev, 0x1e);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
+			ret);
+		return ret;
+	}
+
+	if (ret != MAX9271_ID) {
+		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
+			ret);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_verify_id);
+
+int max9271_set_address(struct max9271_device *dev, u8 addr)
+{
+	int ret;
+
+	ret = max9271_write(dev, 0x00, addr << 1);
+	if (ret < 0) {
+		dev_err(&dev->client->dev,
+			"MAX9271 I2C address change failed (%d)\n", ret);
+		return ret;
+	}
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_address);
+
+int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr)
+{
+	int ret;
+
+	ret = max9271_write(dev, 0x01, addr << 1);
+	if (ret < 0) {
+		dev_err(&dev->client->dev,
+			"MAX9271 deserializer address set failed (%d)\n", ret);
+		return ret;
+	}
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_address);
+
+int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest)
+{
+	int ret;
+
+	ret = max9271_write(dev, 0x09, source << 1);
+	if (ret < 0) {
+		dev_err(&dev->client->dev,
+			"MAX9271 I2C translation setup failed (%d)\n", ret);
+		return ret;
+	}
+	usleep_range(3500, 5000);
+
+	ret = max9271_write(dev, 0x0a, dest << 1);
+	if (ret < 0) {
+		dev_err(&dev->client->dev,
+			"MAX9271 I2C translation setup failed (%d)\n", ret);
+		return ret;
+	}
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_translation);
diff --git a/drivers/media/i2c/max9271.h b/drivers/media/i2c/max9271.h
new file mode 100644
index 000000000000..a4bec81dbfb4
--- /dev/null
+++ b/drivers/media/i2c/max9271.h
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2017-2020 Jacopo Mondi
+ * Copyright (C) 2017-2018 Kieran Bingham
+ * Copyright (C) 2017-2018 Laurent Pinchart
+ * Copyright (C) 2017-2018 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ */
+
+#include <linux/i2c.h>
+
+#define MAX9271_DEFAULT_ADDR	0x40
+
+/* Register 0x02 */
+#define MAX9271_SPREAD_SPECT_0		(0 << 5)
+#define MAX9271_SPREAD_SPECT_05		(1 << 5)
+#define MAX9271_SPREAD_SPECT_15		(2 << 5)
+#define MAX9271_SPREAD_SPECT_1		(5 << 5)
+#define MAX9271_SPREAD_SPECT_2		(3 << 5)
+#define MAX9271_SPREAD_SPECT_3		(6 << 5)
+#define MAX9271_SPREAD_SPECT_4		(7 << 5)
+#define MAX9271_R02_RES			BIT(4)
+#define MAX9271_PCLK_AUTODETECT		(3 << 2)
+#define MAX9271_SERIAL_AUTODETECT	(0x03)
+
+/* Register 0x04 */
+#define MAX9271_SEREN			BIT(7)
+#define MAX9271_CLINKEN			BIT(6)
+#define MAX9271_PRBSEN			BIT(5)
+#define MAX9271_SLEEP			BIT(4)
+#define MAX9271_INTTYPE_I2C		(0 << 2)
+#define MAX9271_INTTYPE_UART		(1 << 2)
+#define MAX9271_INTTYPE_NONE		(2 << 2)
+#define MAX9271_REVCCEN			BIT(1)
+#define MAX9271_FWDCCEN			BIT(0)
+/* Register 0x07 */
+#define MAX9271_DBL			BIT(7)
+#define MAX9271_DRS			BIT(6)
+#define MAX9271_BWS			BIT(5)
+#define MAX9271_ES			BIT(4)
+#define MAX9271_HVEN			BIT(2)
+#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
+#define MAX9271_EDC_6BIT_CRC		(1 << 0)
+#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
+/* Register 0x08 */
+#define MAX9271_INVVS			BIT(7)
+#define MAX9271_INVHS			BIT(6)
+#define MAX9271_REV_LOGAIN		BIT(3)
+#define MAX9271_REV_HIVTH		BIT(0)
+/* Register 0x09 */
+#define MAX9271_ID			0x09
+/* Register 0x0d */
+#define MAX9271_I2CLOCACK		BIT(7)
+#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
+#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
+#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
+#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
+#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
+#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
+#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
+#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
+#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
+#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
+#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
+#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
+#define MAX9271_I2CSLVTO_NONE		(3 << 0)
+#define MAX9271_I2CSLVTO_1024US		(2 << 0)
+#define MAX9271_I2CSLVTO_256US		(1 << 0)
+#define MAX9271_I2CSLVTO_64US		(0 << 0)
+/* Register 0x0f */
+#define MAX9271_GPIO5OUT		BIT(5)
+#define MAX9271_GPIO4OUT		BIT(4)
+#define MAX9271_GPIO3OUT		BIT(3)
+#define MAX9271_GPIO2OUT		BIT(2)
+#define MAX9271_GPIO1OUT		BIT(1)
+#define MAX9271_GPO			BIT(0)
+/* Register 0x15 */
+#define MAX9271_PCLKDET			BIT(0)
+
+/**
+ * struct max9271_device - max9271 device
+ * @client: The i2c client for the max9271 instance
+ */
+struct max9271_device {
+	struct i2c_client *client;
+};
+
+/**
+ * max9271_set_serial_link() - Enable/disable serial link
+ * @dev: The max9271 device
+ * @enable: Serial link enable/disable flag
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_serial_link(struct max9271_device *dev, bool enable);
+
+/**
+ * max9271_configure_i2c() - Configure I2C bus parameters
+ * @dev: The max9271 device
+ * @i2c_config: The I2C bus configuration bit mask
+ *
+ * Configure MAX9271 I2C interface. The bus configuration provided in the
+ * @i2c_config parameter shall be assembled using bit values defined by the
+ * MAX9271_I2C* macros.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config);
+
+/**
+ * max9271_set_high_threshold() - Enable or disable reverse channel high
+ *				  threshold
+ * @dev: The max9271 device
+ * @enable: High threshold enable/disable flag
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_high_threshold(struct max9271_device *dev, bool enable);
+
+/**
+ * max9271_configure_gmsl_link() - Configure the GMSL link
+ * @dev: The max9271 device
+ *
+ * FIXME: the GMSL link configuration is currently hardcoded and performed
+ * by programming registers 0x04, 0x07 and 0x02.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_configure_gmsl_link(struct max9271_device *dev);
+
+/**
+ * max9271_set_gpios() - Set gpio lines to physical high value
+ * @dev: The max9271 device
+ * @gpio_mask: The mask of gpio lines to set to high value
+ *
+ * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]*
+ * bit values.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask);
+
+/**
+ * max9271_clear_gpios() - Set gpio lines to physical low value
+ * @dev: The max9271 device
+ * @gpio_mask: The mask of gpio lines to set to low value
+ *
+ * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]*
+ * bit values.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask);
+
+/**
+ * max9271_enable_gpios() - Enable gpio lines
+ * @dev: The max9271 device
+ * @gpio_mask: The mask of gpio lines to enable
+ *
+ * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO*
+ * bit values. GPO line is always enabled by default.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask);
+
+/**
+ * max9271_disable_gpios() - Disable gpio lines
+ * @dev: The max9271 device
+ * @gpio_mask: The mask of gpio lines to disable
+ *
+ * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO*
+ * bit values. GPO line is always enabled by default and cannot be disabled.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask);
+
+/**
+ * max9271_verify_id() - Read and verify MAX9271 id
+ * @dev: The max9271 device
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_verify_id(struct max9271_device *dev);
+
+/**
+ * max9271_set_address() - Program a new I2C address
+ * @dev: The max9271 device
+ * @addr: The new I2C address in 7-bit format
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_address(struct max9271_device *dev, u8 addr);
+
+/**
+ * max9271_set_deserializer_address() - Program the remote deserializer address
+ * @dev: The max9271 device
+ * @addr: The deserializer I2C address in 7-bit format
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr);
+
+/**
+ * max9271_set_translation() - Program I2C address translation
+ * @dev: The max9271 device
+ * @source: The I2C source address
+ * @dest: The I2C destination address
+ *
+ * Program address translation from @source to @dest. This is required to
+ * communicate with local devices that do not support address reprogramming.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest);
diff --git a/drivers/media/i2c/rdacm20-ov10635.h b/drivers/media/i2c/rdacm20-ov10635.h
new file mode 100644
index 000000000000..3c53a3262ee2
--- /dev/null
+++ b/drivers/media/i2c/rdacm20-ov10635.h
@@ -0,0 +1,953 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * IMI RDACM20 camera OV10635 sensor register initialization values
+ *
+ * Copyright (C) 2017-2018 Jacopo Mondi
+ * Copyright (C) 2017-2018 Kieran Bingham
+ * Copyright (C) 2017-2018 Laurent Pinchart
+ * Copyright (C) 2017-2018 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ */
+
+/*
+ * Generated by the OmniVision ov10635 sensor camera wizard for
+ * 1280x800@30/UYVY/BT601/8bit.
+ */
+
+#ifndef __RDACM20_OV10635_H__
+#define __RDACM20_OV10635_H__
+
+#define OV10635_SENSOR_WIDTH		1312
+#define OV10635_SENSOR_HEIGHT		814
+
+#define OV10635_MAX_WIDTH		1280
+#define OV10635_MAX_HEIGHT		800
+
+/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */
+#define OV10635_HTS			1572
+/* FPS = 29,9998 */
+#define OV10635_VTS			933
+
+struct ov10635_reg {
+	u16	reg;
+	u8	val;
+};
+
+static const struct ov10635_reg ov10635_regs_wizard[] = {
+	{ 0x301b, 0xff },
+	{ 0x301c, 0xff },
+	{ 0x301a, 0xff },
+	{ 0x3011, 0x42 },
+	{ 0x6900, 0x0c },
+	{ 0x6901, 0x19 },
+	{ 0x3503, 0x10 },
+	{ 0x3025, 0x03 },
+	{ 0x3003, 0x16 },
+	{ 0x3004, 0x30 },
+	{ 0x3005, 0x40 },
+	{ 0x3006, 0x91 },
+	{ 0x3600, 0x74 },
+	{ 0x3601, 0x2b },
+	{ 0x3612, 0x00 },
+	{ 0x3611, 0x67 },
+	{ 0x3633, 0xca },
+	{ 0x3602, 0xaf },
+	{ 0x3603, 0x04 },
+	{ 0x3630, 0x28 },
+	{ 0x3631, 0x16 },
+	{ 0x3714, 0x10 },
+	{ 0x371d, 0x01 },
+	{ 0x4300, 0x3a },
+	{ 0x3007, 0x01 },
+	{ 0x3024, 0x03 },
+	{ 0x3020, 0x0a },
+	{ 0x3702, 0x0d },
+	{ 0x3703, 0x20 },
+	{ 0x3704, 0x15 },
+	{ 0x3709, 0xa8 },
+	{ 0x370c, 0xc7 },
+	{ 0x370d, 0x80 },
+	{ 0x3712, 0x00 },
+	{ 0x3713, 0x20 },
+	{ 0x3715, 0x04 },
+	{ 0x381d, 0x40 },
+	{ 0x381c, 0x00 },
+	{ 0x3822, 0x50 },
+	{ 0x3824, 0x10 },
+	{ 0x3815, 0x8c },
+	{ 0x3804, 0x05 },
+	{ 0x3805, 0x1f },
+	{ 0x3800, 0x00 },
+	{ 0x3801, 0x00 },
+	{ 0x3806, 0x03 },
+	{ 0x3807, 0x28 },
+	{ 0x3802, 0x00 },
+	{ 0x3803, 0x07 },
+	{ 0x3808, 0x05 },
+	{ 0x3809, 0x00 },
+	{ 0x380a, 0x03 },
+	{ 0x380b, 0x20 },
+	{ 0x380c, OV10635_HTS >> 8 },
+	{ 0x380d, OV10635_HTS & 0xff },
+	{ 0x380e, OV10635_VTS >> 8 },
+	{ 0x380f, OV10635_VTS & 0xff },
+	{ 0x3813, 0x02 },
+	{ 0x3811, 0x08 },
+	{ 0x381f, 0x0c },
+	{ 0x3819, 0x04 },
+	{ 0x3804, 0x01 },
+	{ 0x3805, 0x00 },
+	{ 0x3828, 0x03 },
+	{ 0x3829, 0x10 },
+	{ 0x382a, 0x10 },
+	{ 0x3621, 0x63 },
+	{ 0x5005, 0x08 },
+	{ 0x56d5, 0x00 },
+	{ 0x56d6, 0x80 },
+	{ 0x56d7, 0x00 },
+	{ 0x56d8, 0x00 },
+	{ 0x56d9, 0x00 },
+	{ 0x56da, 0x80 },
+	{ 0x56db, 0x00 },
+	{ 0x56dc, 0x00 },
+	{ 0x56e8, 0x00 },
+	{ 0x56e9, 0x7f },
+	{ 0x56ea, 0x00 },
+	{ 0x56eb, 0x7f },
+	{ 0x5100, 0x00 },
+	{ 0x5101, 0x80 },
+	{ 0x5102, 0x00 },
+	{ 0x5103, 0x80 },
+	{ 0x5104, 0x00 },
+	{ 0x5105, 0x80 },
+	{ 0x5106, 0x00 },
+	{ 0x5107, 0x80 },
+	{ 0x5108, 0x00 },
+	{ 0x5109, 0x00 },
+	{ 0x510a, 0x00 },
+	{ 0x510b, 0x00 },
+	{ 0x510c, 0x00 },
+	{ 0x510d, 0x00 },
+	{ 0x510e, 0x00 },
+	{ 0x510f, 0x00 },
+	{ 0x5110, 0x00 },
+	{ 0x5111, 0x80 },
+	{ 0x5112, 0x00 },
+	{ 0x5113, 0x80 },
+	{ 0x5114, 0x00 },
+	{ 0x5115, 0x80 },
+	{ 0x5116, 0x00 },
+	{ 0x5117, 0x80 },
+	{ 0x5118, 0x00 },
+	{ 0x5119, 0x00 },
+	{ 0x511a, 0x00 },
+	{ 0x511b, 0x00 },
+	{ 0x511c, 0x00 },
+	{ 0x511d, 0x00 },
+	{ 0x511e, 0x00 },
+	{ 0x511f, 0x00 },
+	{ 0x56d0, 0x00 },
+	{ 0x5006, 0x04 },
+	{ 0x5608, 0x05 },
+	{ 0x52d7, 0x06 },
+	{ 0x528d, 0x08 },
+	{ 0x5293, 0x12 },
+	{ 0x52d3, 0x12 },
+	{ 0x5288, 0x06 },
+	{ 0x5289, 0x20 },
+	{ 0x52c8, 0x06 },
+	{ 0x52c9, 0x20 },
+	{ 0x52cd, 0x04 },
+	{ 0x5381, 0x00 },
+	{ 0x5382, 0xff },
+	{ 0x5589, 0x76 },
+	{ 0x558a, 0x47 },
+	{ 0x558b, 0xef },
+	{ 0x558c, 0xc9 },
+	{ 0x558d, 0x49 },
+	{ 0x558e, 0x30 },
+	{ 0x558f, 0x67 },
+	{ 0x5590, 0x3f },
+	{ 0x5591, 0xf0 },
+	{ 0x5592, 0x10 },
+	{ 0x55a2, 0x6d },
+	{ 0x55a3, 0x55 },
+	{ 0x55a4, 0xc3 },
+	{ 0x55a5, 0xb5 },
+	{ 0x55a6, 0x43 },
+	{ 0x55a7, 0x38 },
+	{ 0x55a8, 0x5f },
+	{ 0x55a9, 0x4b },
+	{ 0x55aa, 0xf0 },
+	{ 0x55ab, 0x10 },
+	{ 0x5581, 0x52 },
+	{ 0x5300, 0x01 },
+	{ 0x5301, 0x00 },
+	{ 0x5302, 0x00 },
+	{ 0x5303, 0x0e },
+	{ 0x5304, 0x00 },
+	{ 0x5305, 0x0e },
+	{ 0x5306, 0x00 },
+	{ 0x5307, 0x36 },
+	{ 0x5308, 0x00 },
+	{ 0x5309, 0xd9 },
+	{ 0x530a, 0x00 },
+	{ 0x530b, 0x0f },
+	{ 0x530c, 0x00 },
+	{ 0x530d, 0x2c },
+	{ 0x530e, 0x00 },
+	{ 0x530f, 0x59 },
+	{ 0x5310, 0x00 },
+	{ 0x5311, 0x7b },
+	{ 0x5312, 0x00 },
+	{ 0x5313, 0x22 },
+	{ 0x5314, 0x00 },
+	{ 0x5315, 0xd5 },
+	{ 0x5316, 0x00 },
+	{ 0x5317, 0x13 },
+	{ 0x5318, 0x00 },
+	{ 0x5319, 0x18 },
+	{ 0x531a, 0x00 },
+	{ 0x531b, 0x26 },
+	{ 0x531c, 0x00 },
+	{ 0x531d, 0xdc },
+	{ 0x531e, 0x00 },
+	{ 0x531f, 0x02 },
+	{ 0x5320, 0x00 },
+	{ 0x5321, 0x24 },
+	{ 0x5322, 0x00 },
+	{ 0x5323, 0x56 },
+	{ 0x5324, 0x00 },
+	{ 0x5325, 0x85 },
+	{ 0x5326, 0x00 },
+	{ 0x5327, 0x20 },
+	{ 0x5609, 0x01 },
+	{ 0x560a, 0x40 },
+	{ 0x560b, 0x01 },
+	{ 0x560c, 0x40 },
+	{ 0x560d, 0x00 },
+	{ 0x560e, 0xfa },
+	{ 0x560f, 0x00 },
+	{ 0x5610, 0xfa },
+	{ 0x5611, 0x02 },
+	{ 0x5612, 0x80 },
+	{ 0x5613, 0x02 },
+	{ 0x5614, 0x80 },
+	{ 0x5615, 0x01 },
+	{ 0x5616, 0x2c },
+	{ 0x5617, 0x01 },
+	{ 0x5618, 0x2c },
+	{ 0x563b, 0x01 },
+	{ 0x563c, 0x01 },
+	{ 0x563d, 0x01 },
+	{ 0x563e, 0x01 },
+	{ 0x563f, 0x03 },
+	{ 0x5640, 0x03 },
+	{ 0x5641, 0x03 },
+	{ 0x5642, 0x05 },
+	{ 0x5643, 0x09 },
+	{ 0x5644, 0x05 },
+	{ 0x5645, 0x05 },
+	{ 0x5646, 0x05 },
+	{ 0x5647, 0x05 },
+	{ 0x5651, 0x00 },
+	{ 0x5652, 0x80 },
+	{ 0x521a, 0x01 },
+	{ 0x521b, 0x03 },
+	{ 0x521c, 0x06 },
+	{ 0x521d, 0x0a },
+	{ 0x521e, 0x0e },
+	{ 0x521f, 0x12 },
+	{ 0x5220, 0x16 },
+	{ 0x5223, 0x02 },
+	{ 0x5225, 0x04 },
+	{ 0x5227, 0x08 },
+	{ 0x5229, 0x0c },
+	{ 0x522b, 0x12 },
+	{ 0x522d, 0x18 },
+	{ 0x522f, 0x1e },
+	{ 0x5241, 0x04 },
+	{ 0x5242, 0x01 },
+	{ 0x5243, 0x03 },
+	{ 0x5244, 0x06 },
+	{ 0x5245, 0x0a },
+	{ 0x5246, 0x0e },
+	{ 0x5247, 0x12 },
+	{ 0x5248, 0x16 },
+	{ 0x524a, 0x03 },
+	{ 0x524c, 0x04 },
+	{ 0x524e, 0x08 },
+	{ 0x5250, 0x0c },
+	{ 0x5252, 0x12 },
+	{ 0x5254, 0x18 },
+	{ 0x5256, 0x1e },
+	/* fifo_line_length = 2*hts */
+	{ 0x4606, (2 * OV10635_HTS) >> 8 },
+	{ 0x4607, (2 * OV10635_HTS) & 0xff },
+	/* fifo_hsync_start = 2*(hts - xres) */
+	{ 0x460a, (2 * (OV10635_HTS - OV10635_MAX_WIDTH)) >> 8 },
+	{ 0x460b, (2 * (OV10635_HTS - OV10635_MAX_WIDTH)) & 0xff },
+	{ 0x460c, 0x00 },
+	{ 0x4620, 0x0e },
+	/* BT601: 0x08 is also acceptable as HS/VS mode */
+	{ 0x4700, 0x04 },
+	{ 0x4701, 0x00 },
+	{ 0x4702, 0x01 },
+	{ 0x4004, 0x04 },
+	{ 0x4005, 0x18 },
+	{ 0x4001, 0x06 },
+	{ 0x4050, 0x22 },
+	{ 0x4051, 0x24 },
+	{ 0x4052, 0x02 },
+	{ 0x4057, 0x9c },
+	{ 0x405a, 0x00 },
+	{ 0x4202, 0x02 },
+	{ 0x3023, 0x10 },
+	{ 0x0100, 0x01 },
+	{ 0x0100, 0x01 },
+	{ 0x6f10, 0x07 },
+	{ 0x6f11, 0x82 },
+	{ 0x6f12, 0x04 },
+	{ 0x6f13, 0x00 },
+	{ 0xd000, 0x19 },
+	{ 0xd001, 0xa0 },
+	{ 0xd002, 0x00 },
+	{ 0xd003, 0x01 },
+	{ 0xd004, 0xa9 },
+	{ 0xd005, 0xad },
+	{ 0xd006, 0x10 },
+	{ 0xd007, 0x40 },
+	{ 0xd008, 0x44 },
+	{ 0xd009, 0x00 },
+	{ 0xd00a, 0x68 },
+	{ 0xd00b, 0x00 },
+	{ 0xd00c, 0x15 },
+	{ 0xd00d, 0x00 },
+	{ 0xd00e, 0x00 },
+	{ 0xd00f, 0x00 },
+	{ 0xd040, 0x9c },
+	{ 0xd041, 0x21 },
+	{ 0xd042, 0xff },
+	{ 0xd043, 0xf8 },
+	{ 0xd044, 0xd4 },
+	{ 0xd045, 0x01 },
+	{ 0xd046, 0x48 },
+	{ 0xd047, 0x00 },
+	{ 0xd048, 0xd4 },
+	{ 0xd049, 0x01 },
+	{ 0xd04a, 0x50 },
+	{ 0xd04b, 0x04 },
+	{ 0xd04c, 0x18 },
+	{ 0xd04d, 0x60 },
+	{ 0xd04e, 0x00 },
+	{ 0xd04f, 0x01 },
+	{ 0xd050, 0xa8 },
+	{ 0xd051, 0x63 },
+	{ 0xd052, 0x02 },
+	{ 0xd053, 0xa4 },
+	{ 0xd054, 0x85 },
+	{ 0xd055, 0x43 },
+	{ 0xd056, 0x00 },
+	{ 0xd057, 0x00 },
+	{ 0xd058, 0x18 },
+	{ 0xd059, 0x60 },
+	{ 0xd05a, 0x00 },
+	{ 0xd05b, 0x01 },
+	{ 0xd05c, 0xa8 },
+	{ 0xd05d, 0x63 },
+	{ 0xd05e, 0x03 },
+	{ 0xd05f, 0xf0 },
+	{ 0xd060, 0x98 },
+	{ 0xd061, 0xa3 },
+	{ 0xd062, 0x00 },
+	{ 0xd063, 0x00 },
+	{ 0xd064, 0x8c },
+	{ 0xd065, 0x6a },
+	{ 0xd066, 0x00 },
+	{ 0xd067, 0x6e },
+	{ 0xd068, 0xe5 },
+	{ 0xd069, 0x85 },
+	{ 0xd06a, 0x18 },
+	{ 0xd06b, 0x00 },
+	{ 0xd06c, 0x10 },
+	{ 0xd06d, 0x00 },
+	{ 0xd06e, 0x00 },
+	{ 0xd06f, 0x10 },
+	{ 0xd070, 0x9c },
+	{ 0xd071, 0x80 },
+	{ 0xd072, 0x00 },
+	{ 0xd073, 0x03 },
+	{ 0xd074, 0x18 },
+	{ 0xd075, 0x60 },
+	{ 0xd076, 0x00 },
+	{ 0xd077, 0x01 },
+	{ 0xd078, 0xa8 },
+	{ 0xd079, 0x63 },
+	{ 0xd07a, 0x07 },
+	{ 0xd07b, 0x80 },
+	{ 0xd07c, 0x07 },
+	{ 0xd07d, 0xff },
+	{ 0xd07e, 0xf9 },
+	{ 0xd07f, 0x03 },
+	{ 0xd080, 0x8c },
+	{ 0xd081, 0x63 },
+	{ 0xd082, 0x00 },
+	{ 0xd083, 0x00 },
+	{ 0xd084, 0xa5 },
+	{ 0xd085, 0x6b },
+	{ 0xd086, 0x00 },
+	{ 0xd087, 0xff },
+	{ 0xd088, 0x18 },
+	{ 0xd089, 0x80 },
+	{ 0xd08a, 0x00 },
+	{ 0xd08b, 0x01 },
+	{ 0xd08c, 0xa8 },
+	{ 0xd08d, 0x84 },
+	{ 0xd08e, 0x01 },
+	{ 0xd08f, 0x04 },
+	{ 0xd090, 0xe1 },
+	{ 0xd091, 0x6b },
+	{ 0xd092, 0x58 },
+	{ 0xd093, 0x00 },
+	{ 0xd094, 0x94 },
+	{ 0xd095, 0x6a },
+	{ 0xd096, 0x00 },
+	{ 0xd097, 0x70 },
+	{ 0xd098, 0xe1 },
+	{ 0xd099, 0x6b },
+	{ 0xd09a, 0x20 },
+	{ 0xd09b, 0x00 },
+	{ 0xd09c, 0x95 },
+	{ 0xd09d, 0x6b },
+	{ 0xd09e, 0x00 },
+	{ 0xd09f, 0x00 },
+	{ 0xd0a0, 0xe4 },
+	{ 0xd0a1, 0x8b },
+	{ 0xd0a2, 0x18 },
+	{ 0xd0a3, 0x00 },
+	{ 0xd0a4, 0x0c },
+	{ 0xd0a5, 0x00 },
+	{ 0xd0a6, 0x00 },
+	{ 0xd0a7, 0x23 },
+	{ 0xd0a8, 0x15 },
+	{ 0xd0a9, 0x00 },
+	{ 0xd0aa, 0x00 },
+	{ 0xd0ab, 0x00 },
+	{ 0xd0ac, 0x18 },
+	{ 0xd0ad, 0x60 },
+	{ 0xd0ae, 0x80 },
+	{ 0xd0af, 0x06 },
+	{ 0xd0b0, 0xa8 },
+	{ 0xd0b1, 0x83 },
+	{ 0xd0b2, 0x40 },
+	{ 0xd0b3, 0x08 },
+	{ 0xd0b4, 0xa8 },
+	{ 0xd0b5, 0xe3 },
+	{ 0xd0b6, 0x38 },
+	{ 0xd0b7, 0x2a },
+	{ 0xd0b8, 0xa8 },
+	{ 0xd0b9, 0xc3 },
+	{ 0xd0ba, 0x40 },
+	{ 0xd0bb, 0x09 },
+	{ 0xd0bc, 0xa8 },
+	{ 0xd0bd, 0xa3 },
+	{ 0xd0be, 0x38 },
+	{ 0xd0bf, 0x29 },
+	{ 0xd0c0, 0x8c },
+	{ 0xd0c1, 0x65 },
+	{ 0xd0c2, 0x00 },
+	{ 0xd0c3, 0x00 },
+	{ 0xd0c4, 0xd8 },
+	{ 0xd0c5, 0x04 },
+	{ 0xd0c6, 0x18 },
+	{ 0xd0c7, 0x00 },
+	{ 0xd0c8, 0x8c },
+	{ 0xd0c9, 0x67 },
+	{ 0xd0ca, 0x00 },
+	{ 0xd0cb, 0x00 },
+	{ 0xd0cc, 0xd8 },
+	{ 0xd0cd, 0x06 },
+	{ 0xd0ce, 0x18 },
+	{ 0xd0cf, 0x00 },
+	{ 0xd0d0, 0x18 },
+	{ 0xd0d1, 0x60 },
+	{ 0xd0d2, 0x80 },
+	{ 0xd0d3, 0x06 },
+	{ 0xd0d4, 0xa8 },
+	{ 0xd0d5, 0xe3 },
+	{ 0xd0d6, 0x67 },
+	{ 0xd0d7, 0x02 },
+	{ 0xd0d8, 0xa9 },
+	{ 0xd0d9, 0x03 },
+	{ 0xd0da, 0x67 },
+	{ 0xd0db, 0x03 },
+	{ 0xd0dc, 0xa8 },
+	{ 0xd0dd, 0xc3 },
+	{ 0xd0de, 0x3d },
+	{ 0xd0df, 0x05 },
+	{ 0xd0e0, 0x8c },
+	{ 0xd0e1, 0x66 },
+	{ 0xd0e2, 0x00 },
+	{ 0xd0e3, 0x00 },
+	{ 0xd0e4, 0xb8 },
+	{ 0xd0e5, 0x63 },
+	{ 0xd0e6, 0x00 },
+	{ 0xd0e7, 0x18 },
+	{ 0xd0e8, 0xb8 },
+	{ 0xd0e9, 0x63 },
+	{ 0xd0ea, 0x00 },
+	{ 0xd0eb, 0x98 },
+	{ 0xd0ec, 0xbc },
+	{ 0xd0ed, 0x03 },
+	{ 0xd0ee, 0x00 },
+	{ 0xd0ef, 0x00 },
+	{ 0xd0f0, 0x10 },
+	{ 0xd0f1, 0x00 },
+	{ 0xd0f2, 0x00 },
+	{ 0xd0f3, 0x16 },
+	{ 0xd0f4, 0xb8 },
+	{ 0xd0f5, 0x83 },
+	{ 0xd0f6, 0x00 },
+	{ 0xd0f7, 0x19 },
+	{ 0xd0f8, 0x8c },
+	{ 0xd0f9, 0x67 },
+	{ 0xd0fa, 0x00 },
+	{ 0xd0fb, 0x00 },
+	{ 0xd0fc, 0xb8 },
+	{ 0xd0fd, 0xa4 },
+	{ 0xd0fe, 0x00 },
+	{ 0xd0ff, 0x98 },
+	{ 0xd100, 0xb8 },
+	{ 0xd101, 0x83 },
+	{ 0xd102, 0x00 },
+	{ 0xd103, 0x08 },
+	{ 0xd104, 0x8c },
+	{ 0xd105, 0x68 },
+	{ 0xd106, 0x00 },
+	{ 0xd107, 0x00 },
+	{ 0xd108, 0xe0 },
+	{ 0xd109, 0x63 },
+	{ 0xd10a, 0x20 },
+	{ 0xd10b, 0x04 },
+	{ 0xd10c, 0xe0 },
+	{ 0xd10d, 0x65 },
+	{ 0xd10e, 0x18 },
+	{ 0xd10f, 0x00 },
+	{ 0xd110, 0xa4 },
+	{ 0xd111, 0x83 },
+	{ 0xd112, 0xff },
+	{ 0xd113, 0xff },
+	{ 0xd114, 0xb8 },
+	{ 0xd115, 0x64 },
+	{ 0xd116, 0x00 },
+	{ 0xd117, 0x48 },
+	{ 0xd118, 0xd8 },
+	{ 0xd119, 0x07 },
+	{ 0xd11a, 0x18 },
+	{ 0xd11b, 0x00 },
+	{ 0xd11c, 0xd8 },
+	{ 0xd11d, 0x08 },
+	{ 0xd11e, 0x20 },
+	{ 0xd11f, 0x00 },
+	{ 0xd120, 0x9c },
+	{ 0xd121, 0x60 },
+	{ 0xd122, 0x00 },
+	{ 0xd123, 0x00 },
+	{ 0xd124, 0xd8 },
+	{ 0xd125, 0x06 },
+	{ 0xd126, 0x18 },
+	{ 0xd127, 0x00 },
+	{ 0xd128, 0x00 },
+	{ 0xd129, 0x00 },
+	{ 0xd12a, 0x00 },
+	{ 0xd12b, 0x08 },
+	{ 0xd12c, 0x15 },
+	{ 0xd12d, 0x00 },
+	{ 0xd12e, 0x00 },
+	{ 0xd12f, 0x00 },
+	{ 0xd130, 0x8c },
+	{ 0xd131, 0x6a },
+	{ 0xd132, 0x00 },
+	{ 0xd133, 0x76 },
+	{ 0xd134, 0xbc },
+	{ 0xd135, 0x23 },
+	{ 0xd136, 0x00 },
+	{ 0xd137, 0x00 },
+	{ 0xd138, 0x13 },
+	{ 0xd139, 0xff },
+	{ 0xd13a, 0xff },
+	{ 0xd13b, 0xe6 },
+	{ 0xd13c, 0x18 },
+	{ 0xd13d, 0x60 },
+	{ 0xd13e, 0x80 },
+	{ 0xd13f, 0x06 },
+	{ 0xd140, 0x03 },
+	{ 0xd141, 0xff },
+	{ 0xd142, 0xff },
+	{ 0xd143, 0xdd },
+	{ 0xd144, 0xa8 },
+	{ 0xd145, 0x83 },
+	{ 0xd146, 0x40 },
+	{ 0xd147, 0x08 },
+	{ 0xd148, 0x85 },
+	{ 0xd149, 0x21 },
+	{ 0xd14a, 0x00 },
+	{ 0xd14b, 0x00 },
+	{ 0xd14c, 0x85 },
+	{ 0xd14d, 0x41 },
+	{ 0xd14e, 0x00 },
+	{ 0xd14f, 0x04 },
+	{ 0xd150, 0x44 },
+	{ 0xd151, 0x00 },
+	{ 0xd152, 0x48 },
+	{ 0xd153, 0x00 },
+	{ 0xd154, 0x9c },
+	{ 0xd155, 0x21 },
+	{ 0xd156, 0x00 },
+	{ 0xd157, 0x08 },
+	{ 0x6f0e, 0x03 },
+	{ 0x6f0f, 0x00 },
+	{ 0x460e, 0x08 },
+	{ 0x460f, 0x01 },
+	{ 0x4610, 0x00 },
+	{ 0x4611, 0x01 },
+	{ 0x4612, 0x00 },
+	{ 0x4613, 0x01 },
+	/* 8 bits */
+	{ 0x4605, 0x08 },
+	/* Swap data bits order [9:0] -> [0:9] */
+	{ 0x4709, 0x10 },
+	{ 0x4608, 0x00 },
+	{ 0x4609, 0x08 },
+	{ 0x6804, 0x00 },
+	{ 0x6805, 0x06 },
+	{ 0x6806, 0x00 },
+	{ 0x5120, 0x00 },
+	{ 0x3510, 0x00 },
+	{ 0x3504, 0x00 },
+	{ 0x6800, 0x00 },
+	{ 0x6f0d, 0x01 },
+	/* PCLK falling edge */
+	{ 0x4708, 0x01 },
+	{ 0x5000, 0xff },
+	{ 0x5001, 0xbf },
+	{ 0x5002, 0x7e },
+	{ 0x503d, 0x00 },
+	{ 0xc450, 0x01 },
+	{ 0xc452, 0x04 },
+	{ 0xc453, 0x00 },
+	{ 0xc454, 0x00 },
+	{ 0xc455, 0x01 },
+	{ 0xc456, 0x01 },
+	{ 0xc457, 0x00 },
+	{ 0xc458, 0x00 },
+	{ 0xc459, 0x00 },
+	{ 0xc45b, 0x00 },
+	{ 0xc45c, 0x01 },
+	{ 0xc45d, 0x00 },
+	{ 0xc45e, 0x00 },
+	{ 0xc45f, 0x00 },
+	{ 0xc460, 0x00 },
+	{ 0xc461, 0x01 },
+	{ 0xc462, 0x01 },
+	{ 0xc464, 0x03 },
+	{ 0xc465, 0x00 },
+	{ 0xc466, 0x8a },
+	{ 0xc467, 0x00 },
+	{ 0xc468, 0x86 },
+	{ 0xc469, 0x00 },
+	{ 0xc46a, 0x40 },
+	{ 0xc46b, 0x50 },
+	{ 0xc46c, 0x30 },
+	{ 0xc46d, 0x28 },
+	{ 0xc46e, 0x60 },
+	{ 0xc46f, 0x40 },
+	{ 0xc47c, 0x01 },
+	{ 0xc47d, 0x38 },
+	{ 0xc47e, 0x00 },
+	{ 0xc47f, 0x00 },
+	{ 0xc480, 0x00 },
+	{ 0xc481, 0xff },
+	{ 0xc482, 0x00 },
+	{ 0xc483, 0x40 },
+	{ 0xc484, 0x00 },
+	{ 0xc485, 0x18 },
+	{ 0xc486, 0x00 },
+	{ 0xc487, 0x18 },
+	{ 0xc488, (OV10635_VTS - 8) * 16 >> 8},
+	{ 0xc489, (OV10635_VTS - 8) * 16 & 0xff},
+	{ 0xc48a, (OV10635_VTS - 8) * 16 >> 8},
+	{ 0xc48b, (OV10635_VTS - 8) * 16 & 0xff},
+	{ 0xc48c, 0x00 },
+	{ 0xc48d, 0x04 },
+	{ 0xc48e, 0x00 },
+	{ 0xc48f, 0x04 },
+	{ 0xc490, 0x03 },
+	{ 0xc492, 0x20 },
+	{ 0xc493, 0x08 },
+	{ 0xc498, 0x02 },
+	{ 0xc499, 0x00 },
+	{ 0xc49a, 0x02 },
+	{ 0xc49b, 0x00 },
+	{ 0xc49c, 0x02 },
+	{ 0xc49d, 0x00 },
+	{ 0xc49e, 0x02 },
+	{ 0xc49f, 0x60 },
+	{ 0xc4a0, 0x03 },
+	{ 0xc4a1, 0x00 },
+	{ 0xc4a2, 0x04 },
+	{ 0xc4a3, 0x00 },
+	{ 0xc4a4, 0x00 },
+	{ 0xc4a5, 0x10 },
+	{ 0xc4a6, 0x00 },
+	{ 0xc4a7, 0x40 },
+	{ 0xc4a8, 0x00 },
+	{ 0xc4a9, 0x80 },
+	{ 0xc4aa, 0x0d },
+	{ 0xc4ab, 0x00 },
+	{ 0xc4ac, 0x0f },
+	{ 0xc4ad, 0xc0 },
+	{ 0xc4b4, 0x01 },
+	{ 0xc4b5, 0x01 },
+	{ 0xc4b6, 0x00 },
+	{ 0xc4b7, 0x01 },
+	{ 0xc4b8, 0x00 },
+	{ 0xc4b9, 0x01 },
+	{ 0xc4ba, 0x01 },
+	{ 0xc4bb, 0x00 },
+	{ 0xc4bc, 0x01 },
+	{ 0xc4bd, 0x60 },
+	{ 0xc4be, 0x02 },
+	{ 0xc4bf, 0x33 },
+	{ 0xc4c8, 0x03 },
+	{ 0xc4c9, 0xd0 },
+	{ 0xc4ca, 0x0e },
+	{ 0xc4cb, 0x00 },
+	{ 0xc4cc, 0x0e },
+	{ 0xc4cd, 0x51 },
+	{ 0xc4ce, 0x0e },
+	{ 0xc4cf, 0x51 },
+	{ 0xc4d0, 0x04 },
+	{ 0xc4d1, 0x80 },
+	{ 0xc4e0, 0x04 },
+	{ 0xc4e1, 0x02 },
+	{ 0xc4e2, 0x01 },
+	{ 0xc4e4, 0x10 },
+	{ 0xc4e5, 0x20 },
+	{ 0xc4e6, 0x30 },
+	{ 0xc4e7, 0x40 },
+	{ 0xc4e8, 0x50 },
+	{ 0xc4e9, 0x60 },
+	{ 0xc4ea, 0x70 },
+	{ 0xc4eb, 0x80 },
+	{ 0xc4ec, 0x90 },
+	{ 0xc4ed, 0xa0 },
+	{ 0xc4ee, 0xb0 },
+	{ 0xc4ef, 0xc0 },
+	{ 0xc4f0, 0xd0 },
+	{ 0xc4f1, 0xe0 },
+	{ 0xc4f2, 0xf0 },
+	{ 0xc4f3, 0x80 },
+	{ 0xc4f4, 0x00 },
+	{ 0xc4f5, 0x20 },
+	{ 0xc4f6, 0x02 },
+	{ 0xc4f7, 0x00 },
+	{ 0xc4f8, 0x00 },
+	{ 0xc4f9, 0x00 },
+	{ 0xc4fa, 0x00 },
+	{ 0xc4fb, 0x01 },
+	{ 0xc4fc, 0x01 },
+	{ 0xc4fd, 0x00 },
+	{ 0xc4fe, 0x04 },
+	{ 0xc4ff, 0x02 },
+	{ 0xc500, 0x48 },
+	{ 0xc501, 0x74 },
+	{ 0xc502, 0x58 },
+	{ 0xc503, 0x80 },
+	{ 0xc504, 0x05 },
+	{ 0xc505, 0x80 },
+	{ 0xc506, 0x03 },
+	{ 0xc507, 0x80 },
+	{ 0xc508, 0x01 },
+	{ 0xc509, 0xc0 },
+	{ 0xc50a, 0x01 },
+	{ 0xc50b, 0xa0 },
+	{ 0xc50c, 0x01 },
+	{ 0xc50d, 0x2c },
+	{ 0xc50e, 0x01 },
+	{ 0xc50f, 0x0a },
+	{ 0xc510, 0x00 },
+	{ 0xc511, 0x00 },
+	{ 0xc512, 0xe5 },
+	{ 0xc513, 0x14 },
+	{ 0xc514, 0x04 },
+	{ 0xc515, 0x00 },
+	{ 0xc518, OV10635_VTS >> 8},
+	{ 0xc519, OV10635_VTS & 0xff},
+	{ 0xc51a, OV10635_HTS >> 8},
+	{ 0xc51b, OV10635_HTS & 0xff},
+	{ 0xc2e0, 0x00 },
+	{ 0xc2e1, 0x51 },
+	{ 0xc2e2, 0x00 },
+	{ 0xc2e3, 0xd6 },
+	{ 0xc2e4, 0x01 },
+	{ 0xc2e5, 0x5e },
+	{ 0xc2e9, 0x01 },
+	{ 0xc2ea, 0x7a },
+	{ 0xc2eb, 0x90 },
+	{ 0xc2ed, 0x00 },
+	{ 0xc2ee, 0x7a },
+	{ 0xc2ef, 0x64 },
+	{ 0xc308, 0x00 },
+	{ 0xc309, 0x00 },
+	{ 0xc30a, 0x00 },
+	{ 0xc30c, 0x00 },
+	{ 0xc30d, 0x01 },
+	{ 0xc30e, 0x00 },
+	{ 0xc30f, 0x00 },
+	{ 0xc310, 0x01 },
+	{ 0xc311, 0x60 },
+	{ 0xc312, 0xff },
+	{ 0xc313, 0x08 },
+	{ 0xc314, 0x01 },
+	{ 0xc315, 0x00 },
+	{ 0xc316, 0xff },
+	{ 0xc317, 0x0b },
+	{ 0xc318, 0x00 },
+	{ 0xc319, 0x0c },
+	{ 0xc31a, 0x00 },
+	{ 0xc31b, 0xe0 },
+	{ 0xc31c, 0x00 },
+	{ 0xc31d, 0x14 },
+	{ 0xc31e, 0x00 },
+	{ 0xc31f, 0xc5 },
+	{ 0xc320, 0xff },
+	{ 0xc321, 0x4b },
+	{ 0xc322, 0xff },
+	{ 0xc323, 0xf0 },
+	{ 0xc324, 0xff },
+	{ 0xc325, 0xe8 },
+	{ 0xc326, 0x00 },
+	{ 0xc327, 0x46 },
+	{ 0xc328, 0xff },
+	{ 0xc329, 0xd2 },
+	{ 0xc32a, 0xff },
+	{ 0xc32b, 0xe4 },
+	{ 0xc32c, 0xff },
+	{ 0xc32d, 0xbb },
+	{ 0xc32e, 0x00 },
+	{ 0xc32f, 0x61 },
+	{ 0xc330, 0xff },
+	{ 0xc331, 0xf9 },
+	{ 0xc332, 0x00 },
+	{ 0xc333, 0xd9 },
+	{ 0xc334, 0x00 },
+	{ 0xc335, 0x2e },
+	{ 0xc336, 0x00 },
+	{ 0xc337, 0xb1 },
+	{ 0xc338, 0xff },
+	{ 0xc339, 0x64 },
+	{ 0xc33a, 0xff },
+	{ 0xc33b, 0xeb },
+	{ 0xc33c, 0xff },
+	{ 0xc33d, 0xe8 },
+	{ 0xc33e, 0x00 },
+	{ 0xc33f, 0x48 },
+	{ 0xc340, 0xff },
+	{ 0xc341, 0xd0 },
+	{ 0xc342, 0xff },
+	{ 0xc343, 0xed },
+	{ 0xc344, 0xff },
+	{ 0xc345, 0xad },
+	{ 0xc346, 0x00 },
+	{ 0xc347, 0x66 },
+	{ 0xc348, 0x01 },
+	{ 0xc349, 0x00 },
+	{ 0x6700, 0x04 },
+	{ 0x6701, 0x7b },
+	{ 0x6702, 0xfd },
+	{ 0x6703, 0xf9 },
+	{ 0x6704, 0x3d },
+	{ 0x6705, 0x71 },
+	{ 0x6706, 0x78 },
+	{ 0x6708, 0x05 },
+	{ 0x6f06, 0x6f },
+	{ 0x6f07, 0x00 },
+	{ 0x6f0a, 0x6f },
+	{ 0x6f0b, 0x00 },
+	{ 0x6f00, 0x03 },
+	{ 0xc34c, 0x01 },
+	{ 0xc34d, 0x00 },
+	{ 0xc34e, 0x46 },
+	{ 0xc34f, 0x55 },
+	{ 0xc350, 0x00 },
+	{ 0xc351, 0x40 },
+	{ 0xc352, 0x00 },
+	{ 0xc353, 0xff },
+	{ 0xc354, 0x04 },
+	{ 0xc355, 0x08 },
+	{ 0xc356, 0x01 },
+	{ 0xc357, 0xef },
+	{ 0xc358, 0x30 },
+	{ 0xc359, 0x01 },
+	{ 0xc35a, 0x64 },
+	{ 0xc35b, 0x46 },
+	{ 0xc35c, 0x00 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0x3042, 0xf0 },
+	{ 0xc261, 0x01 },
+	{ 0x301b, 0xf0 },
+	{ 0x301c, 0xf0 },
+	{ 0x301a, 0xf0 },
+	{ 0x6f00, 0xc3 },
+	{ 0xc46a, 0x30 },
+	{ 0xc46d, 0x20 },
+	{ 0xc464, 0x84 },
+	{ 0xc465, 0x00 },
+	{ 0x6f00, 0x03 },
+	{ 0x6f00, 0x43 },
+	{ 0x381c, 0x00 },
+	{ 0x381d, 0x40 },
+	{ 0xc454, 0x01 },
+	{ 0x6f00, 0xc3 },
+	{ 0xc454, 0x00 },
+	{ 0xc4b1, 0x02 },
+	{ 0xc4b2, 0x01 },
+	{ 0xc4b3, 0x03 },
+	{ 0x6f00, 0x03 },
+	{ 0x6f00, 0x43 },
+	/* enable FSIN (FRAMESYNC input) functionality */
+	{ 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 },
+	{ 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff },
+	{ 0x3834, OV10635_VTS >> 8 },
+	{ 0x3835, OV10635_VTS & 0xff },
+	{ 0x302e, 0x01 },
+};
+
+#endif /* __RDACM20_OV10635_H__ */
diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
new file mode 100644
index 000000000000..88986e8c0d25
--- /dev/null
+++ b/drivers/media/i2c/rdacm20.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IMI RDACM20 GMSL Camera Driver
+ *
+ * Copyright (C) 2017-2020 Jacopo Mondi
+ * Copyright (C) 2017-2020 Kieran Bingham
+ * Copyright (C) 2017-2019 Laurent Pinchart
+ * Copyright (C) 2017-2019 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ */
+
+/*
+ * The camera is made of an Omnivision OV10635 sensor connected to a Maxim
+ * MAX9271 GMSL serializer.
+ */
+
+#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>
+
+#include "rdacm20-ov10635.h"
+#include "max9271.h"
+
+#define RDACM20_SENSOR_HARD_RESET
+
+#define OV10635_I2C_ADDRESS		0x30
+
+#define OV10635_SOFTWARE_RESET		0x0103
+#define OV10635_PID			0x300a
+#define OV10635_VER			0x300b
+#define OV10635_SC_CMMN_SCCB_ID		0x300c
+#define OV10635_SC_CMMN_SCCB_ID_SELECT	BIT(0)
+#define OV10635_VERSION			0xa635
+
+#define OV10635_WIDTH			1280
+#define OV10635_HEIGHT			800
+#define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY8_2X8
+
+struct rdacm20_device {
+	struct device			*dev;
+	struct max9271_device		*serializer;
+	struct i2c_client		*sensor;
+	struct v4l2_subdev		sd;
+	struct media_pad		pad;
+	struct v4l2_ctrl_handler	ctrls;
+	u32				addrs[32];
+};
+
+static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct rdacm20_device, sd);
+}
+
+static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client)
+{
+	return sd_to_rdacm20(i2c_get_clientdata(client));
+}
+
+static int ov10635_read16(struct rdacm20_device *dev, u16 reg)
+{
+	u8 buf[2] = { reg >> 8, reg & 0xff };
+	int ret;
+
+	ret = i2c_master_send(dev->sensor, buf, 2);
+	if (ret == 2)
+		ret = i2c_master_recv(dev->sensor, buf, 2);
+
+	if (ret < 0) {
+		dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n",
+			__func__, reg, ret);
+		return ret;
+	}
+
+	return (buf[0] << 8) | buf[1];
+}
+
+static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
+{
+	u8 buf[3] = { reg >> 8, reg & 0xff, val };
+	int ret;
+
+	dev_dbg(dev->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
+
+	ret = i2c_master_send(dev->sensor, buf, 3);
+	return ret < 0 ? ret : 0;
+}
+
+static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
+{
+	int ret;
+
+	ret = __ov10635_write(dev, reg, val);
+	if (ret < 0)
+		dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n",
+			__func__, reg, ret);
+
+	return ret;
+}
+
+static int ov10635_set_regs(struct rdacm20_device *dev,
+			    const struct ov10635_reg *regs,
+			    unsigned int nr_regs)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < nr_regs; i++) {
+		ret = __ov10635_write(dev, regs[i].reg, regs[i].val);
+		if (ret) {
+			dev_err(dev->dev,
+				"%s: register %u (0x%04x) write failed (%d)\n",
+				__func__, i, regs[i].reg, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct rdacm20_device *dev = sd_to_rdacm20(sd);
+
+	/*
+	 * Enable serial link now that the ISP provides a valid pixel clock
+	 * to start serializing video data on the GMSL link.
+	 */
+	return max9271_set_serial_link(dev->serializer, enable);
+}
+
+static int rdacm20_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 = OV10635_FORMAT;
+
+	return 0;
+}
+
+static int rdacm20_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		= OV10635_WIDTH;
+	mf->height		= OV10635_HEIGHT;
+	mf->code		= OV10635_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 rdacm20_video_ops = {
+	.s_stream	= rdacm20_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
+	.enum_mbus_code = rdacm20_enum_mbus_code,
+	.get_fmt	= rdacm20_get_fmt,
+	.set_fmt	= rdacm20_get_fmt,
+};
+
+static struct v4l2_subdev_ops rdacm20_subdev_ops = {
+	.video		= &rdacm20_video_ops,
+	.pad		= &rdacm20_subdev_pad_ops,
+};
+
+static int rdacm20_initialize(struct rdacm20_device *dev)
+{
+	int ret;
+
+	/* Verify communication with the MAX9271: ping to wakeup. */
+	dev->serializer->client->addr = MAX9271_DEFAULT_ADDR;
+	i2c_smbus_read_byte(dev->serializer->client);
+
+	/* Serial link disabled during config as it needs a valid pixel clock. */
+	ret = max9271_set_serial_link(dev->serializer, false);
+	if (ret)
+		return ret;
+
+	/*
+	 *  Ensure that we have a good link configuration before attempting to
+	 *  identify the device.
+	 */
+	max9271_configure_i2c(dev->serializer,
+			      MAX9271_I2CSLVSH_469NS_234NS |
+			      MAX9271_I2CSLVTO_1024US |
+			      MAX9271_I2CMSTBT_105KBPS);
+	if (ret)
+		return ret;
+
+	max9271_configure_gmsl_link(dev->serializer);
+
+	ret = max9271_verify_id(dev->serializer);
+	if (ret < 0)
+		return ret;
+
+	ret = max9271_set_address(dev->serializer, dev->addrs[0]);
+	if (ret < 0)
+		return ret;
+	dev->serializer->client->addr = dev->addrs[0];
+
+	/*
+	 * Reset the sensor by cycling the OV10635 reset signal connected to the
+	 * MAX9271 GPIO1 and verify communication with the OV10635.
+	 */
+	max9271_clear_gpios(dev->serializer, MAX9271_GPIO1OUT);
+	max9271_set_gpios(dev->serializer, MAX9271_GPIO1OUT);
+
+	ret = ov10635_read16(dev, OV10635_PID);
+	if (ret < 1) {
+		dev_err(dev->dev, "OV10635 ID read failed (%d)\n",
+			ret);
+		return -ENXIO;
+	}
+
+	if (ret != OV10635_VERSION) {
+		dev_err(dev->dev, "OV10635 ID mismatch (0x%04x)\n",
+			ret);
+		return -ENXIO;
+	}
+
+	/* Change the sensor I2C address. */
+	ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
+			    (dev->addrs[1] << 1) |
+			    OV10635_SC_CMMN_SCCB_ID_SELECT);
+	if (ret < 0) {
+		dev_err(dev->dev,
+			"OV10635 I2C address change failed (%d)\n", ret);
+		return ret;
+	}
+	dev->sensor->addr = dev->addrs[1];
+	usleep_range(3500, 5000);
+
+	/* Program the 0V10635 initial configuration. */
+	ret = ov10635_set_regs(dev, ov10635_regs_wizard,
+			       ARRAY_SIZE(ov10635_regs_wizard));
+	if (ret)
+		return ret;
+
+	dev_info(dev->dev, "Identified MAX9271 + OV10635 device\n");
+
+	return 0;
+}
+
+static int rdacm20_probe(struct i2c_client *client)
+{
+	struct rdacm20_device *dev;
+	struct fwnode_handle *ep;
+	int ret;
+
+	dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	dev->dev = &client->dev;
+
+	dev->serializer = devm_kzalloc(&client->dev, sizeof(*dev->serializer),
+				       GFP_KERNEL);
+	if (!dev->serializer)
+		return -ENOMEM;
+
+	dev->serializer->client = client;
+
+	ret = of_property_read_u32_array(client->dev.of_node, "reg",
+					 dev->addrs, 2);
+	if (ret < 0) {
+		dev_err(dev->dev, "Invalid DT reg property: %d\n", ret);
+		return -EINVAL;
+	}
+
+	/* Create the dummy I2C client for the sensor. */
+	dev->sensor = i2c_new_dummy_device(client->adapter,
+					   OV10635_I2C_ADDRESS);
+	if (IS_ERR(dev->sensor)) {
+		ret = PTR_ERR(dev->sensor);
+		goto error;
+	}
+
+	/* Initialize the hardware. */
+	ret = rdacm20_initialize(dev);
+	if (ret < 0)
+		goto error;
+
+	/* Initialize and register the subdevice. */
+	v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops);
+	dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	v4l2_ctrl_handler_init(&dev->ctrls, 1);
+	/*
+	 * FIXME: Compute the real pixel rate. The 50 MP/s value comes from the
+	 * hardcoded frequency in the BSP CSI-2 receiver driver.
+	 */
+	v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, 50000000,
+			  50000000, 1, 50000000);
+	dev->sd.ctrl_handler = &dev->ctrls;
+
+	ret = dev->ctrls.error;
+	if (ret)
+		goto error_free_ctrls;
+
+	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;
+
+	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;
+
+	ret = v4l2_async_register_subdev(&dev->sd);
+	if (ret)
+		goto error_put_node;
+
+	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);
+	if (dev->sensor)
+		i2c_unregister_device(dev->sensor);
+
+	dev_err(&client->dev, "probe failed\n");
+
+	return ret;
+}
+
+static int rdacm20_remove(struct i2c_client *client)
+{
+	struct rdacm20_device *dev = i2c_to_rdacm20(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->sensor);
+
+	return 0;
+}
+
+static void rdacm20_shutdown(struct i2c_client *client)
+{
+	struct rdacm20_device *dev = i2c_to_rdacm20(client);
+
+	/* make sure stream off during shutdown (reset/reboot) */
+	rdacm20_s_stream(&dev->sd, 0);
+}
+
+static const struct of_device_id rdacm20_of_ids[] = {
+	{ .compatible = "imi,rdacm20", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rdacm20_of_ids);
+
+static struct i2c_driver rdacm20_i2c_driver = {
+	.driver	= {
+		.name	= "rdacm20",
+		.of_match_table = rdacm20_of_ids,
+	},
+	.probe_new	= rdacm20_probe,
+	.remove		= rdacm20_remove,
+	.shutdown	= rdacm20_shutdown,
+};
+
+module_i2c_driver(rdacm20_i2c_driver);
+
+MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v7 4/6] media: i2c: max9286: Adjust reverse channel amplitude
  2020-02-16 10:50 [PATCH v7 0/6] RDACM20 and RDACM21 GMSL camera support Jacopo Mondi
                   ` (2 preceding siblings ...)
  2020-02-16 10:51 ` [PATCH v7 3/6] media: i2c: Add driver for RDACM20 camera module Jacopo Mondi
@ 2020-02-16 10:51 ` Jacopo Mondi
  2020-02-16 10:51 ` [PATCH v7 5/6] media: i2c: max9286: Adjust image format size Jacopo Mondi
  2020-02-16 10:51 ` [PATCH v7 6/6] media: i2c: Add driver for RDACM21 camera module Jacopo Mondi
  5 siblings, 0 replies; 7+ messages in thread
From: Jacopo Mondi @ 2020-02-16 10:51 UTC (permalink / raw)
  To: kieran.bingham+renesas, laurent.pinchart, niklas.soderlund
  Cc: Jacopo Mondi, linux-renesas-soc

Start with reverse channel amplitude set to 100mV and later increase it
to 170mV to compensate the serializer high threshold.

This allows more reliable communications with RDACM21 camera which have
not been pre-programmed with an already compensated reverse channel
configuration.

Warning: this change breaks operation with pre-programmed RDACM20
camera modules

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>

---
v7:
 - new patch required to operate with RDACM21
---
 drivers/media/i2c/max9286.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index ed5dcae6b363..dd3bfb3c59b7 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -594,10 +594,13 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
 	 * All enabled sources have probed and enabled their reverse control
 	 * channels:
 	 *
-	 * - Verify all configuration links are properly detected
+	 * - Increase reverse channel amplitude to 170mV
 	 * - Disable auto-ack as communication on the control channel are now
 	 *   stable.
 	 */
+	max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) |
+		      MAX9286_REV_AMP_X);
+
 	max9286_check_config_link(priv, priv->source_mask);
 
 	/*
@@ -984,12 +987,11 @@ static int max9286_setup(struct max9286_priv *priv)
 	 *
 	 * - Enable custom reverse channel configuration (through register 0x3f)
 	 *   and set the first pulse length to 35 clock cycles.
-	 * - Increase the reverse channel amplitude to 170mV to accommodate the
-	 *   high threshold enabled by the serializer driver.
+	 * - Set reverse channel amplitude to 100mV and increase it later after
+	 *   the serializer high threshold have been increased.
 	 */
 	max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35));
-	max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) |
-		      MAX9286_REV_AMP_X);
+	max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(100));
 	usleep_range(2000, 2500);
 
 	/*
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v7 5/6] media: i2c: max9286: Adjust image format size
  2020-02-16 10:50 [PATCH v7 0/6] RDACM20 and RDACM21 GMSL camera support Jacopo Mondi
                   ` (3 preceding siblings ...)
  2020-02-16 10:51 ` [PATCH v7 4/6] media: i2c: max9286: Adjust reverse channel amplitude Jacopo Mondi
@ 2020-02-16 10:51 ` Jacopo Mondi
  2020-02-16 10:51 ` [PATCH v7 6/6] media: i2c: Add driver for RDACM21 camera module Jacopo Mondi
  5 siblings, 0 replies; 7+ messages in thread
From: Jacopo Mondi @ 2020-02-16 10:51 UTC (permalink / raw)
  To: kieran.bingham+renesas, laurent.pinchart, niklas.soderlund
  Cc: Jacopo Mondi, linux-renesas-soc

RDACM21 provides 1280x1080 images. Reflect this in the format reported
by the V4L2 APIs.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>

---
   v7: new patch
---
 drivers/media/i2c/max9286.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index dd3bfb3c59b7..730ce4fd033e 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -842,7 +842,7 @@ static const struct v4l2_subdev_ops max9286_subdev_ops = {
 static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
 {
 	fmt->width		= 1280;
-	fmt->height		= 800;
+	fmt->height		= 1080;
 	fmt->code		= MEDIA_BUS_FMT_UYVY8_2X8;
 	fmt->colorspace		= V4L2_COLORSPACE_SRGB;
 	fmt->field		= V4L2_FIELD_NONE;
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v7 6/6] media: i2c: Add driver for RDACM21 camera module
  2020-02-16 10:50 [PATCH v7 0/6] RDACM20 and RDACM21 GMSL camera support Jacopo Mondi
                   ` (4 preceding siblings ...)
  2020-02-16 10:51 ` [PATCH v7 5/6] media: i2c: max9286: Adjust image format size Jacopo Mondi
@ 2020-02-16 10:51 ` Jacopo Mondi
  5 siblings, 0 replies; 7+ messages in thread
From: Jacopo Mondi @ 2020-02-16 10:51 UTC (permalink / raw)
  To: kieran.bingham+renesas, laurent.pinchart, niklas.soderlund
  Cc: Jacopo Mondi, linux-renesas-soc

The RDACM20 is a GMSL camera supporting 1280x1080 resolution images
developed by IMI based on an Omnivision 10640 sensor, an Omnivision
OV490 ISP and a Maxim MAX9271 GMSL serializer.

The driver uses the max9271 library module, to maximize code reuse with
other camera module drivers using the same serializer, such as rdacm20.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>

---
v7:
 [Jacopo]
 - new patch
 [kieran]
 - Use new i2c_new_dummy_device API
---
 MAINTAINERS                 |  12 +
 drivers/media/i2c/Kconfig   |  11 +
 drivers/media/i2c/Makefile  |   2 +
 drivers/media/i2c/rdacm21.c | 516 ++++++++++++++++++++++++++++++++++++
 4 files changed, 541 insertions(+)
 create mode 100644 drivers/media/i2c/rdacm21.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 5633864d847b..20c6d85bdcbc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14060,6 +14060,18 @@ F:	drivers/media/i2c/rdacm20*
 F:	drivers/media/i2c/max9271.c
 F:	drivers/media/i2c/max9271.h
 
+RDACM21 Camera Sensor
+M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
+M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
+M:	Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
+M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/rdacm2x-gmsl.yaml
+F:	drivers/media/i2c/rdacm21.c
+F:	drivers/media/i2c/max9271.c
+F:	drivers/media/i2c/max9271.h
+
 RDC R-321X SoC
 M:	Florian Fainelli <florian@openwrt.org>
 S:	Maintained
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 8ec4a1915f8c..0c5b6e7aa8b6 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -1058,6 +1058,17 @@ config VIDEO_RDACM20
 	  This camera should be used in conjunction with a GMSL
 	  deserialiser such as the MAX9286.
 
+config VIDEO_RDACM21
+	tristate "IMI RDACM21 camera support"
+	depends on I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+	select V4L2_FWNODE
+	help
+	  This driver supports the IMI RDACM21 GMSL camera, used in
+	  ADAS systems.
+
+	  This camera should be used in conjunction with a GMSL
+	  deserialiser such as the MAX9286.
+
 comment "Flash devices"
 
 if MEDIA_CAMERA_SUPPORT
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 693b7d742827..f46d06318126 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -120,5 +120,7 @@ obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
 obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
 rdacm20-camera_module-objs	:= rdacm20.o max9271.o
 obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20-camera_module.o
+rdacm21-camera_module-objs	:= rdacm21.o max9271.o
+obj-$(CONFIG_VIDEO_RDACM21)	+= rdacm21-camera_module.o
 
 obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c
new file mode 100644
index 000000000000..46ea763fc38d
--- /dev/null
+++ b/drivers/media/i2c/rdacm21.c
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IMI RDACM21 GMSL Camera Driver
+ *
+ * Copyright (C) 2017-2020 Jacopo Mondi
+ * Copyright (C) 2017-2019 Kieran Bingham
+ * Copyright (C) 2017-2019 Laurent Pinchart
+ * Copyright (C) 2017-2019 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ */
+
+#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>
+#include "max9271.h"
+
+#define OV10640_I2C_ADDRESS		0x30
+#define OV10640_ID_LOW			0xa6
+
+#define OV490_I2C_ADDRESS		0x24
+
+#define OV490_ISP_HSIZE_LOW		0x60
+#define OV490_ISP_HSIZE_HIGH		0x61
+#define OV490_ISP_VSIZE_LOW		0x62
+#define OV490_ISP_VSIZE_HIGH		0x63
+
+#define OV490_PID			0x300a
+#define OV490_VER			0x300b
+#define OV490_ID_VAL			0x0490
+#define OV490_ID(_p, _v)		((((_p) & 0xff) << 8) | ((_v) & 0xff))
+
+struct rdacm21_device {
+	struct device			*dev;
+	struct max9271_device		*serializer;
+	struct i2c_client		*isp;
+	struct v4l2_subdev		sd;
+	struct media_pad		pad;
+	struct v4l2_mbus_framefmt	fmt;
+	u32				addrs[32];
+};
+
+static inline struct rdacm21_device *sd_to_rdacm21(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct rdacm21_device, sd);
+}
+
+static inline struct rdacm21_device *i2c_to_rdacm21(struct i2c_client *client)
+{
+	return sd_to_rdacm21(i2c_get_clientdata(client));
+}
+
+static const struct ov490_reg {
+	u16 reg;
+	u8 val;
+} ov490_regs_wizard[] = {
+	{0xfffd, 0x80},
+	{0xfffe, 0x82},
+	{0x0071, 0x11},
+	{0x0075, 0x11},
+	{0xfffe, 0x29},
+	{0x6010, 0x01},
+	/*
+	 * OV490 EMB line disable in YUV and RAW data,
+	 * NOTE: EMB line is still used in ISP and sensor
+	 */
+	{0xe000, 0x14},
+	{0xfffe, 0x28},
+	{0x6000, 0x04},
+	{0x6004, 0x00},
+	/*
+	 * PCLK polarity - useless due to silicon bug.
+	 * Use 0x808000bb register instead.
+	 */
+	{0x6008, 0x00},
+	{0xfffe, 0x80},
+	{0x0091, 0x00},
+	/* bit[3]=0 - PCLK polarity workaround. */
+	{0x00bb, 0x1d},
+	/* Ov490 FSIN: app_fsin_from_fsync */
+	{0xfffe, 0x85},
+	{0x0008, 0x00},
+	{0x0009, 0x01},
+	/* FSIN0 source. */
+	{0x000A, 0x05},
+	{0x000B, 0x00},
+	/* FSIN0 delay. */
+	{0x0030, 0x02},
+	{0x0031, 0x00},
+	{0x0032, 0x00},
+	{0x0033, 0x00},
+	/* FSIN1 delay. */
+	{0x0038, 0x02},
+	{0x0039, 0x00},
+	{0x003A, 0x00},
+	{0x003B, 0x00},
+	/* FSIN0 length. */
+	{0x0070, 0x2C},
+	{0x0071, 0x01},
+	{0x0072, 0x00},
+	{0x0073, 0x00},
+	/* FSIN1 length. */
+	{0x0074, 0x64},
+	{0x0075, 0x00},
+	{0x0076, 0x00},
+	{0x0077, 0x00},
+	{0x0000, 0x14},
+	{0x0001, 0x00},
+	{0x0002, 0x00},
+	{0x0003, 0x00},
+	/*
+	 * Load fsin0,load fsin1,load other,
+	 * It will be cleared automatically.
+	 */
+	{0x0004, 0x32},
+	{0x0005, 0x00},
+	{0x0006, 0x00},
+	{0x0007, 0x00},
+	{0xfffe, 0x80},
+	/* Sensor FSIN. */
+	{0x0081, 0x00},
+	/* ov10640 FSIN enable */
+	{0xfffe, 0x19},
+	{0x5000, 0x00},
+	{0x5001, 0x30},
+	{0x5002, 0x8c},
+	{0x5003, 0xb2},
+	{0xfffe, 0x80},
+	{0x00c0, 0xc1},
+	/* ov10640 HFLIP=1 by default */
+	{0xfffe, 0x19},
+	{0x5000, 0x01},
+	{0x5001, 0x00},
+	{0xfffe, 0x80},
+	{0x00c0, 0xdc},
+};
+
+static int ov490_read(struct rdacm21_device *dev, u16 reg, u8 *val)
+{
+	u8 buf[2] = { reg >> 8, reg & 0xff };
+	int ret;
+
+	ret = i2c_master_send(dev->isp, buf, 2);
+	if (ret == 2)
+		ret = i2c_master_recv(dev->isp, val, 1);
+
+	if (ret < 0) {
+		dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n",
+			__func__, reg, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ov490_write(struct rdacm21_device *dev, u16 reg, u8 val)
+{
+	u8 buf[3] = { reg >> 8, reg & 0xff, val };
+	int ret;
+
+	dev_dbg(dev->dev, "%s: 0x%04x = 0x%02x\n", __func__, reg, val);
+
+	ret = i2c_master_send(dev->isp, buf, 3);
+	if (ret < 0) {
+		dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n",
+			__func__, reg, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rdacm21_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct rdacm21_device *dev = sd_to_rdacm21(sd);
+
+	/*
+	 * Enable serial link now that the ISP provides a valid pixel clock
+	 * to start serializing video data on the GMSL link.
+	 */
+	return max9271_set_serial_link(dev->serializer, enable);
+}
+
+static int rdacm21_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 = MEDIA_BUS_FMT_YUYV8_2X8;
+
+	return 0;
+}
+
+static int rdacm21_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 rdacm21_device *dev = sd_to_rdacm21(sd);
+
+	if (format->pad)
+		return -EINVAL;
+
+	mf->width		= dev->fmt.width;
+	mf->height		= dev->fmt.height;
+	mf->code		= MEDIA_BUS_FMT_YUYV8_2X8;
+	mf->colorspace		= V4L2_COLORSPACE_SRGB;
+	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 rdacm21_video_ops = {
+	.s_stream	= rdacm21_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops rdacm21_subdev_pad_ops = {
+	.enum_mbus_code = rdacm21_enum_mbus_code,
+	.get_fmt	= rdacm21_get_fmt,
+	.set_fmt	= rdacm21_get_fmt,
+};
+
+static struct v4l2_subdev_ops rdacm21_subdev_ops = {
+	.video		= &rdacm21_video_ops,
+	.pad		= &rdacm21_subdev_pad_ops,
+};
+
+static int ov490_initialize(struct rdacm21_device *dev)
+{
+	unsigned int timeout;
+	u8 pid, ver, val;
+	unsigned int i;
+	int ret;
+
+	/* Read OV490 Id to test communications. */
+	ret = ov490_write(dev, 0xfffd, 0x80);
+	ret = ov490_write(dev, 0xfffe, 0x80);
+	usleep_range(100, 150);
+
+	ret = ov490_read(dev, OV490_PID, &pid);
+	if (ret < 0) {
+		dev_err(dev->dev, "OV490 PID read failed (%d)\n", ret);
+		return ret;
+	}
+
+	ret = ov490_read(dev, OV490_VER, &ver);
+	if (ret < 0) {
+		dev_err(dev->dev, "OV490 VERSION read failed (%d)\n", ret);
+		return ret;
+	}
+
+	if (OV490_ID(pid, ver) != OV490_ID_VAL) {
+		dev_err(dev->dev, "OV490 ID mismatch: (0x%04x)\n",
+			OV490_ID(pid, ver));
+		return -ENODEV;
+	}
+
+	/* Wait for firmware boot by reading streamon status. */
+	ov490_write(dev, 0xfffd, 0x80);
+	ov490_write(dev, 0xfffe, 0x29);
+	usleep_range(100, 150);
+	for (timeout = 300; timeout > 0; timeout--) {
+		ov490_read(dev, 0xd000, &val);
+		if (val == 0x0c)
+			break;
+		mdelay(1);
+	}
+	if (!timeout) {
+		dev_err(dev->dev, "Timeout firmware boot wait\n");
+		return -ENODEV;
+	}
+	dev_dbg(dev->dev, "%s:%d Firmware booted in %u msec\n",
+		__func__, __LINE__, 300 - timeout);
+
+	/* Read OV10640 Id to test communications. */
+	ov490_write(dev, 0xfffd, 0x80);
+	ov490_write(dev, 0xfffe, 0x19);
+	usleep_range(100, 150);
+
+	ov490_write(dev, 0x5000, 0x01);
+	ov490_write(dev, 0x5001, 0x30);
+	ov490_write(dev, 0x5002, 0x0a);
+
+	ov490_write(dev, 0xfffe, 0x80);
+	usleep_range(100, 150);
+	ov490_write(dev, 0xc0, 0xc1);
+	ov490_write(dev, 0xfffe, 0x19);
+	usleep_range(1000, 1500);
+	ov490_read(dev, 0x5000, &val);
+	if (val != OV10640_ID_LOW) {
+		dev_err(dev->dev, "OV10640 ID mismatch: (0x%02x)\n", val);
+		return -ENODEV;
+	}
+
+	dev_dbg(dev->dev, "%s:%d OV10640 ID = % \n", __func__, __LINE__, val);
+
+	for (i = 0; i < ARRAY_SIZE(ov490_regs_wizard); ++i) {
+		ret = ov490_write(dev, ov490_regs_wizard[i].reg,
+				  ov490_regs_wizard[i].val);
+		if (ret < 0) {
+			dev_err(dev->dev,
+				"%s: register %u (0x%04x) write failed (%d)\n",
+				__func__, i, ov490_regs_wizard[i].reg, ret);
+
+			return -EIO;
+		}
+
+		usleep_range(100, 150);
+	}
+
+	/*
+	 * The ISP is programmed with the content of a serial flash memory.
+	 * Read the firmware configuration to reflect it through the V4L2 APIs.
+	 */
+	ov490_write(dev, 0xfffd, 0x80);
+	ov490_write(dev, 0xfffe, 0x82);
+	usleep_range(100, 150);
+	ov490_read(dev, OV490_ISP_HSIZE_HIGH, &val);
+	dev->fmt.width = (val & 0xf) << 8;
+	ov490_read(dev, OV490_ISP_HSIZE_LOW, &val);
+	dev->fmt.width |= (val & 0xff);
+
+	ov490_read(dev, OV490_ISP_VSIZE_HIGH, &val);
+	dev->fmt.height = (val & 0xf) << 8;
+	ov490_read(dev, OV490_ISP_VSIZE_LOW, &val);
+	dev->fmt.height |= val & 0xff;
+
+	/* Set bus width to 12 bits [0:11] */
+	ov490_write(dev, 0xfffd, 0x80);
+	ov490_write(dev, 0xfffe, 0x28);
+	usleep_range(100, 150);
+	ov490_write(dev, 0x6009, 0x10);
+
+	dev_info(dev->dev, "Identified RDACM21 camera module\n");
+
+	return 0;
+}
+
+static int rdacm21_initialize(struct rdacm21_device *dev)
+{
+	int ret;
+
+	/* Verify communication with the MAX9271: ping to wakeup. */
+	dev->serializer->client->addr = MAX9271_DEFAULT_ADDR;
+	i2c_smbus_read_byte(dev->serializer->client);
+
+	/* Serial link disabled during config as it needs a valid pixel clock. */
+	ret = max9271_set_serial_link(dev->serializer, false);
+	if (ret)
+		return ret;
+
+	/* Set GPO high to hold OV490 in reset during max9271 configuration. */
+	ret = max9271_set_gpios(dev->serializer, MAX9271_GPO);
+	if (ret)
+		return ret;
+
+	/* Configure I2C bus at 105Kbps speed and configure GMSL link. */
+	ret = max9271_configure_i2c(dev->serializer,
+				    MAX9271_I2CSLVSH_469NS_234NS |
+				    MAX9271_I2CSLVTO_1024US |
+				    MAX9271_I2CMSTBT_105KBPS);
+	if (ret)
+		return ret;
+
+	ret = max9271_configure_gmsl_link(dev->serializer);
+	if (ret)
+		return ret;
+
+	ret = max9271_set_address(dev->serializer, dev->addrs[0]);
+	if (ret)
+		return ret;
+	dev->serializer->client->addr = dev->addrs[0];
+
+	/*
+	 * Release OV490 from reset and program address translation
+	 * before performing OV490 configuration.
+	 */
+	ret = max9271_clear_gpios(dev->serializer, MAX9271_GPO);
+	if (ret)
+		return ret;
+
+	ret = max9271_set_translation(dev->serializer, dev->addrs[1],
+				      OV490_I2C_ADDRESS);
+	if (ret)
+		return ret;
+	dev->isp->addr = dev->addrs[1];
+
+	ret = ov490_initialize(dev);
+	if (ret)
+		return ret;
+
+	/*
+	 * Set reverse channel high threshold to increase noise immunity.
+	 *
+	 * This should be compensated by increasing the reverse channel
+	 * amplitude on the remote deserializer side.
+	 */
+	ret = max9271_set_high_threshold(dev->serializer, true);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rdacm21_probe(struct i2c_client *client)
+{
+	struct rdacm21_device *dev;
+	struct fwnode_handle *ep;
+	int ret;
+
+	dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	dev->dev = &client->dev;
+
+	dev->serializer = devm_kzalloc(&client->dev, sizeof(*dev->serializer),
+				       GFP_KERNEL);
+	if (!dev->serializer)
+		return -ENOMEM;
+
+	dev->serializer->client = client;
+
+	ret = of_property_read_u32_array(client->dev.of_node, "reg",
+					 dev->addrs, 2);
+	if (ret < 0) {
+		dev_err(dev->dev, "Invalid DT reg property: %d\n", ret);
+		return -EINVAL;
+	}
+
+	/* Create the dummy I2C client for the sensor. */
+	dev->isp = i2c_new_dummy_device(client->adapter, OV490_I2C_ADDRESS);
+	if (IS_ERR(dev->isp))
+		return PTR_ERR(dev->isp);
+
+	ret = rdacm21_initialize(dev);
+	if (ret < 0)
+		goto error;
+
+	/* Initialize and register the subdevice. */
+	v4l2_i2c_subdev_init(&dev->sd, client, &rdacm21_subdev_ops);
+	dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	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;
+
+	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;
+	}
+	dev->sd.fwnode = ep;
+
+	ret = v4l2_async_register_subdev(&dev->sd);
+	if (ret)
+		goto error_put_node;
+
+	return 0;
+
+error_put_node:
+	fwnode_handle_put(dev->sd.fwnode);
+error:
+	i2c_unregister_device(dev->isp);
+
+	return ret;
+}
+
+static int rdacm21_remove(struct i2c_client *client)
+{
+	struct rdacm21_device *dev = i2c_to_rdacm21(client);
+
+	fwnode_handle_put(dev->sd.fwnode);
+	v4l2_async_unregister_subdev(&dev->sd);
+	i2c_unregister_device(dev->isp);
+
+	return 0;
+}
+
+static const struct of_device_id rdacm21_of_ids[] = {
+	{ .compatible = "imi,rdacm21" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rdacm21_of_ids);
+
+static struct i2c_driver rdacm21_i2c_driver = {
+	.driver	= {
+		.name	= "rdacm21",
+		.of_match_table = rdacm21_of_ids,
+	},
+	.probe_new	= rdacm21_probe,
+	.remove		= rdacm21_remove,
+};
+
+module_i2c_driver(rdacm21_i2c_driver);
+
+MODULE_DESCRIPTION("GMSL Camera driver for RDACM21");
+MODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov");
+MODULE_LICENSE("GPL v2");
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-02-16 10:48 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-16 10:50 [PATCH v7 0/6] RDACM20 and RDACM21 GMSL camera support Jacopo Mondi
2020-02-16 10:51 ` [PATCH v7 1/6] media: i2c: max9286: Disable overlap window Jacopo Mondi
2020-02-16 10:51 ` [PATCH v7 2/6] dt-bindings: media: i2c: Add bindings for IMI RDACM2x Jacopo Mondi
2020-02-16 10:51 ` [PATCH v7 3/6] media: i2c: Add driver for RDACM20 camera module Jacopo Mondi
2020-02-16 10:51 ` [PATCH v7 4/6] media: i2c: max9286: Adjust reverse channel amplitude Jacopo Mondi
2020-02-16 10:51 ` [PATCH v7 5/6] media: i2c: max9286: Adjust image format size Jacopo Mondi
2020-02-16 10:51 ` [PATCH v7 6/6] media: i2c: Add driver for RDACM21 camera module Jacopo Mondi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).