linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/4] MAX9286 GMSL Support
@ 2018-11-02 15:47 Kieran Bingham
  2018-11-02 15:47 ` [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286 Kieran Bingham
                   ` (3 more replies)
  0 siblings, 4 replies; 26+ messages in thread
From: Kieran Bingham @ 2018-11-02 15:47 UTC (permalink / raw)
  To: linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Kieran Bingham

This series provides a pair of drivers for GMSL cameras on the R-Car
ADAS platforms.

These drivers originate from Cogent Embedded, and have been refactored
to split the MAX9286 away from the RDACM20 drivers which were once very
tightly coupled.

This posting is the culmination of ~100 changesets spread across Jacopo,
Niklas, Laurent and myself - thus they contain all of our SoB tags.

This series makes use of Sakari's VC developments and is based upon the
most recent rebase of this series by Niklas at:
  git://git.ragnatech.se/linux/ v4l2/mux

Thanks to the VC developments, this driver is capable of streaming from
all 4 input ports simultaneously through the RCar-VIN capture system.

This driver along with the associated platform support for the Renesas
R-Car Salvator-X, and the Eagle-V3M can be found at:

  git://git.kernel.org/pub/scm/linux/kernel/git/kbingham/rcar.git gmsl/v4


Further anticipated work in this area includes supporting the RDACM21
camera, at which point the RDACM20 will be adapted to separate out the
MAX9271 and the OV10635 sensor components.


Jacopo Mondi (1):
  dt-bindings: media: i2c: Add bindings for IMI RDACM20

Kieran Bingham (2):
  media: i2c: Add MAX9286 driver
  media: i2c: Add RDACM20 driver

Laurent Pinchart (1):
  dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286

 .../bindings/media/i2c/imi,rdacm20.txt        |   65 +
 .../bindings/media/i2c/maxim,max9286.txt      |  182 +++
 .../devicetree/bindings/vendor-prefixes.txt   |    1 +
 MAINTAINERS                                   |   20 +
 drivers/media/i2c/Kconfig                     |   22 +
 drivers/media/i2c/Makefile                    |    2 +
 drivers/media/i2c/max9286.c                   | 1189 +++++++++++++++++
 drivers/media/i2c/rdacm20-ov10635.h           |  953 +++++++++++++
 drivers/media/i2c/rdacm20.c                   |  635 +++++++++
 9 files changed, 3069 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/imi,rdacm20.txt
 create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
 create mode 100644 drivers/media/i2c/max9286.c
 create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
 create mode 100644 drivers/media/i2c/rdacm20.c

-- 
2.17.1


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

* [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286
  2018-11-02 15:47 [PATCH v4 0/4] MAX9286 GMSL Support Kieran Bingham
@ 2018-11-02 15:47 ` Kieran Bingham
  2018-11-05 20:02   ` Rob Herring
  2018-11-13 22:42   ` Luca Ceresoli
  2018-11-02 15:47 ` [PATCH v4 2/4] dt-bindings: media: i2c: Add bindings for IMI RDACM20 Kieran Bingham
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 26+ messages in thread
From: Kieran Bingham @ 2018-11-02 15:47 UTC (permalink / raw)
  To: linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Laurent Pinchart, Jacopo Mondi

From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

The MAX9286 deserializes video data received on up to 4 Gigabit
Multimedia Serial Links (GMSL) and outputs them on a CSI-2 port using up
to 4 data lanes.

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

---
v3:
 - Update binding descriptions

v4:
 - Define the use of a CSI2 D-PHY
 - Rename pwdn-gpios to enable-gpios (with inverted polarity)
 - Remove clock-lanes mapping support
 - rewrap text blocks
 - Fix typos
---
 .../bindings/media/i2c/maxim,max9286.txt      | 182 ++++++++++++++++++
 1 file changed, 182 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
new file mode 100644
index 000000000000..672f6a4d417d
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
@@ -0,0 +1,182 @@
+Maxim Integrated Quad GMSL Deserializer
+---------------------------------------
+
+The MAX9286 deserializer receives video data on up to 4 Gigabit Multimedia
+Serial Links (GMSL) and outputs them on a CSI-2 D-PHY port using up to 4 data
+lanes.
+
+In addition to video data, the GMSL links carry a bidirectional control channel
+that encapsulates I2C messages. The MAX9286 forwards all I2C traffic not
+addressed to itself to the other side of the links, where a GMSL serializer
+will output it on a local I2C bus. In the other direction all I2C traffic
+received over GMSL by the MAX9286 is output on the local I2C bus.
+
+Required Properties:
+
+- compatible: Shall be "maxim,max9286"
+- reg: I2C device address
+
+Optional Properties:
+
+- poc-supply: Regulator providing Power over Coax to the cameras
+- enable-gpios: GPIO connected to the #PWDN pin with inverted polarity
+
+Required endpoint nodes:
+-----------------------
+
+The connections to the MAX9286 GMSL and its endpoint nodes are modeled using
+the OF graph bindings in accordance with the video interface bindings defined
+in Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+The following table lists the port number corresponding to each device port.
+
+        Port            Description
+        ----------------------------------------
+        Port 0          GMSL Input 0
+        Port 1          GMSL Input 1
+        Port 2          GMSL Input 2
+        Port 3          GMSL Input 3
+        Port 4          CSI-2 Output
+
+Optional Endpoint Properties for GMSL Input Ports (Port [0-3]):
+
+- remote-endpoint: phandle to the remote GMSL source endpoint subnode in the
+  remote node port.
+
+Required Endpoint Properties for CSI-2 Output Port (Port 4):
+
+- remote-endpoint: phandle to the remote CSI-2 sink endpoint node.
+- data-lanes: array of physical CSI-2 data lane indexes.
+
+Required i2c-mux nodes:
+----------------------
+
+Each GMSL link is modeled as a child bus of an i2c bus multiplexer/switch, in
+accordance with bindings described in
+Documentation/devicetree/bindings/i2c/i2c-mux.txt. The serializer device on the
+remote end of the GMSL link shall be modelled as a child node of the
+corresponding I2C bus.
+
+Required i2c child bus properties:
+- all properties described as required i2c child bus nodes properties in
+  Documentation/devicetree/bindings/i2c/i2c-mux.txt.
+
+Example:
+-------
+
+	gmsl-deserializer@2c {
+		compatible = "maxim,max9286";
+		reg = <0x2c>;
+		poc-supply = <&camera_poc_12v>;
+		enable-gpios = <&gpio 13 GPIO_ACTIVE_LOW>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				max9286_in0: endpoint {
+					remote-endpoint = <&rdacm20_out0>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+				max9286_in1: endpoint {
+					remote-endpoint = <&rdacm20_out1>;
+				};
+			};
+
+			port@2 {
+				reg = <2>;
+				max9286_in2: endpoint {
+					remote-endpoint = <&rdacm20_out2>;
+				};
+			};
+
+			port@3 {
+				reg = <3>;
+				max9286_in3: endpoint {
+					remote-endpoint = <&rdacm20_out3>;
+				};
+			};
+
+			port@4 {
+				reg = <4>;
+				max9286_out: endpoint {
+					data-lanes = <1 2 3 4>;
+					remote-endpoint = <&csi40_in>;
+				};
+			};
+		};
+
+		i2c@0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0>;
+
+			camera@51 {
+				compatible = "imi,rdacm20";
+				reg = <0x51 0x61>;
+
+				port {
+					rdacm20_out0: endpoint {
+						remote-endpoint = <&max9286_in0>;
+					};
+				};
+
+			};
+		};
+
+		i2c@1 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+
+			camera@52 {
+				compatible = "imi,rdacm20";
+				reg = <0x52 0x62>;
+				port {
+					rdacm20_out1: endpoint {
+						remote-endpoint = <&max9286_in1>;
+					};
+				};
+			};
+		};
+
+		i2c@2 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <2>;
+
+			camera@53 {
+				compatible = "imi,rdacm20";
+				reg = <0x53 0x63>;
+				port {
+					rdacm20_out2: endpoint {
+						remote-endpoint = <&max9286_in2>;
+					};
+				};
+			};
+		};
+
+		i2c@3 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <3>;
+
+			camera@54 {
+				compatible = "imi,rdacm20";
+				reg = <0x54 0x64>;
+				port {
+					rdacm20_out3: endpoint {
+						remote-endpoint = <&max9286_in3>;
+					};
+				};
+			};
+		};
+	};
-- 
2.17.1


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

* [PATCH v4 2/4] dt-bindings: media: i2c: Add bindings for IMI RDACM20
  2018-11-02 15:47 [PATCH v4 0/4] MAX9286 GMSL Support Kieran Bingham
  2018-11-02 15:47 ` [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286 Kieran Bingham
@ 2018-11-02 15:47 ` Kieran Bingham
  2018-11-05 20:13   ` Rob Herring
  2018-11-02 15:47 ` [PATCH v4 3/4] media: i2c: Add MAX9286 driver Kieran Bingham
  2018-11-02 15:47 ` [PATCH v4 4/4] media: i2c: Add RDACM20 driver Kieran Bingham
  3 siblings, 1 reply; 26+ messages in thread
From: Kieran Bingham @ 2018-11-02 15:47 UTC (permalink / raw)
  To: linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi

From: Jacopo Mondi <jacopo+renesas@jmondi.org>

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

Document its device tree binding interface.

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
---
 .../bindings/media/i2c/imi,rdacm20.txt        | 65 +++++++++++++++++++
 .../devicetree/bindings/vendor-prefixes.txt   |  1 +
 2 files changed, 66 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/imi,rdacm20.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/imi,rdacm20.txt b/Documentation/devicetree/bindings/media/i2c/imi,rdacm20.txt
new file mode 100644
index 000000000000..23915da4c3bf
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/imi,rdacm20.txt
@@ -0,0 +1,65 @@
+IMI D&D RDACM20 Automotive Camera Platform
+------------------------------------------
+
+The IMI D&D RDACM20 is a GMSL-compatible camera designed for automotive
+applications. It encloses a Maxim Integrated MAX9271 GMSL serializer, an
+Omnivision OV10635 camera sensor and an embedded MCU, and connects 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     |
+ ---------------                               --------------------------------
+
+The RDACM20 transmits video data generated by the embedded camera sensor on the
+GMSL serial channel to a remote GMSL de-serializer, as well as it receives and
+transmits I2C messages encapsulated in 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 embedded camera sensor and MCU. All
+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 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.
+
+Required Properties:
+
+- compatible: Shall be "imi,rdacm20".
+- reg: Pair of I2C device addresses, the first to be assigned to the serializer
+  the second to be assigned to the camera sensor.
+
+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.
+
+Required endpoint properties:
+
+- remote-endpoint: phandle to the remote GMSL endpoint sub-node in the remote
+  node port.
+
+Example:
+-------
+
+	i2c@0 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0>;
+
+		camera@51 {
+			compatible = "imi,rdacm20";
+			reg = <0x51 0x61>;
+
+			port {
+				rdacm20_out0: endpoint {
+					remote-endpoint = <&max9286_in0>;
+				};
+			};
+
+		};
+	};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 2c3fc512e746..34b0ed876850 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -171,6 +171,7 @@ idt	Integrated Device Technologies, Inc.
 ifi	Ingenieurburo Fur Ic-Technologie (I/F/I)
 ilitek	ILI Technology Corporation (ILITEK)
 img	Imagination Technologies Ltd.
+imi	Integrated Micro-Electronics Inc.
 infineon Infineon Technologies
 inforce	Inforce Computing
 ingenic	Ingenic Semiconductor
-- 
2.17.1


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

* [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-02 15:47 [PATCH v4 0/4] MAX9286 GMSL Support Kieran Bingham
  2018-11-02 15:47 ` [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286 Kieran Bingham
  2018-11-02 15:47 ` [PATCH v4 2/4] dt-bindings: media: i2c: Add bindings for IMI RDACM20 Kieran Bingham
@ 2018-11-02 15:47 ` Kieran Bingham
  2018-11-07 15:06   ` Kieran Bingham
  2018-11-13 22:49   ` Luca Ceresoli
  2018-11-02 15:47 ` [PATCH v4 4/4] media: i2c: Add RDACM20 driver Kieran Bingham
  3 siblings, 2 replies; 26+ messages in thread
From: Kieran Bingham @ 2018-11-02 15:47 UTC (permalink / raw)
  To: linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and
CSI-2 output. The device supports multicamera streaming applications,
and features the ability to synchronise the attached cameras.

CSI-2 output can be configured with 1 to 4 lanes, and a control channel
is supported over I2C, which implements an I2C mux to facilitate
communications with connected cameras across the reverse control
channel.

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

--
v2:
 - Fix MAINTAINERS entry

This posting is released with the following modifications to work
without Sakari's VC developments:
 - max9286_g_mbus_config() re-instated
 - max9286_get_frame_desc() is not bus/csi aware
 - max9286_{get,set}_routing() removed

v3:
 - Initialise notifier with v4l2_async_notifier_init
 - Update for new mbus csi2 format V4L2_MBUS_CSI2_DPHY

v4: - Re-introduce required code to function with the VC series.

 - Implement max9286_get_routing, max9286_set_routing
 - Remove max9286_g_mbus_config
---
 MAINTAINERS                 |   10 +
 drivers/media/i2c/Kconfig   |   11 +
 drivers/media/i2c/Makefile  |    1 +
 drivers/media/i2c/max9286.c | 1189 +++++++++++++++++++++++++++++++++++
 4 files changed, 1211 insertions(+)
 create mode 100644 drivers/media/i2c/max9286.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 23021e0df5d7..745f0fd1fff1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8864,6 +8864,16 @@ F:	Documentation/devicetree/bindings/hwmon/max6697.txt
 F:	drivers/hwmon/max6697.c
 F:	include/linux/platform_data/max6697.h
 
+MAX9286 QUAD GMSL DESERIALIZER DRIVER
+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/max9286.txt
+F:	drivers/media/i2c/max9286.c
+
 MAX9860 MONO AUDIO VOICE CODEC DRIVER
 M:	Peter Rosin <peda@axentia.se>
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 704af210e270..eadc00bdd3bf 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -472,6 +472,17 @@ config VIDEO_VPX3220
 	  To compile this driver as a module, choose M here: the
 	  module will be called vpx3220.
 
+config VIDEO_MAX9286
+	tristate "Maxim MAX9286 GMSL deserializer support"
+	depends on I2C && I2C_MUX
+	depends on VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+	select V4L2_FWNODE
+	help
+	  This driver supports the Maxim MAX9286 GMSL deserializer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called max9286.
+
 comment "Video and audio decoders"
 
 config VIDEO_SAA717X
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 260d4d9ec2a1..4de7fe62b179 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -110,5 +110,6 @@ obj-$(CONFIG_VIDEO_IMX258)	+= imx258.o
 obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
 obj-$(CONFIG_VIDEO_IMX319)	+= imx319.o
 obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
+obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
 
 obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
new file mode 100644
index 000000000000..c39c2f86e07d
--- /dev/null
+++ b/drivers/media/i2c/max9286.c
@@ -0,0 +1,1189 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Maxim MAX9286 GMSL Deserializer Driver
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fwnode.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* Register 0x00 */
+#define MAX9286_MSTLINKSEL_AUTO		(7 << 5)
+#define MAX9286_MSTLINKSEL(n)		((n) << 5)
+#define MAX9286_EN_VS_GEN		BIT(4)
+#define MAX9286_LINKEN(n)		(1 << (n))
+/* Register 0x01 */
+#define MAX9286_FSYNCMODE_ECU		(3 << 6)
+#define MAX9286_FSYNCMODE_EXT		(2 << 6)
+#define MAX9286_FSYNCMODE_INT_OUT	(1 << 6)
+#define MAX9286_FSYNCMODE_INT_HIZ	(0 << 6)
+#define MAX9286_GPIEN			BIT(5)
+#define MAX9286_ENLMO_RSTFSYNC		BIT(2)
+#define MAX9286_FSYNCMETH_AUTO		(2 << 0)
+#define MAX9286_FSYNCMETH_SEMI_AUTO	(1 << 0)
+#define MAX9286_FSYNCMETH_MANUAL	(0 << 0)
+#define MAX9286_REG_FSYNC_PERIOD_L	0x06
+#define MAX9286_REG_FSYNC_PERIOD_M	0x07
+#define MAX9286_REG_FSYNC_PERIOD_H	0x08
+/* Register 0x0a */
+#define MAX9286_FWDCCEN(n)		(1 << ((n) + 4))
+#define MAX9286_REVCCEN(n)		(1 << (n))
+/* Register 0x0c */
+#define MAX9286_HVEN			BIT(7)
+#define MAX9286_EDC_6BIT_HAMMING	(2 << 5)
+#define MAX9286_EDC_6BIT_CRC		(1 << 5)
+#define MAX9286_EDC_1BIT_PARITY		(0 << 5)
+#define MAX9286_DESEL			BIT(4)
+#define MAX9286_INVVS			BIT(3)
+#define MAX9286_INVHS			BIT(2)
+#define MAX9286_HVSRC_D0		(2 << 0)
+#define MAX9286_HVSRC_D14		(1 << 0)
+#define MAX9286_HVSRC_D18		(0 << 0)
+/* Register 0x12 */
+#define MAX9286_CSILANECNT(n)		(((n) - 1) << 6)
+#define MAX9286_CSIDBL			BIT(5)
+#define MAX9286_DBL			BIT(4)
+#define MAX9286_DATATYPE_USER_8BIT	(11 << 0)
+#define MAX9286_DATATYPE_USER_YUV_12BIT	(10 << 0)
+#define MAX9286_DATATYPE_USER_24BIT	(9 << 0)
+#define MAX9286_DATATYPE_RAW14		(8 << 0)
+#define MAX9286_DATATYPE_RAW11		(7 << 0)
+#define MAX9286_DATATYPE_RAW10		(6 << 0)
+#define MAX9286_DATATYPE_RAW8		(5 << 0)
+#define MAX9286_DATATYPE_YUV422_10BIT	(4 << 0)
+#define MAX9286_DATATYPE_YUV422_8BIT	(3 << 0)
+#define MAX9286_DATATYPE_RGB555		(2 << 0)
+#define MAX9286_DATATYPE_RGB565		(1 << 0)
+#define MAX9286_DATATYPE_RGB888		(0 << 0)
+/* Register 0x15 */
+#define MAX9286_VC(n)			((n) << 5)
+#define MAX9286_VCTYPE			BIT(4)
+#define MAX9286_CSIOUTEN		BIT(3)
+#define MAX9286_0X15_RESV		(3 << 0)
+/* Register 0x1b */
+#define MAX9286_SWITCHIN(n)		(1 << ((n) + 4))
+#define MAX9286_ENEQ(n)			(1 << (n))
+/* Register 0x27 */
+#define MAX9286_LOCKED			BIT(7)
+/* Register 0x31 */
+#define MAX9286_FSYNC_LOCKED		BIT(6)
+/* Register 0x34 */
+#define MAX9286_I2CLOCACK		BIT(7)
+#define MAX9286_I2CSLVSH_1046NS_469NS	(3 << 5)
+#define MAX9286_I2CSLVSH_938NS_352NS	(2 << 5)
+#define MAX9286_I2CSLVSH_469NS_234NS	(1 << 5)
+#define MAX9286_I2CSLVSH_352NS_117NS	(0 << 5)
+#define MAX9286_I2CMSTBT_837KBPS	(7 << 2)
+#define MAX9286_I2CMSTBT_533KBPS	(6 << 2)
+#define MAX9286_I2CMSTBT_339KBPS	(5 << 2)
+#define MAX9286_I2CMSTBT_173KBPS	(4 << 2)
+#define MAX9286_I2CMSTBT_105KBPS	(3 << 2)
+#define MAX9286_I2CMSTBT_84KBPS		(2 << 2)
+#define MAX9286_I2CMSTBT_28KBPS		(1 << 2)
+#define MAX9286_I2CMSTBT_8KBPS		(0 << 2)
+#define MAX9286_I2CSLVTO_NONE		(3 << 0)
+#define MAX9286_I2CSLVTO_1024US		(2 << 0)
+#define MAX9286_I2CSLVTO_256US		(1 << 0)
+#define MAX9286_I2CSLVTO_64US		(0 << 0)
+/* Register 0x3b */
+#define MAX9286_REV_TRF(n)		((n) << 4)
+#define MAX9286_REV_AMP(n)		((((n) - 30) / 10) << 1) /* in mV */
+#define MAX9286_REV_AMP_X		BIT(0)
+/* Register 0x3f */
+#define MAX9286_EN_REV_CFG		BIT(6)
+#define MAX9286_REV_FLEN(n)		((n) - 20)
+/* Register 0x49 */
+#define MAX9286_VIDEO_DETECT_MASK	0x0f
+/* Register 0x69 */
+#define MAX9286_LFLTBMONMASKED		BIT(7)
+#define MAX9286_LOCKMONMASKED		BIT(6)
+#define MAX9286_AUTOCOMBACKEN		BIT(5)
+#define MAX9286_AUTOMASKEN		BIT(4)
+#define MAX9286_MASKLINK(n)		((n) << 0)
+
+#define MAX9286_NUM_GMSL		4
+#define MAX9286_N_SINKS			4
+#define MAX9286_N_PADS			5
+#define MAX9286_SRC_PAD			4
+
+#define MAXIM_I2C_I2C_SPEED_400KHZ	MAX9286_I2CMSTBT_339KBPS
+#define MAXIM_I2C_I2C_SPEED_100KHZ	MAX9286_I2CMSTBT_105KBPS
+#define MAXIM_I2C_SPEED			MAXIM_I2C_I2C_SPEED_100KHZ
+
+struct max9286_source {
+	struct v4l2_async_subdev asd;
+	struct v4l2_subdev *sd;
+	struct fwnode_handle *fwnode;
+};
+
+#define asd_to_max9286_source(_asd) \
+	container_of(_asd, struct max9286_source, asd)
+
+struct max9286_device {
+	struct i2c_client *client;
+	struct v4l2_subdev sd;
+	struct media_pad pads[MAX9286_N_PADS];
+	struct regulator *regulator;
+	bool poc_enabled;
+	int streaming;
+
+	struct i2c_mux_core *mux;
+	unsigned int mux_channel;
+
+	struct v4l2_ctrl_handler ctrls;
+
+	struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
+
+	unsigned int nsources;
+	unsigned int source_mask;
+	unsigned int route_mask;
+	unsigned int csi2_data_lanes;
+	struct max9286_source sources[MAX9286_NUM_GMSL];
+	struct v4l2_async_notifier notifier;
+};
+
+static struct max9286_source *next_source(struct max9286_device *max9286,
+					  struct max9286_source *source)
+{
+	if (!source)
+		source = &max9286->sources[0];
+	else
+		source++;
+
+	for (; source < &max9286->sources[MAX9286_NUM_GMSL]; source++) {
+		if (source->fwnode)
+			return source;
+	}
+
+	return NULL;
+}
+
+#define for_each_source(max9286, source) \
+	for (source = NULL; (source = next_source(max9286, source)); )
+
+#define to_index(max9286, source) (source - &max9286->sources[0])
+
+#define sd_to_max9286(_sd) container_of(_sd, struct max9286_device, sd)
+
+/* -----------------------------------------------------------------------------
+ * I2C IO
+ */
+
+static int max9286_read(struct max9286_device *dev, u8 reg)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(dev->client, reg);
+	if (ret < 0)
+		dev_err(&dev->client->dev,
+			"%s: register 0x%02x read failed (%d)\n",
+			__func__, reg, ret);
+
+	return ret;
+}
+
+static int max9286_write(struct max9286_device *dev, u8 reg, u8 val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
+	if (ret < 0)
+		dev_err(&dev->client->dev,
+			"%s: register 0x%02x write failed (%d)\n",
+			__func__, reg, ret);
+
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * I2C Multiplexer
+ */
+
+static int max9286_i2c_mux_close(struct max9286_device *dev)
+{
+
+	/* FIXME: See note in max9286_i2c_mux_select() */
+	if (dev->streaming)
+		return 0;
+	/*
+	 * Ensure that both the forward and reverse channel are disabled on the
+	 * mux, and that the channel ID is invalidated to ensure we reconfigure
+	 * on the next select call.
+	 */
+	dev->mux_channel = -1;
+	max9286_write(dev, 0x0a, 0x00);
+	usleep_range(3000, 5000);
+
+	return 0;
+}
+
+static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
+{
+	struct max9286_device *dev = i2c_mux_priv(muxc);
+
+	/*
+	 * FIXME: This state keeping is a hack and do the job. It should
+	 * be should be reworked. One option to consider is that once all
+	 * cameras are programmed the mux selection logic should be disabled
+	 * and all all reverse and forward channels enable all the time.
+	 *
+	 * In any case this logic with a int that have two states should be
+	 * reworked!
+	 */
+	if (dev->streaming == 1) {
+		max9286_write(dev, 0x0a, 0xff);
+		dev->streaming = 2;
+		return 0;
+	} else if (dev->streaming == 2) {
+		return 0;
+	}
+
+	if (dev->mux_channel == chan)
+		return 0;
+
+	dev->mux_channel = chan;
+
+	max9286_write(dev, 0x0a, MAX9286_FWDCCEN(chan) | MAX9286_REVCCEN(chan));
+
+	/*
+	 * We must sleep after any change to the forward or reverse channel
+	 * configuration.
+	 */
+	usleep_range(3000, 5000);
+
+	return 0;
+}
+
+static int max9286_i2c_mux_init(struct max9286_device *dev)
+{
+	struct max9286_source *source;
+	int ret;
+
+	if (!i2c_check_functionality(dev->client->adapter,
+				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+		return -ENODEV;
+
+	dev->mux = i2c_mux_alloc(dev->client->adapter, &dev->client->dev,
+				 dev->nsources, 0, I2C_MUX_LOCKED,
+				 max9286_i2c_mux_select, NULL);
+	if (!dev->mux)
+		return -ENOMEM;
+
+	dev->mux->priv = dev;
+
+	for_each_source(dev, source) {
+		unsigned int index = to_index(dev, source);
+
+		ret = i2c_mux_add_adapter(dev->mux, 0, index, 0);
+		if (ret < 0)
+			goto error;
+	}
+
+	return 0;
+
+error:
+	i2c_mux_del_adapters(dev->mux);
+	return ret;
+}
+
+static void max9286_configure_i2c(struct max9286_device *dev, bool localack)
+{
+	u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
+		    MAXIM_I2C_SPEED;
+
+	if (localack)
+		config |= MAX9286_I2CLOCACK;
+
+	max9286_write(dev, 0x34, config);
+	usleep_range(3000, 5000);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdev
+ */
+
+static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
+				struct v4l2_subdev *subdev,
+				struct v4l2_async_subdev *asd)
+{
+	struct max9286_device *dev = sd_to_max9286(notifier->sd);
+	struct max9286_source *source = asd_to_max9286_source(asd);
+	unsigned int index = to_index(dev, source);
+	unsigned int src_pad;
+	int ret;
+
+	ret = media_entity_get_fwnode_pad(&subdev->entity,
+					  source->fwnode,
+					  MEDIA_PAD_FL_SOURCE);
+	if (ret < 0) {
+		dev_err(&dev->client->dev,
+			"Failed to find pad for %s\n", subdev->name);
+		return ret;
+	}
+
+	source->sd = subdev;
+	src_pad = ret;
+
+	ret = media_create_pad_link(&source->sd->entity, src_pad,
+				    &dev->sd.entity, index,
+				    MEDIA_LNK_FL_ENABLED |
+				    MEDIA_LNK_FL_IMMUTABLE);
+	if (ret) {
+		dev_err(&dev->client->dev,
+			"Unable to link %s:%u -> %s:%u\n",
+			source->sd->name, src_pad, dev->sd.name, index);
+		return ret;
+	}
+
+	dev_dbg(&dev->client->dev, "Bound %s pad: %u on index %u\n",
+		subdev->name, src_pad, index);
+
+	return 0;
+}
+
+static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
+				  struct v4l2_subdev *subdev,
+				  struct v4l2_async_subdev *asd)
+{
+	struct max9286_source *source = asd_to_max9286_source(asd);
+
+	source->sd = NULL;
+}
+
+static const struct v4l2_async_notifier_operations max9286_notify_ops = {
+	.bound = max9286_notify_bound,
+	.unbind = max9286_notify_unbind,
+};
+
+/*
+ * max9286_check_video_links() - Make sure video links are detected and locked
+ *
+ * Performs safety checks on video link status. Make sure they are detected
+ * and all enabled links are locked.
+ *
+ * Returns 0 for success, -EIO for errors.
+ */
+static int max9286_check_video_links(struct max9286_device *dev)
+{
+	unsigned int i;
+	int ret;
+
+	/*
+	 * Make sure valid video links are detected.
+	 * The delay is not characterized in de-serializer manual, wait up
+	 * to 5 ms.
+	 */
+	for (i = 0; i < 10; i++) {
+		ret = max9286_read(dev, 0x49);
+		if (ret < 0)
+			return -EIO;
+
+		if ((ret & MAX9286_VIDEO_DETECT_MASK) == dev->source_mask)
+			break;
+
+		usleep_range(350, 500);
+	}
+
+	if (i == 10) {
+		dev_err(&dev->client->dev,
+			"Unable to detect video links: 0x%2x\n", ret);
+		return -EIO;
+	}
+
+	/* Make sure all enabled links are locked (4ms max). */
+	for (i = 0; i < 10; i++) {
+		ret = max9286_read(dev, 0x27);
+		if (ret < 0)
+			return -EIO;
+
+		if (ret & MAX9286_LOCKED)
+			break;
+
+		usleep_range(350, 450);
+	}
+
+	if (i == 10) {
+		dev_err(&dev->client->dev, "Not all enabled links locked\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct max9286_device *dev = sd_to_max9286(sd);
+	struct max9286_source *source;
+	unsigned int i;
+	bool sync = false;
+	int ret;
+
+	if (enable) {
+		/* FIXME: See note in max9286_i2c_mux_select() */
+		dev->streaming = 1;
+
+		/* Start all cameras. */
+		for_each_source(dev, source) {
+			ret = v4l2_subdev_call(source->sd, video, s_stream, 1);
+			if (ret)
+				return ret;
+		}
+
+		ret = max9286_check_video_links(dev);
+		if (ret)
+			return ret;
+
+		/*
+		 * Wait until frame synchronization is locked.
+		 *
+		 * Manual says frame sync locking should take ~6 VTS.
+		 * From pratical experience at least 8 are required. Give
+		 * 12 complete frames time (~33ms at 30 fps) to achieve frame
+		 * locking before returning error.
+		 */
+		for (i = 0; i < 36; i++) {
+			if (max9286_read(dev, 0x31) & MAX9286_FSYNC_LOCKED) {
+				sync = true;
+				break;
+			}
+			usleep_range(9000, 11000);
+		}
+
+		if (!sync) {
+			dev_err(&dev->client->dev,
+				"Failed to get frame synchronization\n");
+			return -EINVAL;
+		}
+
+		/*
+		 * Enable CSI output, VC set according to link number.
+		 * Bit 7 must be set (chip manual says it's 0 and reserved).
+		 */
+		max9286_write(dev, 0x15, 0x80 | MAX9286_VCTYPE |
+			      MAX9286_CSIOUTEN | MAX9286_0X15_RESV);
+	} else {
+		max9286_write(dev, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
+
+		/* Stop all cameras. */
+		for_each_source(dev, source)
+			v4l2_subdev_call(source->sd, video, s_stream, 0);
+
+		/* FIXME: See note in max9286_i2c_mux_select() */
+		dev->streaming = 0;
+	}
+
+	return 0;
+}
+
+static int max9286_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_UYVY8_2X8;
+
+	return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+max9286_get_pad_format(struct max9286_device *dev,
+		       struct v4l2_subdev_pad_config *cfg,
+		       unsigned int pad, u32 which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(&dev->sd, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &dev->fmt[pad];
+	default:
+		return NULL;
+	}
+}
+
+static int max9286_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *format)
+{
+	struct max9286_device *dev = sd_to_max9286(sd);
+	struct v4l2_mbus_framefmt *cfg_fmt;
+
+	if (format->pad >= MAX9286_SRC_PAD)
+		return -EINVAL;
+
+	/* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */
+	switch (format->format.code) {
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+	case MEDIA_BUS_FMT_VYUY8_2X8:
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+		break;
+	default:
+		format->format.code = MEDIA_BUS_FMT_YUYV8_2X8;
+		break;
+	}
+
+	cfg_fmt = max9286_get_pad_format(dev, cfg, format->pad, format->which);
+	if (!cfg_fmt)
+		return -EINVAL;
+
+	*cfg_fmt = format->format;
+
+	return 0;
+}
+
+static int max9286_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *format)
+{
+	struct max9286_device *dev = sd_to_max9286(sd);
+	struct v4l2_mbus_framefmt *cfg_fmt;
+
+	if (format->pad >= MAX9286_SRC_PAD)
+		return -EINVAL;
+
+	cfg_fmt = max9286_get_pad_format(dev, cfg, format->pad, format->which);
+	if (!cfg_fmt)
+		return -EINVAL;
+
+	format->format = *cfg_fmt;
+
+	return 0;
+}
+
+static int max9286_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
+			   struct v4l2_mbus_frame_desc *fd)
+{
+	struct max9286_device *dev = sd_to_max9286(sd);
+	struct max9286_source *source;
+	unsigned int i = 0;
+
+	memset(fd, 0, sizeof(*fd));
+
+	for_each_source(dev, source) {
+		unsigned int index = to_index(dev, source);
+
+		fd->entry[i].stream = i;
+		fd->entry[i].bus.csi2.channel = index;
+		fd->entry[i].bus.csi2.data_type = 0x1e;
+		i++;
+	}
+
+	fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+	fd->num_entries = dev->nsources;
+
+	return 0;
+}
+
+static int max9286_get_routing(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_routing *routing)
+{
+	struct max9286_device *dev = sd_to_max9286(sd);
+	struct v4l2_subdev_route *r = routing->routes;
+	struct max9286_source *source;
+
+	/* There is one route per sink pad. */
+	if (routing->num_routes < dev->nsources) {
+		routing->num_routes = dev->nsources;
+		return -ENOSPC;
+	}
+
+	routing->num_routes = dev->nsources;
+
+	for_each_source(dev, source) {
+		unsigned int index = to_index(dev, source);
+
+		r->sink_pad = index;
+		r->sink_stream = 0;
+		r->source_pad = MAX9286_SRC_PAD;
+		r->source_stream = index;
+		r->flags = dev->route_mask & BIT(index) ?
+			V4L2_SUBDEV_ROUTE_FL_ACTIVE : 0;
+		r++;
+	}
+
+	return 0;
+}
+
+static int max9286_set_routing(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_routing *routing)
+{
+
+	struct max9286_device *dev = sd_to_max9286(sd);
+	struct v4l2_subdev_route *r = routing->routes;
+	unsigned int i;
+
+	if (routing->num_routes > dev->nsources)
+		return -ENOSPC;
+
+	for (i = 0; i < routing->num_routes; i++) {
+		if (r->sink_pad >= MAX9286_SRC_PAD ||
+		    r->sink_stream != 0 ||
+		    r->source_pad != MAX9286_SRC_PAD ||
+		    r->source_stream != r->sink_pad)
+			return -EINVAL;
+
+		if (r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)
+			dev->route_mask |= BIT(r->sink_pad);
+		else
+			dev->route_mask &= ~BIT(r->sink_pad);
+
+		r++;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops max9286_video_ops = {
+	.s_stream	= max9286_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
+	.enum_mbus_code = max9286_enum_mbus_code,
+	.get_fmt	= max9286_get_fmt,
+	.set_fmt	= max9286_set_fmt,
+	.get_frame_desc = max9286_get_frame_desc,
+	.get_routing	= max9286_get_routing,
+	.set_routing	= max9286_set_routing,
+};
+
+static const struct v4l2_subdev_ops max9286_subdev_ops = {
+	.video		= &max9286_video_ops,
+	.pad		= &max9286_pad_ops,
+};
+
+static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
+{
+	fmt->width		= 1280;
+	fmt->height		= 800;
+	fmt->code		= MEDIA_BUS_FMT_UYVY8_2X8;
+	fmt->colorspace		= V4L2_COLORSPACE_SRGB;
+	fmt->field		= V4L2_FIELD_NONE;
+	fmt->ycbcr_enc		= V4L2_YCBCR_ENC_DEFAULT;
+	fmt->quantization	= V4L2_QUANTIZATION_DEFAULT;
+	fmt->xfer_func		= V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format;
+	unsigned int i;
+
+	for (i = 0; i < MAX9286_N_SINKS; i++) {
+		format = v4l2_subdev_get_try_format(subdev, fh->pad, i);
+		max9286_init_format(format);
+	}
+
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
+	.open = max9286_open,
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe/Remove
+ */
+
+static int max9286_setup(struct max9286_device *dev)
+{
+	/*
+	 * Link ordering values for all enabled links combinations. Orders must
+	 * be assigned sequentially from 0 to the number of enabled links
+	 * without leaving any hole for disabled links. We thus assign orders to
+	 * enabled links first, and use the remaining order values for disabled
+	 * links are all links must have a different order value;
+	 */
+	static const u8 link_order[] = {
+		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */
+		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */
+		(3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */
+		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */
+		(3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */
+		(3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */
+		(3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */
+		(3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */
+		(0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */
+		(1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */
+		(1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */
+		(2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */
+		(1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */
+		(2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */
+		(2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */
+		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */
+	};
+
+	/*
+	 * Set the I2C bus speed.
+	 *
+	 * Enable I2C Local Acknowledge during the probe sequences of the camera
+	 * only. This should be disabled after the mux is initialised.
+	 */
+	max9286_configure_i2c(dev, true);
+
+	/*
+	 * Reverse channel setup.
+	 *
+	 * - 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.
+	 */
+	max9286_write(dev, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35));
+	max9286_write(dev, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) |
+		      MAX9286_REV_AMP_X);
+
+	/*
+	 * Enable GMSL links, mask unused ones and autodetect link
+	 * used as CSI clock source.
+	 */
+	max9286_write(dev, 0x00, MAX9286_MSTLINKSEL_AUTO | dev->route_mask);
+	max9286_write(dev, 0x0b, link_order[dev->route_mask]);
+	max9286_write(dev, 0x69, (0xf & ~dev->route_mask));
+
+	/*
+	 * Video format setup:
+	 * Disable CSI output, VC is set accordingly to Link number.
+	 */
+	max9286_write(dev, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
+
+	/* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */
+	max9286_write(dev, 0x12, MAX9286_CSIDBL	| MAX9286_DBL |
+		      MAX9286_CSILANECNT(dev->csi2_data_lanes) |
+		      MAX9286_DATATYPE_YUV422_8BIT);
+
+	/* Automatic: FRAMESYNC taken from the slowest Link. */
+	max9286_write(dev, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
+		      MAX9286_FSYNCMETH_AUTO);
+
+	/* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */
+	max9286_write(dev, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
+		      MAX9286_HVSRC_D14);
+
+	/*
+	 * Wait for 2ms to allow the link to resynchronize after the
+	 * configuration change.
+	 */
+	usleep_range(2000, 5000);
+
+	return 0;
+}
+
+static const struct of_device_id max9286_dt_ids[] = {
+	{ .compatible = "maxim,max9286" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, max9286_dt_ids);
+
+static int max9286_init(struct device *dev, void *data)
+{
+	struct max9286_device *max9286;
+	struct i2c_client *client;
+	struct device_node *ep;
+	unsigned int i;
+	int ret;
+
+	/* Skip non-max9286 devices. */
+	if (!dev->of_node || !of_match_node(max9286_dt_ids, dev->of_node))
+		return 0;
+
+	client = to_i2c_client(dev);
+	max9286 = i2c_get_clientdata(client);
+
+	/* Enable the bus power. */
+	ret = regulator_enable(max9286->regulator);
+	if (ret < 0) {
+		dev_err(&client->dev, "Unable to turn PoC on\n");
+		return ret;
+	}
+
+	max9286->poc_enabled = true;
+
+	ret = max9286_setup(max9286);
+	if (ret) {
+		dev_err(dev, "Unable to setup max9286\n");
+		goto err_regulator;
+	}
+
+	v4l2_i2c_subdev_init(&max9286->sd, client, &max9286_subdev_ops);
+	max9286->sd.internal_ops = &max9286_subdev_internal_ops;
+	max9286->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	v4l2_ctrl_handler_init(&max9286->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(&max9286->ctrls, NULL, V4L2_CID_PIXEL_RATE,
+			  50000000, 50000000, 1, 50000000);
+	max9286->sd.ctrl_handler = &max9286->ctrls;
+	ret = max9286->ctrls.error;
+	if (ret)
+		goto err_regulator;
+
+	max9286->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+
+	max9286->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE;
+	for (i = 0; i < MAX9286_SRC_PAD; i++)
+		max9286->pads[i].flags = MEDIA_PAD_FL_SINK;
+	ret = media_entity_pads_init(&max9286->sd.entity, MAX9286_N_PADS,
+				     max9286->pads);
+	if (ret)
+		goto err_regulator;
+
+	ep = of_graph_get_endpoint_by_regs(dev->of_node, MAX9286_SRC_PAD, -1);
+	if (!ep) {
+		dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n");
+		ret = -ENOENT;
+		goto err_regulator;
+	}
+	max9286->sd.fwnode = of_fwnode_handle(ep);
+
+	ret = v4l2_async_register_subdev(&max9286->sd);
+	if (ret < 0) {
+		dev_err(dev, "Unable to register subdevice\n");
+		goto err_put_node;
+	}
+
+	ret = max9286_i2c_mux_init(max9286);
+	if (ret) {
+		dev_err(dev, "Unable to initialize I2C multiplexer\n");
+		goto err_subdev_unregister;
+	}
+
+	/*
+	 * Re-configure I2C with local acknowledge disabled after cameras
+	 * have probed.
+	 */
+	max9286_configure_i2c(max9286, false);
+
+	/* Leave the mux channels disabled until they are selected. */
+	max9286_i2c_mux_close(max9286);
+
+	return 0;
+
+err_subdev_unregister:
+	v4l2_async_unregister_subdev(&max9286->sd);
+	max9286_i2c_mux_close(max9286);
+err_put_node:
+	of_node_put(ep);
+err_regulator:
+	regulator_disable(max9286->regulator);
+	max9286->poc_enabled = false;
+
+	return ret;
+}
+
+static int max9286_is_bound(struct device *dev, void *data)
+{
+	struct device *this = data;
+	int ret;
+
+	if (dev == this)
+		return 0;
+
+	/* Skip non-max9286 devices. */
+	if (!dev->of_node || !of_match_node(max9286_dt_ids, dev->of_node))
+		return 0;
+
+	ret = device_is_bound(dev);
+	if (!ret)
+		return -EPROBE_DEFER;
+
+	return 0;
+}
+
+static struct device_node *max9286_get_i2c_by_id(struct device_node *parent,
+						 u32 id)
+{
+	struct device_node *child;
+
+	for_each_child_of_node(parent, child) {
+		u32 i2c_id = 0;
+
+		if (of_node_cmp(child->name, "i2c") != 0)
+			continue;
+		of_property_read_u32(child, "reg", &i2c_id);
+		if (id == i2c_id)
+			return child;
+	}
+
+	return NULL;
+}
+
+static int max9286_check_i2c_bus_by_id(struct device *dev, int id)
+{
+	struct device_node *i2c_np;
+
+	i2c_np = max9286_get_i2c_by_id(dev->of_node, id);
+	if (!i2c_np) {
+		dev_err(dev, "Failed to find corresponding i2c@%u\n", id);
+		return -ENODEV;
+	}
+
+	if (!of_device_is_available(i2c_np)) {
+		dev_dbg(dev, "Skipping port %u with disabled I2C bus\n", id);
+		of_node_put(i2c_np);
+		return -ENODEV;
+	}
+
+	of_node_put(i2c_np);
+
+	return 0;
+}
+
+static void max9286_cleanup_dt(struct max9286_device *max9286)
+{
+	struct max9286_source *source;
+
+	/*
+	 * Not strictly part of the DT, but the notifier is registered during
+	 * max9286_parse_dt(), and the notifier holds references to the fwnodes
+	 * thus the cleanup is here to mirror the registration.
+	 */
+	v4l2_async_notifier_unregister(&max9286->notifier);
+
+	for_each_source(max9286, source) {
+		fwnode_handle_put(source->fwnode);
+		source->fwnode = NULL;
+	}
+}
+
+static int max9286_parse_dt(struct max9286_device *max9286)
+{
+	struct device *dev = &max9286->client->dev;
+	struct device_node *ep_np = NULL;
+	int ret;
+
+	v4l2_async_notifier_init(&max9286->notifier);
+
+	for_each_endpoint_of_node(dev->of_node, ep_np) {
+		struct max9286_source *source;
+		struct of_endpoint ep;
+
+		of_graph_parse_endpoint(ep_np, &ep);
+		dev_dbg(dev, "Endpoint %pOF on port %d",
+			ep.local_node, ep.port);
+
+		if (ep.port > MAX9286_NUM_GMSL) {
+			dev_err(dev, "Invalid endpoint %s on port %d",
+				of_node_full_name(ep.local_node),
+				ep.port);
+
+			continue;
+		}
+
+		/* For the source endpoint just parse the bus configuration. */
+		if (ep.port == MAX9286_SRC_PAD) {
+			struct v4l2_fwnode_endpoint vep;
+			int ret;
+
+			ret = v4l2_fwnode_endpoint_alloc_parse(
+					of_fwnode_handle(ep_np), &vep);
+			if (ret)
+				return ret;
+
+			if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) {
+				dev_err(dev,
+					"Media bus %u type not supported\n",
+					vep.bus_type);
+				v4l2_fwnode_endpoint_free(&vep);
+				return -EINVAL;
+			}
+
+			max9286->csi2_data_lanes =
+				vep.bus.mipi_csi2.num_data_lanes;
+			v4l2_fwnode_endpoint_free(&vep);
+
+			continue;
+		}
+
+		/* Skip if the corresponding GMSL link is unavailable. */
+		if (max9286_check_i2c_bus_by_id(dev, ep.port))
+			continue;
+
+		if (max9286->sources[ep.port].fwnode) {
+			dev_err(dev,
+				"Multiple port endpoints are not supported: %d",
+				ep.port);
+
+			continue;
+		}
+
+		source = &max9286->sources[ep.port];
+		source->fwnode = fwnode_graph_get_remote_endpoint(
+						of_fwnode_handle(ep_np));
+		if (!source->fwnode) {
+			dev_err(dev,
+				"Endpoint %pOF has no remote endpoint connection\n",
+				ep.local_node);
+
+			continue;
+		}
+
+		source->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+		source->asd.match.fwnode = source->fwnode;
+
+		ret = v4l2_async_notifier_add_subdev(&max9286->notifier,
+						     &source->asd);
+		if (ret) /* TODO: Cleanup notifier! */
+			return ret;
+
+		max9286->source_mask |= BIT(ep.port);
+		max9286->nsources++;
+	}
+
+	/* Do not register the subdev notifier if there are no devices. */
+	if (!max9286->nsources)
+		return 0;
+
+	max9286->route_mask = max9286->source_mask;
+	max9286->notifier.ops = &max9286_notify_ops;
+
+	return v4l2_async_subdev_notifier_register(&max9286->sd,
+						   &max9286->notifier);
+}
+
+static int max9286_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	struct max9286_device *dev;
+	unsigned int i;
+	int ret;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->client = client;
+	i2c_set_clientdata(client, dev);
+
+	for (i = 0; i < MAX9286_N_SINKS; i++)
+		max9286_init_format(&dev->fmt[i]);
+
+	ret = max9286_parse_dt(dev);
+	if (ret)
+		return ret;
+
+	dev->regulator = regulator_get(&client->dev, "poc");
+	if (IS_ERR(dev->regulator)) {
+		if (PTR_ERR(dev->regulator) != -EPROBE_DEFER)
+			dev_err(&client->dev,
+				"Unable to get PoC regulator (%ld)\n",
+				PTR_ERR(dev->regulator));
+		ret = PTR_ERR(dev->regulator);
+		goto err_free;
+	}
+
+	/*
+	 * We can have multiple MAX9286 instances on the same physical I2C
+	 * bus, and I2C children behind ports of separate MAX9286 instances
+	 * having the same I2C address. As the MAX9286 starts by default with
+	 * all ports enabled, we need to disable all ports on all MAX9286
+	 * instances before proceeding to further initialize the devices and
+	 * instantiate children.
+	 *
+	 * Start by just disabling all channels on the current device. Then,
+	 * if all other MAX9286 on the parent bus have been probed, proceed
+	 * to initialize them all, including the current one.
+	 */
+	max9286_i2c_mux_close(dev);
+
+	/*
+	 * The MAX9286 initialises with auto-acknowledge enabled by default.
+	 * This means that if multiple MAX9286 devices are connected to an I2C
+	 * bus, another MAX9286 could ack I2C transfers meant for a device on
+	 * the other side of the GMSL links for this MAX9286 (such as a
+	 * MAX9271). To prevent that disable auto-acknowledge early on; it
+	 * will be enabled later as needed.
+	 */
+	max9286_configure_i2c(dev, false);
+
+	ret = device_for_each_child(client->dev.parent, &client->dev,
+				    max9286_is_bound);
+	if (ret)
+		return 0;
+
+	dev_dbg(&client->dev,
+		"All max9286 probed: start initialization sequence\n");
+	ret = device_for_each_child(client->dev.parent, NULL,
+				    max9286_init);
+	if (ret < 0)
+		goto err_regulator;
+
+	/* Leave the mux channels disabled until they are selected. */
+	max9286_i2c_mux_close(dev);
+
+	return 0;
+
+err_regulator:
+	regulator_put(dev->regulator);
+	max9286_i2c_mux_close(dev);
+	max9286_configure_i2c(dev, false);
+err_free:
+	max9286_cleanup_dt(dev);
+	kfree(dev);
+
+	return ret;
+}
+
+static int max9286_remove(struct i2c_client *client)
+{
+	struct max9286_device *dev = i2c_get_clientdata(client);
+
+	i2c_mux_del_adapters(dev->mux);
+
+	fwnode_handle_put(dev->sd.fwnode);
+	v4l2_async_unregister_subdev(&dev->sd);
+
+	if (dev->poc_enabled)
+		regulator_disable(dev->regulator);
+	regulator_put(dev->regulator);
+
+	max9286_cleanup_dt(dev);
+	kfree(dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id max9286_id[] = {
+	{ "max9286", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max9286_id);
+
+static struct i2c_driver max9286_i2c_driver = {
+	.driver	= {
+		.name		= "max9286",
+		.of_match_table	= of_match_ptr(max9286_dt_ids),
+	},
+	.probe		= max9286_probe,
+	.remove		= max9286_remove,
+	.id_table	= max9286_id,
+};
+
+module_i2c_driver(max9286_i2c_driver);
+
+MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
-- 
2.17.1


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

* [PATCH v4 4/4] media: i2c: Add RDACM20 driver
  2018-11-02 15:47 [PATCH v4 0/4] MAX9286 GMSL Support Kieran Bingham
                   ` (2 preceding siblings ...)
  2018-11-02 15:47 ` [PATCH v4 3/4] media: i2c: Add MAX9286 driver Kieran Bingham
@ 2018-11-02 15:47 ` Kieran Bingham
  2018-11-20  8:34   ` Sakari Ailus
  3 siblings, 1 reply; 26+ messages in thread
From: Kieran Bingham @ 2018-11-02 15:47 UTC (permalink / raw)
  To: linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

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.

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
---
 MAINTAINERS                         |  10 +
 drivers/media/i2c/Kconfig           |  11 +
 drivers/media/i2c/Makefile          |   1 +
 drivers/media/i2c/rdacm20-ov10635.h | 953 ++++++++++++++++++++++++++++
 drivers/media/i2c/rdacm20.c         | 635 ++++++++++++++++++
 5 files changed, 1610 insertions(+)
 create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
 create mode 100644 drivers/media/i2c/rdacm20.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 745f0fd1fff1..26ef20087a43 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12230,6 +12230,16 @@ S:	Supported
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
 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/rdacm20.txt
+F:	drivers/media/i2c/rdacm20*
+
 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 eadc00bdd3bf..5eded5e337ec 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -989,6 +989,17 @@ config VIDEO_S5C73M3
 	  This is a V4L2 sensor driver for Samsung S5C73M3
 	  8 Mpixel camera.
 
+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"
 
 config VIDEO_ADP1653
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 4de7fe62b179..121d28283d45 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -111,5 +111,6 @@ obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
 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_SDR_MAX2175) += max2175.o
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..d96b2eb5ab1b
--- /dev/null
+++ b/drivers/media/i2c/rdacm20.c
@@ -0,0 +1,635 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IMI RDACM20 GMSL Camera Driver
+ *
+ * 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.
+ */
+
+/*
+ * The camera is mode 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"
+
+#define RDACM20_SENSOR_HARD_RESET
+
+#define MAX9271_I2C_ADDRESS		0x40
+
+/* 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_SETGPO			BIT(0)
+/* Register 0x15 */
+#define MAX9271_PCLKDET			BIT(0)
+
+#define MAXIM_I2C_I2C_SPEED_400KHZ	MAX9271_I2CMSTBT_339KBPS
+#define MAXIM_I2C_I2C_SPEED_100KHZ	MAX9271_I2CMSTBT_105KBPS
+#define MAXIM_I2C_SPEED			MAXIM_I2C_I2C_SPEED_100KHZ
+
+#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
+/* #define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY10_2X10 */
+
+struct rdacm20_device {
+	struct i2c_client		*client;
+	struct i2c_client		*sensor;
+	struct v4l2_subdev		sd;
+	struct media_pad		pad;
+	struct v4l2_ctrl_handler	ctrls;
+};
+
+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 max9271_read(struct rdacm20_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 rdacm20_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;
+}
+
+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->client->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->client->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->client->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->client->dev,
+				"%s: register %u (0x%04x) write failed (%d)\n",
+				__func__, i, regs[i].reg, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * rdacm20_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 rdacm20_pclk_detect(struct rdacm20_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;
+}
+
+static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct rdacm20_device *dev = sd_to_rdacm20(sd);
+	int ret;
+
+	if (enable) {
+		ret = rdacm20_pclk_detect(dev);
+		if (ret)
+			return ret;
+
+		/* Enable the serial link. */
+		max9271_write(dev, 0x04, MAX9271_SEREN | MAX9271_REVCCEN |
+			      MAX9271_FWDCCEN);
+	} else {
+		/* Disable the serial link. */
+		max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
+			      MAX9271_FWDCCEN);
+	}
+
+	return 0;
+}
+
+static int rdacm20_g_mbus_config(struct v4l2_subdev *sd,
+				 struct v4l2_mbus_config *cfg)
+{
+	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	cfg->type = V4L2_MBUS_CSI2_DPHY;
+
+	return 0;
+}
+
+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,
+	.g_mbus_config	= rdacm20_g_mbus_config,
+};
+
+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 max9271_configure_i2c(struct rdacm20_device *dev)
+{
+	/*
+	 * Configure the I2C bus:
+	 *
+	 * - Enable high thresholds on the reverse channel
+	 * - Disable artificial ACK and set I2C speed
+	 */
+	max9271_write(dev, 0x08, MAX9271_REV_HIVTH);
+	usleep_range(5000, 8000);
+
+	max9271_write(dev, 0x0d, MAX9271_I2CSLVSH_469NS_234NS |
+		      MAX9271_I2CSLVTO_1024US | MAXIM_I2C_SPEED);
+	usleep_range(5000, 8000);
+
+	return 0;
+}
+
+static int max9271_configure_gmsl_link(struct rdacm20_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);
+
+	return 0;
+}
+
+static int max9271_verify_id(struct rdacm20_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;
+}
+
+static int max9271_configure_address(struct rdacm20_device *dev, u8 addr)
+{
+	int ret;
+
+	/* Change the MAX9271 I2C address. */
+	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;
+	}
+	dev->client->addr = addr;
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+
+static int rdacm20_initialize(struct rdacm20_device *dev)
+{
+	u32 addrs[2];
+	int ret;
+
+	ret = of_property_read_u32_array(dev->client->dev.of_node, "reg",
+					 addrs, ARRAY_SIZE(addrs));
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "Invalid DT reg property\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * FIXME: The MAX9271 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 = MAX9271_I2C_ADDRESS;
+
+	/* Verify communication with the MAX9271. */
+	i2c_smbus_read_byte(dev->client);	/* ping to wake-up */
+
+	/*
+	 *  Ensure that we have a good link configuration before attempting to
+	 *  identify the device.
+	 */
+	max9271_configure_i2c(dev);
+	max9271_configure_gmsl_link(dev);
+
+	ret = max9271_verify_id(dev);
+	if (ret < 0)
+		return ret;
+
+	ret = max9271_configure_address(dev, addrs[0]);
+	if (ret < 0)
+		return ret;
+
+	/* Reset and verify communication with the OV10635. */
+#ifdef RDACM20_SENSOR_HARD_RESET
+	/* Cycle the OV10635 reset signal connected to the MAX9271 GPIO1. */
+	max9271_write(dev, 0x0f, 0xff & ~(MAX9271_GPIO1OUT | MAX9271_SETGPO));
+	mdelay(10);
+	max9271_write(dev, 0x0f, 0xff & ~MAX9271_SETGPO);
+	mdelay(10);
+#else
+	/* Perform a software reset. */
+	ret = ov10635_write(dev, OV10635_SOFTWARE_RESET, 1);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "OV10635 reset failed (%d)\n", ret);
+		return -ENXIO;
+	}
+
+	udelay(100);
+#endif
+
+	ret = ov10635_read16(dev, OV10635_PID);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "OV10635 ID read failed (%d)\n",
+			ret);
+		return -ENXIO;
+	}
+
+	if (ret != OV10635_VERSION) {
+		dev_err(&dev->client->dev, "OV10635 ID mismatch (0x%04x)\n",
+			ret);
+		return -ENXIO;
+	}
+
+	dev_info(&dev->client->dev, "Identified MAX9271 + OV10635 device\n");
+
+	/* Change the sensor I2C address. */
+	ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
+			    (addrs[1] << 1) | OV10635_SC_CMMN_SCCB_ID_SELECT);
+	if (ret < 0) {
+		dev_err(&dev->client->dev,
+			"OV10635 I2C address change failed (%d)\n", ret);
+		return ret;
+	}
+	dev->sensor->addr = 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;
+
+	return 0;
+}
+
+static int rdacm20_probe(struct i2c_client *client,
+			 const struct i2c_device_id *did)
+{
+	struct rdacm20_device *dev;
+	struct fwnode_handle *ep;
+	int ret;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->client = client;
+
+	/* Create the dummy I2C client for the sensor. */
+	dev->sensor = i2c_new_dummy(client->adapter, OV10635_I2C_ADDRESS);
+	if (!dev->sensor) {
+		ret = -ENXIO;
+		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;
+
+	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(ep);
+error:
+	media_entity_cleanup(&dev->sd.entity);
+	if (dev->sensor)
+		i2c_unregister_device(dev->sensor);
+	kfree(dev);
+
+	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);
+	media_entity_cleanup(&dev->sd.entity);
+	i2c_unregister_device(dev->sensor);
+	kfree(dev);
+
+	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 i2c_device_id rdacm20_id[] = {
+	{ "rdacm20", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, rdacm20_id);
+
+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		= rdacm20_probe,
+	.remove		= rdacm20_remove,
+	.shutdown	= rdacm20_shutdown,
+	.id_table	= rdacm20_id,
+};
+
+module_i2c_driver(rdacm20_i2c_driver);
+
+MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
-- 
2.17.1


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

* Re: [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286
  2018-11-02 15:47 ` [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286 Kieran Bingham
@ 2018-11-05 20:02   ` Rob Herring
  2018-11-13 22:42   ` Luca Ceresoli
  1 sibling, 0 replies; 26+ messages in thread
From: Rob Herring @ 2018-11-05 20:02 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: linux-renesas-soc, linux-media, devicetree, sakari.ailus,
	Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Laurent Pinchart, Jacopo Mondi

On Fri, Nov 02, 2018 at 03:47:20PM +0000, Kieran Bingham wrote:
> From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> 
> The MAX9286 deserializes video data received on up to 4 Gigabit
> Multimedia Serial Links (GMSL) and outputs them on a CSI-2 port using up
> to 4 data lanes.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> 
> ---
> v3:
>  - Update binding descriptions
> 
> v4:
>  - Define the use of a CSI2 D-PHY
>  - Rename pwdn-gpios to enable-gpios (with inverted polarity)
>  - Remove clock-lanes mapping support
>  - rewrap text blocks
>  - Fix typos
> ---
>  .../bindings/media/i2c/maxim,max9286.txt      | 182 ++++++++++++++++++
>  1 file changed, 182 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
> new file mode 100644
> index 000000000000..672f6a4d417d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
> @@ -0,0 +1,182 @@
> +Maxim Integrated Quad GMSL Deserializer
> +---------------------------------------
> +
> +The MAX9286 deserializer receives video data on up to 4 Gigabit Multimedia
> +Serial Links (GMSL) and outputs them on a CSI-2 D-PHY port using up to 4 data
> +lanes.
> +
> +In addition to video data, the GMSL links carry a bidirectional control channel
> +that encapsulates I2C messages. The MAX9286 forwards all I2C traffic not
> +addressed to itself to the other side of the links, where a GMSL serializer
> +will output it on a local I2C bus. In the other direction all I2C traffic
> +received over GMSL by the MAX9286 is output on the local I2C bus.
> +
> +Required Properties:
> +
> +- compatible: Shall be "maxim,max9286"
> +- reg: I2C device address
> +
> +Optional Properties:
> +
> +- poc-supply: Regulator providing Power over Coax to the cameras
> +- enable-gpios: GPIO connected to the #PWDN pin with inverted polarity
> +
> +Required endpoint nodes:
> +-----------------------
> +
> +The connections to the MAX9286 GMSL and its endpoint nodes are modeled using
> +the OF graph bindings in accordance with the video interface bindings defined
> +in Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +The following table lists the port number corresponding to each device port.
> +
> +        Port            Description
> +        ----------------------------------------
> +        Port 0          GMSL Input 0
> +        Port 1          GMSL Input 1
> +        Port 2          GMSL Input 2
> +        Port 3          GMSL Input 3
> +        Port 4          CSI-2 Output
> +
> +Optional Endpoint Properties for GMSL Input Ports (Port [0-3]):
> +
> +- remote-endpoint: phandle to the remote GMSL source endpoint subnode in the
> +  remote node port.
> +
> +Required Endpoint Properties for CSI-2 Output Port (Port 4):
> +
> +- remote-endpoint: phandle to the remote CSI-2 sink endpoint node.
> +- data-lanes: array of physical CSI-2 data lane indexes.
> +
> +Required i2c-mux nodes:
> +----------------------
> +
> +Each GMSL link is modeled as a child bus of an i2c bus multiplexer/switch, in
> +accordance with bindings described in
> +Documentation/devicetree/bindings/i2c/i2c-mux.txt. The serializer device on the
> +remote end of the GMSL link shall be modelled as a child node of the
> +corresponding I2C bus.
> +
> +Required i2c child bus properties:
> +- all properties described as required i2c child bus nodes properties in
> +  Documentation/devicetree/bindings/i2c/i2c-mux.txt.
> +
> +Example:
> +-------
> +
> +	gmsl-deserializer@2c {
> +		compatible = "maxim,max9286";
> +		reg = <0x2c>;
> +		poc-supply = <&camera_poc_12v>;
> +		enable-gpios = <&gpio 13 GPIO_ACTIVE_LOW>;
> +
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;

> +
> +		i2c@0 {
> +			#address-cells = <1>;
> +			#size-cells = <0>;

It's better to not have a mixture of nodes at a level with and without 
unit-addresses. So I'd move all the i2c nodes under an 'i2c-mux' node.

Rob

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

* Re: [PATCH v4 2/4] dt-bindings: media: i2c: Add bindings for IMI RDACM20
  2018-11-02 15:47 ` [PATCH v4 2/4] dt-bindings: media: i2c: Add bindings for IMI RDACM20 Kieran Bingham
@ 2018-11-05 20:13   ` Rob Herring
  0 siblings, 0 replies; 26+ messages in thread
From: Rob Herring @ 2018-11-05 20:13 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: linux-renesas-soc, linux-media, devicetree, sakari.ailus,
	Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi

On Fri,  2 Nov 2018 15:47:21 +0000, Kieran Bingham wrote:
> From: Jacopo Mondi <jacopo+renesas@jmondi.org>
> 
> The IMI RDACM20 is a Gigabit Multimedia Serial Link (GMSL) camera
> capable of transmitting video and I2C control messages on a coax cable
> physical link for automotive applications.
> 
> Document its device tree binding interface.
> 
> 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
> ---
>  .../bindings/media/i2c/imi,rdacm20.txt        | 65 +++++++++++++++++++
>  .../devicetree/bindings/vendor-prefixes.txt   |  1 +
>  2 files changed, 66 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/imi,rdacm20.txt
> 

Reviewed-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-02 15:47 ` [PATCH v4 3/4] media: i2c: Add MAX9286 driver Kieran Bingham
@ 2018-11-07 15:06   ` Kieran Bingham
  2018-11-07 17:24     ` Luca Ceresoli
  2018-11-13 22:49   ` Luca Ceresoli
  1 sibling, 1 reply; 26+ messages in thread
From: Kieran Bingham @ 2018-11-07 15:06 UTC (permalink / raw)
  To: Kieran Bingham, linux-renesas-soc, linux-media, luca
  Cc: devicetree, sakari.ailus, Niklas Söderlund, Jacopo Mondi,
	Laurent Pinchart, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Luca

<Top posting for new topic>

> <lucaceresoli> kbingham: hi, I'm looking at your GMSL v4 patches
> <lucaceresoli> kbingham: jmondi helped me in understanding parts of it, but I still have a question
> <lucaceresoli> kbingham: are the drivers waiting for the established link before the remote chips are accessed? where?

I'm replying here rather than spam the IRC channel with a big paste.
It's also a useful description to the probe sequence, so I've kept it
with the driver posting.

I hope the following helps illustrate the sequences which are involved:

max9286_probe()
 - max9286_i2c_mux_close() # Disable all links
 - max9286_configure_i2c # Configure early communication settings
 - max9286_init():
   - regulator_enable() # Power up all cameras
   - max9286_setup() # Most link setup is done here.
   ... Set up v4l2/async/media-controller endpoints
   - max9286_i2c_mux_init() # Start configuring cameras:
     - i2c_mux_alloc() # Create our mux device
     - for_each_source(dev, source)
           i2c_mux_add_adapter() # This is where sensors get probed.

So yes sensors are only communicated with once the link is brought up as
much as possible.

Because the sensors are i2c devices on the i2c_mux - they are not probed
until their adapters are created and added.

At this stage the i2c-mux core framework will iterate all the devices
described by the DT for that adapter.

As each one is probed - the i2c_mux framework will call
max9286_i2c_mux_select() and enable only the single link.

This allows us to configure each camera independently

(which is essential because they are all configured to the same i2c
address by default at power on)


Hope this helps, and feel free to ask if you have any more questions.

--
Regards

Kieran


On 02/11/2018 15:47, Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> 
> The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and
> CSI-2 output. The device supports multicamera streaming applications,
> and features the ability to synchronise the attached cameras.
> 
> CSI-2 output can be configured with 1 to 4 lanes, and a control channel
> is supported over I2C, which implements an I2C mux to facilitate
> communications with connected cameras across the reverse control
> channel.
> 
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> 
> --
> v2:
>  - Fix MAINTAINERS entry
> 
> This posting is released with the following modifications to work
> without Sakari's VC developments:
>  - max9286_g_mbus_config() re-instated
>  - max9286_get_frame_desc() is not bus/csi aware
>  - max9286_{get,set}_routing() removed
> 
> v3:
>  - Initialise notifier with v4l2_async_notifier_init
>  - Update for new mbus csi2 format V4L2_MBUS_CSI2_DPHY
> 
> v4: - Re-introduce required code to function with the VC series.
> 
>  - Implement max9286_get_routing, max9286_set_routing
>  - Remove max9286_g_mbus_config
> ---
>  MAINTAINERS                 |   10 +
>  drivers/media/i2c/Kconfig   |   11 +
>  drivers/media/i2c/Makefile  |    1 +
>  drivers/media/i2c/max9286.c | 1189 +++++++++++++++++++++++++++++++++++
>  4 files changed, 1211 insertions(+)
>  create mode 100644 drivers/media/i2c/max9286.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 23021e0df5d7..745f0fd1fff1 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8864,6 +8864,16 @@ F:	Documentation/devicetree/bindings/hwmon/max6697.txt
>  F:	drivers/hwmon/max6697.c
>  F:	include/linux/platform_data/max6697.h
>  
> +MAX9286 QUAD GMSL DESERIALIZER DRIVER
> +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/max9286.txt
> +F:	drivers/media/i2c/max9286.c
> +
>  MAX9860 MONO AUDIO VOICE CODEC DRIVER
>  M:	Peter Rosin <peda@axentia.se>
>  L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 704af210e270..eadc00bdd3bf 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -472,6 +472,17 @@ config VIDEO_VPX3220
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called vpx3220.
>  
> +config VIDEO_MAX9286
> +	tristate "Maxim MAX9286 GMSL deserializer support"
> +	depends on I2C && I2C_MUX
> +	depends on VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
> +	select V4L2_FWNODE
> +	help
> +	  This driver supports the Maxim MAX9286 GMSL deserializer.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called max9286.
> +
>  comment "Video and audio decoders"
>  
>  config VIDEO_SAA717X
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 260d4d9ec2a1..4de7fe62b179 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -110,5 +110,6 @@ obj-$(CONFIG_VIDEO_IMX258)	+= imx258.o
>  obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
>  obj-$(CONFIG_VIDEO_IMX319)	+= imx319.o
>  obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
> +obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
>  
>  obj-$(CONFIG_SDR_MAX2175) += max2175.o
> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> new file mode 100644
> index 000000000000..c39c2f86e07d
> --- /dev/null
> +++ b/drivers/media/i2c/max9286.c
> @@ -0,0 +1,1189 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Maxim MAX9286 GMSL Deserializer Driver
> + *
> + * 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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/fwnode.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c-mux.h>
> +#include <linux/module.h>
> +#include <linux/of_graph.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +/* Register 0x00 */
> +#define MAX9286_MSTLINKSEL_AUTO		(7 << 5)
> +#define MAX9286_MSTLINKSEL(n)		((n) << 5)
> +#define MAX9286_EN_VS_GEN		BIT(4)
> +#define MAX9286_LINKEN(n)		(1 << (n))
> +/* Register 0x01 */
> +#define MAX9286_FSYNCMODE_ECU		(3 << 6)
> +#define MAX9286_FSYNCMODE_EXT		(2 << 6)
> +#define MAX9286_FSYNCMODE_INT_OUT	(1 << 6)
> +#define MAX9286_FSYNCMODE_INT_HIZ	(0 << 6)
> +#define MAX9286_GPIEN			BIT(5)
> +#define MAX9286_ENLMO_RSTFSYNC		BIT(2)
> +#define MAX9286_FSYNCMETH_AUTO		(2 << 0)
> +#define MAX9286_FSYNCMETH_SEMI_AUTO	(1 << 0)
> +#define MAX9286_FSYNCMETH_MANUAL	(0 << 0)
> +#define MAX9286_REG_FSYNC_PERIOD_L	0x06
> +#define MAX9286_REG_FSYNC_PERIOD_M	0x07
> +#define MAX9286_REG_FSYNC_PERIOD_H	0x08
> +/* Register 0x0a */
> +#define MAX9286_FWDCCEN(n)		(1 << ((n) + 4))
> +#define MAX9286_REVCCEN(n)		(1 << (n))
> +/* Register 0x0c */
> +#define MAX9286_HVEN			BIT(7)
> +#define MAX9286_EDC_6BIT_HAMMING	(2 << 5)
> +#define MAX9286_EDC_6BIT_CRC		(1 << 5)
> +#define MAX9286_EDC_1BIT_PARITY		(0 << 5)
> +#define MAX9286_DESEL			BIT(4)
> +#define MAX9286_INVVS			BIT(3)
> +#define MAX9286_INVHS			BIT(2)
> +#define MAX9286_HVSRC_D0		(2 << 0)
> +#define MAX9286_HVSRC_D14		(1 << 0)
> +#define MAX9286_HVSRC_D18		(0 << 0)
> +/* Register 0x12 */
> +#define MAX9286_CSILANECNT(n)		(((n) - 1) << 6)
> +#define MAX9286_CSIDBL			BIT(5)
> +#define MAX9286_DBL			BIT(4)
> +#define MAX9286_DATATYPE_USER_8BIT	(11 << 0)
> +#define MAX9286_DATATYPE_USER_YUV_12BIT	(10 << 0)
> +#define MAX9286_DATATYPE_USER_24BIT	(9 << 0)
> +#define MAX9286_DATATYPE_RAW14		(8 << 0)
> +#define MAX9286_DATATYPE_RAW11		(7 << 0)
> +#define MAX9286_DATATYPE_RAW10		(6 << 0)
> +#define MAX9286_DATATYPE_RAW8		(5 << 0)
> +#define MAX9286_DATATYPE_YUV422_10BIT	(4 << 0)
> +#define MAX9286_DATATYPE_YUV422_8BIT	(3 << 0)
> +#define MAX9286_DATATYPE_RGB555		(2 << 0)
> +#define MAX9286_DATATYPE_RGB565		(1 << 0)
> +#define MAX9286_DATATYPE_RGB888		(0 << 0)
> +/* Register 0x15 */
> +#define MAX9286_VC(n)			((n) << 5)
> +#define MAX9286_VCTYPE			BIT(4)
> +#define MAX9286_CSIOUTEN		BIT(3)
> +#define MAX9286_0X15_RESV		(3 << 0)
> +/* Register 0x1b */
> +#define MAX9286_SWITCHIN(n)		(1 << ((n) + 4))
> +#define MAX9286_ENEQ(n)			(1 << (n))
> +/* Register 0x27 */
> +#define MAX9286_LOCKED			BIT(7)
> +/* Register 0x31 */
> +#define MAX9286_FSYNC_LOCKED		BIT(6)
> +/* Register 0x34 */
> +#define MAX9286_I2CLOCACK		BIT(7)
> +#define MAX9286_I2CSLVSH_1046NS_469NS	(3 << 5)
> +#define MAX9286_I2CSLVSH_938NS_352NS	(2 << 5)
> +#define MAX9286_I2CSLVSH_469NS_234NS	(1 << 5)
> +#define MAX9286_I2CSLVSH_352NS_117NS	(0 << 5)
> +#define MAX9286_I2CMSTBT_837KBPS	(7 << 2)
> +#define MAX9286_I2CMSTBT_533KBPS	(6 << 2)
> +#define MAX9286_I2CMSTBT_339KBPS	(5 << 2)
> +#define MAX9286_I2CMSTBT_173KBPS	(4 << 2)
> +#define MAX9286_I2CMSTBT_105KBPS	(3 << 2)
> +#define MAX9286_I2CMSTBT_84KBPS		(2 << 2)
> +#define MAX9286_I2CMSTBT_28KBPS		(1 << 2)
> +#define MAX9286_I2CMSTBT_8KBPS		(0 << 2)
> +#define MAX9286_I2CSLVTO_NONE		(3 << 0)
> +#define MAX9286_I2CSLVTO_1024US		(2 << 0)
> +#define MAX9286_I2CSLVTO_256US		(1 << 0)
> +#define MAX9286_I2CSLVTO_64US		(0 << 0)
> +/* Register 0x3b */
> +#define MAX9286_REV_TRF(n)		((n) << 4)
> +#define MAX9286_REV_AMP(n)		((((n) - 30) / 10) << 1) /* in mV */
> +#define MAX9286_REV_AMP_X		BIT(0)
> +/* Register 0x3f */
> +#define MAX9286_EN_REV_CFG		BIT(6)
> +#define MAX9286_REV_FLEN(n)		((n) - 20)
> +/* Register 0x49 */
> +#define MAX9286_VIDEO_DETECT_MASK	0x0f
> +/* Register 0x69 */
> +#define MAX9286_LFLTBMONMASKED		BIT(7)
> +#define MAX9286_LOCKMONMASKED		BIT(6)
> +#define MAX9286_AUTOCOMBACKEN		BIT(5)
> +#define MAX9286_AUTOMASKEN		BIT(4)
> +#define MAX9286_MASKLINK(n)		((n) << 0)
> +
> +#define MAX9286_NUM_GMSL		4
> +#define MAX9286_N_SINKS			4
> +#define MAX9286_N_PADS			5
> +#define MAX9286_SRC_PAD			4
> +
> +#define MAXIM_I2C_I2C_SPEED_400KHZ	MAX9286_I2CMSTBT_339KBPS
> +#define MAXIM_I2C_I2C_SPEED_100KHZ	MAX9286_I2CMSTBT_105KBPS
> +#define MAXIM_I2C_SPEED			MAXIM_I2C_I2C_SPEED_100KHZ
> +
> +struct max9286_source {
> +	struct v4l2_async_subdev asd;
> +	struct v4l2_subdev *sd;
> +	struct fwnode_handle *fwnode;
> +};
> +
> +#define asd_to_max9286_source(_asd) \
> +	container_of(_asd, struct max9286_source, asd)
> +
> +struct max9286_device {
> +	struct i2c_client *client;
> +	struct v4l2_subdev sd;
> +	struct media_pad pads[MAX9286_N_PADS];
> +	struct regulator *regulator;
> +	bool poc_enabled;
> +	int streaming;
> +
> +	struct i2c_mux_core *mux;
> +	unsigned int mux_channel;
> +
> +	struct v4l2_ctrl_handler ctrls;
> +
> +	struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
> +
> +	unsigned int nsources;
> +	unsigned int source_mask;
> +	unsigned int route_mask;
> +	unsigned int csi2_data_lanes;
> +	struct max9286_source sources[MAX9286_NUM_GMSL];
> +	struct v4l2_async_notifier notifier;
> +};
> +
> +static struct max9286_source *next_source(struct max9286_device *max9286,
> +					  struct max9286_source *source)
> +{
> +	if (!source)
> +		source = &max9286->sources[0];
> +	else
> +		source++;
> +
> +	for (; source < &max9286->sources[MAX9286_NUM_GMSL]; source++) {
> +		if (source->fwnode)
> +			return source;
> +	}
> +
> +	return NULL;
> +}
> +
> +#define for_each_source(max9286, source) \
> +	for (source = NULL; (source = next_source(max9286, source)); )
> +
> +#define to_index(max9286, source) (source - &max9286->sources[0])
> +
> +#define sd_to_max9286(_sd) container_of(_sd, struct max9286_device, sd)
> +
> +/* -----------------------------------------------------------------------------
> + * I2C IO
> + */
> +
> +static int max9286_read(struct max9286_device *dev, u8 reg)
> +{
> +	int ret;
> +
> +	ret = i2c_smbus_read_byte_data(dev->client, reg);
> +	if (ret < 0)
> +		dev_err(&dev->client->dev,
> +			"%s: register 0x%02x read failed (%d)\n",
> +			__func__, reg, ret);
> +
> +	return ret;
> +}
> +
> +static int max9286_write(struct max9286_device *dev, u8 reg, u8 val)
> +{
> +	int ret;
> +
> +	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
> +	if (ret < 0)
> +		dev_err(&dev->client->dev,
> +			"%s: register 0x%02x write failed (%d)\n",
> +			__func__, reg, ret);
> +
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * I2C Multiplexer
> + */
> +
> +static int max9286_i2c_mux_close(struct max9286_device *dev)
> +{
> +
> +	/* FIXME: See note in max9286_i2c_mux_select() */
> +	if (dev->streaming)
> +		return 0;
> +	/*
> +	 * Ensure that both the forward and reverse channel are disabled on the
> +	 * mux, and that the channel ID is invalidated to ensure we reconfigure
> +	 * on the next select call.
> +	 */
> +	dev->mux_channel = -1;
> +	max9286_write(dev, 0x0a, 0x00);
> +	usleep_range(3000, 5000);
> +
> +	return 0;
> +}
> +
> +static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
> +{
> +	struct max9286_device *dev = i2c_mux_priv(muxc);
> +
> +	/*
> +	 * FIXME: This state keeping is a hack and do the job. It should
> +	 * be should be reworked. One option to consider is that once all
> +	 * cameras are programmed the mux selection logic should be disabled
> +	 * and all all reverse and forward channels enable all the time.
> +	 *
> +	 * In any case this logic with a int that have two states should be
> +	 * reworked!
> +	 */
> +	if (dev->streaming == 1) {
> +		max9286_write(dev, 0x0a, 0xff);
> +		dev->streaming = 2;
> +		return 0;
> +	} else if (dev->streaming == 2) {
> +		return 0;
> +	}
> +
> +	if (dev->mux_channel == chan)
> +		return 0;
> +
> +	dev->mux_channel = chan;
> +
> +	max9286_write(dev, 0x0a, MAX9286_FWDCCEN(chan) | MAX9286_REVCCEN(chan));
> +
> +	/*
> +	 * We must sleep after any change to the forward or reverse channel
> +	 * configuration.
> +	 */
> +	usleep_range(3000, 5000);
> +
> +	return 0;
> +}
> +
> +static int max9286_i2c_mux_init(struct max9286_device *dev)
> +{
> +	struct max9286_source *source;
> +	int ret;
> +
> +	if (!i2c_check_functionality(dev->client->adapter,
> +				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
> +		return -ENODEV;
> +
> +	dev->mux = i2c_mux_alloc(dev->client->adapter, &dev->client->dev,
> +				 dev->nsources, 0, I2C_MUX_LOCKED,
> +				 max9286_i2c_mux_select, NULL);
> +	if (!dev->mux)
> +		return -ENOMEM;
> +
> +	dev->mux->priv = dev;
> +
> +	for_each_source(dev, source) {
> +		unsigned int index = to_index(dev, source);
> +
> +		ret = i2c_mux_add_adapter(dev->mux, 0, index, 0);
> +		if (ret < 0)
> +			goto error;
> +	}
> +
> +	return 0;
> +
> +error:
> +	i2c_mux_del_adapters(dev->mux);
> +	return ret;
> +}
> +
> +static void max9286_configure_i2c(struct max9286_device *dev, bool localack)
> +{
> +	u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
> +		    MAXIM_I2C_SPEED;
> +
> +	if (localack)
> +		config |= MAX9286_I2CLOCACK;
> +
> +	max9286_write(dev, 0x34, config);
> +	usleep_range(3000, 5000);
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 Subdev
> + */
> +
> +static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> +				struct v4l2_subdev *subdev,
> +				struct v4l2_async_subdev *asd)
> +{
> +	struct max9286_device *dev = sd_to_max9286(notifier->sd);
> +	struct max9286_source *source = asd_to_max9286_source(asd);
> +	unsigned int index = to_index(dev, source);
> +	unsigned int src_pad;
> +	int ret;
> +
> +	ret = media_entity_get_fwnode_pad(&subdev->entity,
> +					  source->fwnode,
> +					  MEDIA_PAD_FL_SOURCE);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev,
> +			"Failed to find pad for %s\n", subdev->name);
> +		return ret;
> +	}
> +
> +	source->sd = subdev;
> +	src_pad = ret;
> +
> +	ret = media_create_pad_link(&source->sd->entity, src_pad,
> +				    &dev->sd.entity, index,
> +				    MEDIA_LNK_FL_ENABLED |
> +				    MEDIA_LNK_FL_IMMUTABLE);
> +	if (ret) {
> +		dev_err(&dev->client->dev,
> +			"Unable to link %s:%u -> %s:%u\n",
> +			source->sd->name, src_pad, dev->sd.name, index);
> +		return ret;
> +	}
> +
> +	dev_dbg(&dev->client->dev, "Bound %s pad: %u on index %u\n",
> +		subdev->name, src_pad, index);
> +
> +	return 0;
> +}
> +
> +static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
> +				  struct v4l2_subdev *subdev,
> +				  struct v4l2_async_subdev *asd)
> +{
> +	struct max9286_source *source = asd_to_max9286_source(asd);
> +
> +	source->sd = NULL;
> +}
> +
> +static const struct v4l2_async_notifier_operations max9286_notify_ops = {
> +	.bound = max9286_notify_bound,
> +	.unbind = max9286_notify_unbind,
> +};
> +
> +/*
> + * max9286_check_video_links() - Make sure video links are detected and locked
> + *
> + * Performs safety checks on video link status. Make sure they are detected
> + * and all enabled links are locked.
> + *
> + * Returns 0 for success, -EIO for errors.
> + */
> +static int max9286_check_video_links(struct max9286_device *dev)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	/*
> +	 * Make sure valid video links are detected.
> +	 * The delay is not characterized in de-serializer manual, wait up
> +	 * to 5 ms.
> +	 */
> +	for (i = 0; i < 10; i++) {
> +		ret = max9286_read(dev, 0x49);
> +		if (ret < 0)
> +			return -EIO;
> +
> +		if ((ret & MAX9286_VIDEO_DETECT_MASK) == dev->source_mask)
> +			break;
> +
> +		usleep_range(350, 500);
> +	}
> +
> +	if (i == 10) {
> +		dev_err(&dev->client->dev,
> +			"Unable to detect video links: 0x%2x\n", ret);
> +		return -EIO;
> +	}
> +
> +	/* Make sure all enabled links are locked (4ms max). */
> +	for (i = 0; i < 10; i++) {
> +		ret = max9286_read(dev, 0x27);
> +		if (ret < 0)
> +			return -EIO;
> +
> +		if (ret & MAX9286_LOCKED)
> +			break;
> +
> +		usleep_range(350, 450);
> +	}
> +
> +	if (i == 10) {
> +		dev_err(&dev->client->dev, "Not all enabled links locked\n");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct max9286_device *dev = sd_to_max9286(sd);
> +	struct max9286_source *source;
> +	unsigned int i;
> +	bool sync = false;
> +	int ret;
> +
> +	if (enable) {
> +		/* FIXME: See note in max9286_i2c_mux_select() */
> +		dev->streaming = 1;
> +
> +		/* Start all cameras. */
> +		for_each_source(dev, source) {
> +			ret = v4l2_subdev_call(source->sd, video, s_stream, 1);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		ret = max9286_check_video_links(dev);
> +		if (ret)
> +			return ret;
> +
> +		/*
> +		 * Wait until frame synchronization is locked.
> +		 *
> +		 * Manual says frame sync locking should take ~6 VTS.
> +		 * From pratical experience at least 8 are required. Give
> +		 * 12 complete frames time (~33ms at 30 fps) to achieve frame
> +		 * locking before returning error.
> +		 */
> +		for (i = 0; i < 36; i++) {
> +			if (max9286_read(dev, 0x31) & MAX9286_FSYNC_LOCKED) {
> +				sync = true;
> +				break;
> +			}
> +			usleep_range(9000, 11000);
> +		}
> +
> +		if (!sync) {
> +			dev_err(&dev->client->dev,
> +				"Failed to get frame synchronization\n");
> +			return -EINVAL;
> +		}
> +
> +		/*
> +		 * Enable CSI output, VC set according to link number.
> +		 * Bit 7 must be set (chip manual says it's 0 and reserved).
> +		 */
> +		max9286_write(dev, 0x15, 0x80 | MAX9286_VCTYPE |
> +			      MAX9286_CSIOUTEN | MAX9286_0X15_RESV);
> +	} else {
> +		max9286_write(dev, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
> +
> +		/* Stop all cameras. */
> +		for_each_source(dev, source)
> +			v4l2_subdev_call(source->sd, video, s_stream, 0);
> +
> +		/* FIXME: See note in max9286_i2c_mux_select() */
> +		dev->streaming = 0;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max9286_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_UYVY8_2X8;
> +
> +	return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +max9286_get_pad_format(struct max9286_device *dev,
> +		       struct v4l2_subdev_pad_config *cfg,
> +		       unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&dev->sd, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &dev->fmt[pad];
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +static int max9286_set_fmt(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_pad_config *cfg,
> +			   struct v4l2_subdev_format *format)
> +{
> +	struct max9286_device *dev = sd_to_max9286(sd);
> +	struct v4l2_mbus_framefmt *cfg_fmt;
> +
> +	if (format->pad >= MAX9286_SRC_PAD)
> +		return -EINVAL;
> +
> +	/* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */
> +	switch (format->format.code) {
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +	case MEDIA_BUS_FMT_VYUY8_2X8:
> +	case MEDIA_BUS_FMT_YUYV8_2X8:
> +	case MEDIA_BUS_FMT_YVYU8_2X8:
> +		break;
> +	default:
> +		format->format.code = MEDIA_BUS_FMT_YUYV8_2X8;
> +		break;
> +	}
> +
> +	cfg_fmt = max9286_get_pad_format(dev, cfg, format->pad, format->which);
> +	if (!cfg_fmt)
> +		return -EINVAL;
> +
> +	*cfg_fmt = format->format;
> +
> +	return 0;
> +}
> +
> +static int max9286_get_fmt(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_pad_config *cfg,
> +			   struct v4l2_subdev_format *format)
> +{
> +	struct max9286_device *dev = sd_to_max9286(sd);
> +	struct v4l2_mbus_framefmt *cfg_fmt;
> +
> +	if (format->pad >= MAX9286_SRC_PAD)
> +		return -EINVAL;
> +
> +	cfg_fmt = max9286_get_pad_format(dev, cfg, format->pad, format->which);
> +	if (!cfg_fmt)
> +		return -EINVAL;
> +
> +	format->format = *cfg_fmt;
> +
> +	return 0;
> +}
> +
> +static int max9286_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
> +			   struct v4l2_mbus_frame_desc *fd)
> +{
> +	struct max9286_device *dev = sd_to_max9286(sd);
> +	struct max9286_source *source;
> +	unsigned int i = 0;
> +
> +	memset(fd, 0, sizeof(*fd));
> +
> +	for_each_source(dev, source) {
> +		unsigned int index = to_index(dev, source);
> +
> +		fd->entry[i].stream = i;
> +		fd->entry[i].bus.csi2.channel = index;
> +		fd->entry[i].bus.csi2.data_type = 0x1e;
> +		i++;
> +	}
> +
> +	fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
> +	fd->num_entries = dev->nsources;
> +
> +	return 0;
> +}
> +
> +static int max9286_get_routing(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_routing *routing)
> +{
> +	struct max9286_device *dev = sd_to_max9286(sd);
> +	struct v4l2_subdev_route *r = routing->routes;
> +	struct max9286_source *source;
> +
> +	/* There is one route per sink pad. */
> +	if (routing->num_routes < dev->nsources) {
> +		routing->num_routes = dev->nsources;
> +		return -ENOSPC;
> +	}
> +
> +	routing->num_routes = dev->nsources;
> +
> +	for_each_source(dev, source) {
> +		unsigned int index = to_index(dev, source);
> +
> +		r->sink_pad = index;
> +		r->sink_stream = 0;
> +		r->source_pad = MAX9286_SRC_PAD;
> +		r->source_stream = index;
> +		r->flags = dev->route_mask & BIT(index) ?
> +			V4L2_SUBDEV_ROUTE_FL_ACTIVE : 0;
> +		r++;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max9286_set_routing(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_routing *routing)
> +{
> +
> +	struct max9286_device *dev = sd_to_max9286(sd);
> +	struct v4l2_subdev_route *r = routing->routes;
> +	unsigned int i;
> +
> +	if (routing->num_routes > dev->nsources)
> +		return -ENOSPC;
> +
> +	for (i = 0; i < routing->num_routes; i++) {
> +		if (r->sink_pad >= MAX9286_SRC_PAD ||
> +		    r->sink_stream != 0 ||
> +		    r->source_pad != MAX9286_SRC_PAD ||
> +		    r->source_stream != r->sink_pad)
> +			return -EINVAL;
> +
> +		if (r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)
> +			dev->route_mask |= BIT(r->sink_pad);
> +		else
> +			dev->route_mask &= ~BIT(r->sink_pad);
> +
> +		r++;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops max9286_video_ops = {
> +	.s_stream	= max9286_s_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
> +	.enum_mbus_code = max9286_enum_mbus_code,
> +	.get_fmt	= max9286_get_fmt,
> +	.set_fmt	= max9286_set_fmt,
> +	.get_frame_desc = max9286_get_frame_desc,
> +	.get_routing	= max9286_get_routing,
> +	.set_routing	= max9286_set_routing,
> +};
> +
> +static const struct v4l2_subdev_ops max9286_subdev_ops = {
> +	.video		= &max9286_video_ops,
> +	.pad		= &max9286_pad_ops,
> +};
> +
> +static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
> +{
> +	fmt->width		= 1280;
> +	fmt->height		= 800;
> +	fmt->code		= MEDIA_BUS_FMT_UYVY8_2X8;
> +	fmt->colorspace		= V4L2_COLORSPACE_SRGB;
> +	fmt->field		= V4L2_FIELD_NONE;
> +	fmt->ycbcr_enc		= V4L2_YCBCR_ENC_DEFAULT;
> +	fmt->quantization	= V4L2_QUANTIZATION_DEFAULT;
> +	fmt->xfer_func		= V4L2_XFER_FUNC_DEFAULT;
> +}
> +
> +static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_mbus_framefmt *format;
> +	unsigned int i;
> +
> +	for (i = 0; i < MAX9286_N_SINKS; i++) {
> +		format = v4l2_subdev_get_try_format(subdev, fh->pad, i);
> +		max9286_init_format(format);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
> +	.open = max9286_open,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Probe/Remove
> + */
> +
> +static int max9286_setup(struct max9286_device *dev)
> +{
> +	/*
> +	 * Link ordering values for all enabled links combinations. Orders must
> +	 * be assigned sequentially from 0 to the number of enabled links
> +	 * without leaving any hole for disabled links. We thus assign orders to
> +	 * enabled links first, and use the remaining order values for disabled
> +	 * links are all links must have a different order value;
> +	 */
> +	static const u8 link_order[] = {
> +		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */
> +		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */
> +		(3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */
> +		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */
> +		(3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */
> +		(3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */
> +		(3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */
> +		(3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */
> +		(0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */
> +		(1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */
> +		(1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */
> +		(2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */
> +		(1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */
> +		(2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */
> +		(2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */
> +		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */
> +	};
> +
> +	/*
> +	 * Set the I2C bus speed.
> +	 *
> +	 * Enable I2C Local Acknowledge during the probe sequences of the camera
> +	 * only. This should be disabled after the mux is initialised.
> +	 */
> +	max9286_configure_i2c(dev, true);
> +
> +	/*
> +	 * Reverse channel setup.
> +	 *
> +	 * - 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.
> +	 */
> +	max9286_write(dev, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35));
> +	max9286_write(dev, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) |
> +		      MAX9286_REV_AMP_X);
> +
> +	/*
> +	 * Enable GMSL links, mask unused ones and autodetect link
> +	 * used as CSI clock source.
> +	 */
> +	max9286_write(dev, 0x00, MAX9286_MSTLINKSEL_AUTO | dev->route_mask);
> +	max9286_write(dev, 0x0b, link_order[dev->route_mask]);
> +	max9286_write(dev, 0x69, (0xf & ~dev->route_mask));
> +
> +	/*
> +	 * Video format setup:
> +	 * Disable CSI output, VC is set accordingly to Link number.
> +	 */
> +	max9286_write(dev, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
> +
> +	/* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */
> +	max9286_write(dev, 0x12, MAX9286_CSIDBL	| MAX9286_DBL |
> +		      MAX9286_CSILANECNT(dev->csi2_data_lanes) |
> +		      MAX9286_DATATYPE_YUV422_8BIT);
> +
> +	/* Automatic: FRAMESYNC taken from the slowest Link. */
> +	max9286_write(dev, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
> +		      MAX9286_FSYNCMETH_AUTO);
> +
> +	/* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */
> +	max9286_write(dev, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
> +		      MAX9286_HVSRC_D14);
> +
> +	/*
> +	 * Wait for 2ms to allow the link to resynchronize after the
> +	 * configuration change.
> +	 */
> +	usleep_range(2000, 5000);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id max9286_dt_ids[] = {
> +	{ .compatible = "maxim,max9286" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, max9286_dt_ids);
> +
> +static int max9286_init(struct device *dev, void *data)
> +{
> +	struct max9286_device *max9286;
> +	struct i2c_client *client;
> +	struct device_node *ep;
> +	unsigned int i;
> +	int ret;
> +
> +	/* Skip non-max9286 devices. */
> +	if (!dev->of_node || !of_match_node(max9286_dt_ids, dev->of_node))
> +		return 0;
> +
> +	client = to_i2c_client(dev);
> +	max9286 = i2c_get_clientdata(client);
> +
> +	/* Enable the bus power. */
> +	ret = regulator_enable(max9286->regulator);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "Unable to turn PoC on\n");
> +		return ret;
> +	}
> +
> +	max9286->poc_enabled = true;
> +
> +	ret = max9286_setup(max9286);
> +	if (ret) {
> +		dev_err(dev, "Unable to setup max9286\n");
> +		goto err_regulator;
> +	}
> +
> +	v4l2_i2c_subdev_init(&max9286->sd, client, &max9286_subdev_ops);
> +	max9286->sd.internal_ops = &max9286_subdev_internal_ops;
> +	max9286->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	v4l2_ctrl_handler_init(&max9286->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(&max9286->ctrls, NULL, V4L2_CID_PIXEL_RATE,
> +			  50000000, 50000000, 1, 50000000);
> +	max9286->sd.ctrl_handler = &max9286->ctrls;
> +	ret = max9286->ctrls.error;
> +	if (ret)
> +		goto err_regulator;
> +
> +	max9286->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
> +
> +	max9286->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE;
> +	for (i = 0; i < MAX9286_SRC_PAD; i++)
> +		max9286->pads[i].flags = MEDIA_PAD_FL_SINK;
> +	ret = media_entity_pads_init(&max9286->sd.entity, MAX9286_N_PADS,
> +				     max9286->pads);
> +	if (ret)
> +		goto err_regulator;
> +
> +	ep = of_graph_get_endpoint_by_regs(dev->of_node, MAX9286_SRC_PAD, -1);
> +	if (!ep) {
> +		dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n");
> +		ret = -ENOENT;
> +		goto err_regulator;
> +	}
> +	max9286->sd.fwnode = of_fwnode_handle(ep);
> +
> +	ret = v4l2_async_register_subdev(&max9286->sd);
> +	if (ret < 0) {
> +		dev_err(dev, "Unable to register subdevice\n");
> +		goto err_put_node;
> +	}
> +
> +	ret = max9286_i2c_mux_init(max9286);
> +	if (ret) {
> +		dev_err(dev, "Unable to initialize I2C multiplexer\n");
> +		goto err_subdev_unregister;
> +	}
> +
> +	/*
> +	 * Re-configure I2C with local acknowledge disabled after cameras
> +	 * have probed.
> +	 */
> +	max9286_configure_i2c(max9286, false);
> +
> +	/* Leave the mux channels disabled until they are selected. */
> +	max9286_i2c_mux_close(max9286);
> +
> +	return 0;
> +
> +err_subdev_unregister:
> +	v4l2_async_unregister_subdev(&max9286->sd);
> +	max9286_i2c_mux_close(max9286);
> +err_put_node:
> +	of_node_put(ep);
> +err_regulator:
> +	regulator_disable(max9286->regulator);
> +	max9286->poc_enabled = false;
> +
> +	return ret;
> +}
> +
> +static int max9286_is_bound(struct device *dev, void *data)
> +{
> +	struct device *this = data;
> +	int ret;
> +
> +	if (dev == this)
> +		return 0;
> +
> +	/* Skip non-max9286 devices. */
> +	if (!dev->of_node || !of_match_node(max9286_dt_ids, dev->of_node))
> +		return 0;
> +
> +	ret = device_is_bound(dev);
> +	if (!ret)
> +		return -EPROBE_DEFER;
> +
> +	return 0;
> +}
> +
> +static struct device_node *max9286_get_i2c_by_id(struct device_node *parent,
> +						 u32 id)
> +{
> +	struct device_node *child;
> +
> +	for_each_child_of_node(parent, child) {
> +		u32 i2c_id = 0;
> +
> +		if (of_node_cmp(child->name, "i2c") != 0)
> +			continue;
> +		of_property_read_u32(child, "reg", &i2c_id);
> +		if (id == i2c_id)
> +			return child;
> +	}
> +
> +	return NULL;
> +}
> +
> +static int max9286_check_i2c_bus_by_id(struct device *dev, int id)
> +{
> +	struct device_node *i2c_np;
> +
> +	i2c_np = max9286_get_i2c_by_id(dev->of_node, id);
> +	if (!i2c_np) {
> +		dev_err(dev, "Failed to find corresponding i2c@%u\n", id);
> +		return -ENODEV;
> +	}
> +
> +	if (!of_device_is_available(i2c_np)) {
> +		dev_dbg(dev, "Skipping port %u with disabled I2C bus\n", id);
> +		of_node_put(i2c_np);
> +		return -ENODEV;
> +	}
> +
> +	of_node_put(i2c_np);
> +
> +	return 0;
> +}
> +
> +static void max9286_cleanup_dt(struct max9286_device *max9286)
> +{
> +	struct max9286_source *source;
> +
> +	/*
> +	 * Not strictly part of the DT, but the notifier is registered during
> +	 * max9286_parse_dt(), and the notifier holds references to the fwnodes
> +	 * thus the cleanup is here to mirror the registration.
> +	 */
> +	v4l2_async_notifier_unregister(&max9286->notifier);
> +
> +	for_each_source(max9286, source) {
> +		fwnode_handle_put(source->fwnode);
> +		source->fwnode = NULL;
> +	}
> +}
> +
> +static int max9286_parse_dt(struct max9286_device *max9286)
> +{
> +	struct device *dev = &max9286->client->dev;
> +	struct device_node *ep_np = NULL;
> +	int ret;
> +
> +	v4l2_async_notifier_init(&max9286->notifier);
> +
> +	for_each_endpoint_of_node(dev->of_node, ep_np) {
> +		struct max9286_source *source;
> +		struct of_endpoint ep;
> +
> +		of_graph_parse_endpoint(ep_np, &ep);
> +		dev_dbg(dev, "Endpoint %pOF on port %d",
> +			ep.local_node, ep.port);
> +
> +		if (ep.port > MAX9286_NUM_GMSL) {
> +			dev_err(dev, "Invalid endpoint %s on port %d",
> +				of_node_full_name(ep.local_node),
> +				ep.port);
> +
> +			continue;
> +		}
> +
> +		/* For the source endpoint just parse the bus configuration. */
> +		if (ep.port == MAX9286_SRC_PAD) {
> +			struct v4l2_fwnode_endpoint vep;
> +			int ret;
> +
> +			ret = v4l2_fwnode_endpoint_alloc_parse(
> +					of_fwnode_handle(ep_np), &vep);
> +			if (ret)
> +				return ret;
> +
> +			if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) {
> +				dev_err(dev,
> +					"Media bus %u type not supported\n",
> +					vep.bus_type);
> +				v4l2_fwnode_endpoint_free(&vep);
> +				return -EINVAL;
> +			}
> +
> +			max9286->csi2_data_lanes =
> +				vep.bus.mipi_csi2.num_data_lanes;
> +			v4l2_fwnode_endpoint_free(&vep);
> +
> +			continue;
> +		}
> +
> +		/* Skip if the corresponding GMSL link is unavailable. */
> +		if (max9286_check_i2c_bus_by_id(dev, ep.port))
> +			continue;
> +
> +		if (max9286->sources[ep.port].fwnode) {
> +			dev_err(dev,
> +				"Multiple port endpoints are not supported: %d",
> +				ep.port);
> +
> +			continue;
> +		}
> +
> +		source = &max9286->sources[ep.port];
> +		source->fwnode = fwnode_graph_get_remote_endpoint(
> +						of_fwnode_handle(ep_np));
> +		if (!source->fwnode) {
> +			dev_err(dev,
> +				"Endpoint %pOF has no remote endpoint connection\n",
> +				ep.local_node);
> +
> +			continue;
> +		}
> +
> +		source->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
> +		source->asd.match.fwnode = source->fwnode;
> +
> +		ret = v4l2_async_notifier_add_subdev(&max9286->notifier,
> +						     &source->asd);
> +		if (ret) /* TODO: Cleanup notifier! */
> +			return ret;
> +
> +		max9286->source_mask |= BIT(ep.port);
> +		max9286->nsources++;
> +	}
> +
> +	/* Do not register the subdev notifier if there are no devices. */
> +	if (!max9286->nsources)
> +		return 0;
> +
> +	max9286->route_mask = max9286->source_mask;
> +	max9286->notifier.ops = &max9286_notify_ops;
> +
> +	return v4l2_async_subdev_notifier_register(&max9286->sd,
> +						   &max9286->notifier);
> +}
> +
> +static int max9286_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *did)
> +{
> +	struct max9286_device *dev;
> +	unsigned int i;
> +	int ret;
> +
> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	dev->client = client;
> +	i2c_set_clientdata(client, dev);
> +
> +	for (i = 0; i < MAX9286_N_SINKS; i++)
> +		max9286_init_format(&dev->fmt[i]);
> +
> +	ret = max9286_parse_dt(dev);
> +	if (ret)
> +		return ret;
> +
> +	dev->regulator = regulator_get(&client->dev, "poc");
> +	if (IS_ERR(dev->regulator)) {
> +		if (PTR_ERR(dev->regulator) != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Unable to get PoC regulator (%ld)\n",
> +				PTR_ERR(dev->regulator));
> +		ret = PTR_ERR(dev->regulator);
> +		goto err_free;
> +	}
> +
> +	/*
> +	 * We can have multiple MAX9286 instances on the same physical I2C
> +	 * bus, and I2C children behind ports of separate MAX9286 instances
> +	 * having the same I2C address. As the MAX9286 starts by default with
> +	 * all ports enabled, we need to disable all ports on all MAX9286
> +	 * instances before proceeding to further initialize the devices and
> +	 * instantiate children.
> +	 *
> +	 * Start by just disabling all channels on the current device. Then,
> +	 * if all other MAX9286 on the parent bus have been probed, proceed
> +	 * to initialize them all, including the current one.
> +	 */
> +	max9286_i2c_mux_close(dev);
> +
> +	/*
> +	 * The MAX9286 initialises with auto-acknowledge enabled by default.
> +	 * This means that if multiple MAX9286 devices are connected to an I2C
> +	 * bus, another MAX9286 could ack I2C transfers meant for a device on
> +	 * the other side of the GMSL links for this MAX9286 (such as a
> +	 * MAX9271). To prevent that disable auto-acknowledge early on; it
> +	 * will be enabled later as needed.
> +	 */
> +	max9286_configure_i2c(dev, false);
> +
> +	ret = device_for_each_child(client->dev.parent, &client->dev,
> +				    max9286_is_bound);
> +	if (ret)
> +		return 0;
> +
> +	dev_dbg(&client->dev,
> +		"All max9286 probed: start initialization sequence\n");
> +	ret = device_for_each_child(client->dev.parent, NULL,
> +				    max9286_init);
> +	if (ret < 0)
> +		goto err_regulator;
> +
> +	/* Leave the mux channels disabled until they are selected. */
> +	max9286_i2c_mux_close(dev);
> +
> +	return 0;
> +
> +err_regulator:
> +	regulator_put(dev->regulator);
> +	max9286_i2c_mux_close(dev);
> +	max9286_configure_i2c(dev, false);
> +err_free:
> +	max9286_cleanup_dt(dev);
> +	kfree(dev);
> +
> +	return ret;
> +}
> +
> +static int max9286_remove(struct i2c_client *client)
> +{
> +	struct max9286_device *dev = i2c_get_clientdata(client);
> +
> +	i2c_mux_del_adapters(dev->mux);
> +
> +	fwnode_handle_put(dev->sd.fwnode);
> +	v4l2_async_unregister_subdev(&dev->sd);
> +
> +	if (dev->poc_enabled)
> +		regulator_disable(dev->regulator);
> +	regulator_put(dev->regulator);
> +
> +	max9286_cleanup_dt(dev);
> +	kfree(dev);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id max9286_id[] = {
> +	{ "max9286", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, max9286_id);
> +
> +static struct i2c_driver max9286_i2c_driver = {
> +	.driver	= {
> +		.name		= "max9286",
> +		.of_match_table	= of_match_ptr(max9286_dt_ids),
> +	},
> +	.probe		= max9286_probe,
> +	.remove		= max9286_remove,
> +	.id_table	= max9286_id,
> +};
> +
> +module_i2c_driver(max9286_i2c_driver);
> +
> +MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver");
> +MODULE_AUTHOR("Vladimir Barinov");
> +MODULE_LICENSE("GPL");
> 


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

* Re: [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-07 15:06   ` Kieran Bingham
@ 2018-11-07 17:24     ` Luca Ceresoli
  2018-11-08 10:11       ` jacopo mondi
  0 siblings, 1 reply; 26+ messages in thread
From: Luca Ceresoli @ 2018-11-07 17:24 UTC (permalink / raw)
  To: kieran.bingham+renesas, Kieran Bingham, linux-renesas-soc, linux-media
  Cc: devicetree, sakari.ailus, Niklas Söderlund, Jacopo Mondi,
	Laurent Pinchart, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Kieran,

thanks for the clarification. One additional note below.

On 07/11/18 16:06, Kieran Bingham wrote:
> Hi Luca
> 
> <Top posting for new topic>
> 
>> <lucaceresoli> kbingham: hi, I'm looking at your GMSL v4 patches
>> <lucaceresoli> kbingham: jmondi helped me in understanding parts of it, but I still have a question
>> <lucaceresoli> kbingham: are the drivers waiting for the established link before the remote chips are accessed? where?
> 
> I'm replying here rather than spam the IRC channel with a big paste.
> It's also a useful description to the probe sequence, so I've kept it
> with the driver posting.
> 
> I hope the following helps illustrate the sequences which are involved:
> 
> max9286_probe()
>  - max9286_i2c_mux_close() # Disable all links
>  - max9286_configure_i2c # Configure early communication settings
>  - max9286_init():
>    - regulator_enable() # Power up all cameras
>    - max9286_setup() # Most link setup is done here.
>    ... Set up v4l2/async/media-controller endpoints
>    - max9286_i2c_mux_init() # Start configuring cameras:
>      - i2c_mux_alloc() # Create our mux device
>      - for_each_source(dev, source)
>            i2c_mux_add_adapter() # This is where sensors get probed.
> 
> So yes sensors are only communicated with once the link is brought up as
> much as possible.

For the records, an additional bit of explanation I got from Kieran via IRC.

The fact that link is already up when the sensors are probed is due to
the fact that the power regulator has a delay of *8 seconds*. This is
intended, because there's an MCU on the camera modules that talks on the
I2C bus during that time, and thus the drivers need to wait after it's done.

This delay happens before max9286_setup() is called.

> Because the sensors are i2c devices on the i2c_mux - they are not probed
> until their adapters are created and added.
> 
> At this stage the i2c-mux core framework will iterate all the devices
> described by the DT for that adapter.
> 
> As each one is probed - the i2c_mux framework will call
> max9286_i2c_mux_select() and enable only the single link.
> 
> This allows us to configure each camera independently
> 
> (which is essential because they are all configured to the same i2c
> address by default at power on)
> 
> 
> Hope this helps, and feel free to ask if you have any more questions.
-- 
Luca

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

* Re: [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-07 17:24     ` Luca Ceresoli
@ 2018-11-08 10:11       ` jacopo mondi
  2018-11-08 11:24         ` Luca Ceresoli
  0 siblings, 1 reply; 26+ messages in thread
From: jacopo mondi @ 2018-11-08 10:11 UTC (permalink / raw)
  To: Luca Ceresoli
  Cc: kieran.bingham+renesas, Kieran Bingham, linux-renesas-soc,
	linux-media, devicetree, sakari.ailus, Niklas Söderlund,
	Laurent Pinchart, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

[-- Attachment #1: Type: text/plain, Size: 5783 bytes --]

Hi Luca, Kieran,
    sorry to jump up, but I feel this should be clarified.

On Wed, Nov 07, 2018 at 06:24:18PM +0100, Luca Ceresoli wrote:
> Hi Kieran,
>
> thanks for the clarification. One additional note below.
>
> On 07/11/18 16:06, Kieran Bingham wrote:
> > Hi Luca
> >
> > <Top posting for new topic>
> >
> >> <lucaceresoli> kbingham: hi, I'm looking at your GMSL v4 patches
> >> <lucaceresoli> kbingham: jmondi helped me in understanding parts of it, but I still have a question
> >> <lucaceresoli> kbingham: are the drivers waiting for the established link before the remote chips are accessed? where?
> >
> > I'm replying here rather than spam the IRC channel with a big paste.
> > It's also a useful description to the probe sequence, so I've kept it
> > with the driver posting.
> >
> > I hope the following helps illustrate the sequences which are involved:
> >
> > max9286_probe()
> >  - max9286_i2c_mux_close() # Disable all links
> >  - max9286_configure_i2c # Configure early communication settings
> >  - max9286_init():
> >    - regulator_enable() # Power up all cameras
> >    - max9286_setup() # Most link setup is done here.
> >    ... Set up v4l2/async/media-controller endpoints
> >    - max9286_i2c_mux_init() # Start configuring cameras:
> >      - i2c_mux_alloc() # Create our mux device
> >      - for_each_source(dev, source)
> >            i2c_mux_add_adapter() # This is where sensors get probed.
> >
> > So yes sensors are only communicated with once the link is brought up as
> > much as possible.
>
> For the records, an additional bit of explanation I got from Kieran via IRC.
>
> The fact that link is already up when the sensors are probed is due to
> the fact that the power regulator has a delay of *8 seconds*. This is
> intended, because there's an MCU on the camera modules that talks on the
> I2C bus during that time, and thus the drivers need to wait after it's done.

The 8sec delay is due to the fact an integrated MCU on the remote
camera module programs the local sensor and the serializer intgrated
in the module in to some default configuration state. At power up, we
just want to let it finish, with all reverse channels closed
(camera module -> SoC direction) not to have the MCU transmitted
messages repeated to the local side (our remote serializer does repeat
messages not directed to it on it's remote side, as our local
deserialier does).

The "link up" thing is fairly more complicated for GMSL than just
having a binary "on" or "off" mode. This technology defines two different
"channels", a 'configuration-channel' for transmitting control messages
on the serial link (i2c messages for the deserializer/serializer pair
this patches support) and a 'video-channel' for transmission of
high-speed data, such as, no surprise, video and images :)

GMSL also defines two "link modes": a clock-less "configuration link"
and an high-speed "video link". The "configuration link" is available a
few msec after power up (roughly), while the "video link" needs a pixel
clock to be supplied to the serializer for it to enter this mode and
be able to lock the status between itself and the deserializer. Then it can
begin serializing video data.

The 'control channel' is available both when the link is in
'configuration' and 'video' mode, while the 'video' channel is
available only when the link is in 'video' mode (or, to put it more
simply: you can send i2c configuration messages while the link is
serializing video).

Our implementation uses the link in 'configuration mode' during the
remote side programming phase, at 'max9286_i2c_mux_init()' time, with
the 'max9286_i2c_mux_select()' function enabling selectively the
'configuration link' of each single remote end. It probes the remote device
by instantiating a new i2c_adapter connected to the mux, one for each
remote end, and performs the device configuration by initially using its
default power up i2c address (it is safe to do so, all other links are
closed), then changes the remote devices address to an unique one
(as our devices allows us to do so, otherwise you should use the
deserializer address translation feature to mask and translate the
remote addresses).

Now all remote devices have an unique i2c address, and we can operate
with all 'configuration links' open with no risk of i2c addresses
collisions.

At this point when we want to start the video stream, we send a
control message to the remote device, which enables the pixel clock
output from the image sensor, and activate the 'video channel' on the
remote serializer. The local deserializer makes sure all 'video links'
are locked (see 'max9286_check_video_links()') and at this point we
can begin serializing/deserializing video data.

As you can see, the initial delay only plays a role in avoiding
collision before we properly configure the channels and the i2c
addresses. The link setup phase is instead an integral part of the
system configuration, and there are no un-necessary delays used to
work around it setup procedure.

Does this help clarifying the system startup procedure?

Thanks
   j
>
> This delay happens before max9286_setup() is called.
>
> > Because the sensors are i2c devices on the i2c_mux - they are not probed
> > until their adapters are created and added.
> >
> > At this stage the i2c-mux core framework will iterate all the devices
> > described by the DT for that adapter.
> >
> > As each one is probed - the i2c_mux framework will call
> > max9286_i2c_mux_select() and enable only the single link.
> >
> > This allows us to configure each camera independently
> >
> > (which is essential because they are all configured to the same i2c
> > address by default at power on)
> >
> >
> > Hope this helps, and feel free to ask if you have any more questions.
> --
> Luca

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-08 10:11       ` jacopo mondi
@ 2018-11-08 11:24         ` Luca Ceresoli
  0 siblings, 0 replies; 26+ messages in thread
From: Luca Ceresoli @ 2018-11-08 11:24 UTC (permalink / raw)
  To: jacopo mondi
  Cc: kieran.bingham+renesas, Kieran Bingham, linux-renesas-soc,
	linux-media, devicetree, sakari.ailus, Niklas Söderlund,
	Laurent Pinchart, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Jacopo,

On 08/11/18 11:11, jacopo mondi wrote:
> Hi Luca, Kieran,
>     sorry to jump up, but I feel this should be clarified.
> 
> On Wed, Nov 07, 2018 at 06:24:18PM +0100, Luca Ceresoli wrote:
>> Hi Kieran,
>>
>> thanks for the clarification. One additional note below.
>>
>> On 07/11/18 16:06, Kieran Bingham wrote:
>>> Hi Luca
>>>
>>> <Top posting for new topic>
>>>
>>>> <lucaceresoli> kbingham: hi, I'm looking at your GMSL v4 patches
>>>> <lucaceresoli> kbingham: jmondi helped me in understanding parts of it, but I still have a question
>>>> <lucaceresoli> kbingham: are the drivers waiting for the established link before the remote chips are accessed? where?
>>>
>>> I'm replying here rather than spam the IRC channel with a big paste.
>>> It's also a useful description to the probe sequence, so I've kept it
>>> with the driver posting.
>>>
>>> I hope the following helps illustrate the sequences which are involved:
>>>
>>> max9286_probe()
>>>  - max9286_i2c_mux_close() # Disable all links
>>>  - max9286_configure_i2c # Configure early communication settings
>>>  - max9286_init():
>>>    - regulator_enable() # Power up all cameras
>>>    - max9286_setup() # Most link setup is done here.
>>>    ... Set up v4l2/async/media-controller endpoints
>>>    - max9286_i2c_mux_init() # Start configuring cameras:
>>>      - i2c_mux_alloc() # Create our mux device
>>>      - for_each_source(dev, source)
>>>            i2c_mux_add_adapter() # This is where sensors get probed.
>>>
>>> So yes sensors are only communicated with once the link is brought up as
>>> much as possible.
>>
>> For the records, an additional bit of explanation I got from Kieran via IRC.
>>
>> The fact that link is already up when the sensors are probed is due to
>> the fact that the power regulator has a delay of *8 seconds*. This is
>> intended, because there's an MCU on the camera modules that talks on the
>> I2C bus during that time, and thus the drivers need to wait after it's done.
> 
> The 8sec delay is due to the fact an integrated MCU on the remote
> camera module programs the local sensor and the serializer intgrated
> in the module in to some default configuration state. At power up, we
> just want to let it finish, with all reverse channels closed
> (camera module -> SoC direction) not to have the MCU transmitted
> messages repeated to the local side (our remote serializer does repeat
> messages not directed to it on it's remote side, as our local
> deserialier does).
> 
> The "link up" thing is fairly more complicated for GMSL than just
> having a binary "on" or "off" mode. This technology defines two different
> "channels", a 'configuration-channel' for transmitting control messages
> on the serial link (i2c messages for the deserializer/serializer pair
> this patches support) and a 'video-channel' for transmission of
> high-speed data, such as, no surprise, video and images :)
> 
> GMSL also defines two "link modes": a clock-less "configuration link"
> and an high-speed "video link". The "configuration link" is available a
> few msec after power up (roughly), while the "video link" needs a pixel
> clock to be supplied to the serializer for it to enter this mode and
> be able to lock the status between itself and the deserializer. Then it can
> begin serializing video data.
> 
> The 'control channel' is available both when the link is in
> 'configuration' and 'video' mode, while the 'video' channel is
> available only when the link is in 'video' mode (or, to put it more
> simply: you can send i2c configuration messages while the link is
> serializing video).
> 
> Our implementation uses the link in 'configuration mode' during the
> remote side programming phase, at 'max9286_i2c_mux_init()' time, with
> the 'max9286_i2c_mux_select()' function enabling selectively the
> 'configuration link' of each single remote end. It probes the remote device
> by instantiating a new i2c_adapter connected to the mux, one for each
> remote end, and performs the device configuration by initially using its
> default power up i2c address (it is safe to do so, all other links are
> closed), then changes the remote devices address to an unique one
> (as our devices allows us to do so, otherwise you should use the
> deserializer address translation feature to mask and translate the
> remote addresses).
> 
> Now all remote devices have an unique i2c address, and we can operate
> with all 'configuration links' open with no risk of i2c addresses
> collisions.
> 
> At this point when we want to start the video stream, we send a
> control message to the remote device, which enables the pixel clock
> output from the image sensor, and activate the 'video channel' on the
> remote serializer. The local deserializer makes sure all 'video links'
> are locked (see 'max9286_check_video_links()') and at this point we
> can begin serializing/deserializing video data.
> 
> As you can see, the initial delay only plays a role in avoiding
> collision before we properly configure the channels and the i2c
> addresses. The link setup phase is instead an integral part of the
> system configuration, and there are no un-necessary delays used to
> work around it setup procedure.
> 
> Does this help clarifying the system startup procedure?

Yes, that's very informative, thank you very much.

Given the complexity of the driver and the non-obviousness of some
workarounds to "unfortunate hardware design choices", I think [some of]
this explanation should be committed together with the driver, in order
to make it more understandable to other people. Even more since you've
already taken time to write it.

Thanks,
-- 
Luca

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

* Re: [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286
  2018-11-02 15:47 ` [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286 Kieran Bingham
  2018-11-05 20:02   ` Rob Herring
@ 2018-11-13 22:42   ` Luca Ceresoli
  2018-11-13 23:12     ` Kieran Bingham
  1 sibling, 1 reply; 26+ messages in thread
From: Luca Ceresoli @ 2018-11-13 22:42 UTC (permalink / raw)
  To: Kieran Bingham, linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Laurent Pinchart, Jacopo Mondi

Hi Kieran, All,

sorry for joining this late... See below my considerations.

On 02/11/18 16:47, Kieran Bingham wrote:
> From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> 
> The MAX9286 deserializes video data received on up to 4 Gigabit
> Multimedia Serial Links (GMSL) and outputs them on a CSI-2 port using up
> to 4 data lanes.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> 
> ---
> v3:
>  - Update binding descriptions
> 
> v4:
>  - Define the use of a CSI2 D-PHY
>  - Rename pwdn-gpios to enable-gpios (with inverted polarity)
>  - Remove clock-lanes mapping support
>  - rewrap text blocks
>  - Fix typos
> ---
>  .../bindings/media/i2c/maxim,max9286.txt      | 182 ++++++++++++++++++
>  1 file changed, 182 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
> new file mode 100644
> index 000000000000..672f6a4d417d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
> @@ -0,0 +1,182 @@
> +Maxim Integrated Quad GMSL Deserializer
> +---------------------------------------
> +
> +The MAX9286 deserializer receives video data on up to 4 Gigabit Multimedia
> +Serial Links (GMSL) and outputs them on a CSI-2 D-PHY port using up to 4 data
> +lanes.
> +
> +In addition to video data, the GMSL links carry a bidirectional control channel
> +that encapsulates I2C messages. The MAX9286 forwards all I2C traffic not
> +addressed to itself to the other side of the links, where a GMSL serializer
> +will output it on a local I2C bus. In the other direction all I2C traffic
> +received over GMSL by the MAX9286 is output on the local I2C bus.
> +
> +Required Properties:
> +
> +- compatible: Shall be "maxim,max9286"
> +- reg: I2C device address
> +
> +Optional Properties:
> +
> +- poc-supply: Regulator providing Power over Coax to the cameras
> +- enable-gpios: GPIO connected to the #PWDN pin with inverted polarity
> +
> +Required endpoint nodes:
> +-----------------------
> +
> +The connections to the MAX9286 GMSL and its endpoint nodes are modeled using
> +the OF graph bindings in accordance with the video interface bindings defined
> +in Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +The following table lists the port number corresponding to each device port.
> +
> +        Port            Description
> +        ----------------------------------------
> +        Port 0          GMSL Input 0
> +        Port 1          GMSL Input 1
> +        Port 2          GMSL Input 2
> +        Port 3          GMSL Input 3
> +        Port 4          CSI-2 Output
> +
> +Optional Endpoint Properties for GMSL Input Ports (Port [0-3]):
> +
> +- remote-endpoint: phandle to the remote GMSL source endpoint subnode in the
> +  remote node port.
> +
> +Required Endpoint Properties for CSI-2 Output Port (Port 4):
> +
> +- remote-endpoint: phandle to the remote CSI-2 sink endpoint node.
> +- data-lanes: array of physical CSI-2 data lane indexes.
> +
> +Required i2c-mux nodes:
> +----------------------
> +
> +Each GMSL link is modeled as a child bus of an i2c bus multiplexer/switch, in
> +accordance with bindings described in
> +Documentation/devicetree/bindings/i2c/i2c-mux.txt. The serializer device on the
> +remote end of the GMSL link shall be modelled as a child node of the
> +corresponding I2C bus.
> +
> +Required i2c child bus properties:
> +- all properties described as required i2c child bus nodes properties in
> +  Documentation/devicetree/bindings/i2c/i2c-mux.txt.
> +
> +Example:
> +-------
> +
> +	gmsl-deserializer@2c {
> +		compatible = "maxim,max9286";
> +		reg = <0x2c>;
> +		poc-supply = <&camera_poc_12v>;
> +		enable-gpios = <&gpio 13 GPIO_ACTIVE_LOW>;
> +
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			port@0 {
> +				reg = <0>;
> +				max9286_in0: endpoint {
> +					remote-endpoint = <&rdacm20_out0>;
> +				};
> +			};
> +
> +			port@1 {
> +				reg = <1>;
> +				max9286_in1: endpoint {
> +					remote-endpoint = <&rdacm20_out1>;
> +				};
> +			};
> +
> +			port@2 {
> +				reg = <2>;
> +				max9286_in2: endpoint {
> +					remote-endpoint = <&rdacm20_out2>;
> +				};
> +			};
> +
> +			port@3 {
> +				reg = <3>;
> +				max9286_in3: endpoint {
> +					remote-endpoint = <&rdacm20_out3>;
> +				};
> +			};
> +
> +			port@4 {
> +				reg = <4>;
> +				max9286_out: endpoint {
> +					data-lanes = <1 2 3 4>;
> +					remote-endpoint = <&csi40_in>;
> +				};
> +			};
> +		};
> +
> +		i2c@0 {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			reg = <0>;
> +
> +			camera@51 {
> +				compatible = "imi,rdacm20";
> +				reg = <0x51 0x61>;

I find this kind of address mapping is the weak point in this patchset.

The ser-deser chipset splits the world in "local" and "remote" side. The
camera node belongs to the remote side, but the 0x51 and 0x61 addresses
belong to the local side. Think about supporting N different main boards
and M remote boards. 0x51 might be available on some main boards but not
all. IMO under the camera@51 (even the i2c@0) node there should be only
remote hardware description. To support the N*M possible combinations,
there should be:

 * a DT for the main board mentioning only addresses for the
   local i2c bus, down to the i2c@0 with address-cells, size-cells and
   reg properties
 * a DT overlay for each remote board, mentioning the remote i2c
   chips with their physical addresses, but no local addresses

The only way I could devise to be generic is to bind each physical
remote address to a local address at runtime.

Also, to be implemented reliably, an address translation feature is
required on the local (de)ser chip.

So the question is: can the max9286 chip do i2c address translation?

Thanks,
-- 
Luca

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

* Re: [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-02 15:47 ` [PATCH v4 3/4] media: i2c: Add MAX9286 driver Kieran Bingham
  2018-11-07 15:06   ` Kieran Bingham
@ 2018-11-13 22:49   ` Luca Ceresoli
  2018-11-14  0:46     ` Kieran Bingham
  1 sibling, 1 reply; 26+ messages in thread
From: Luca Ceresoli @ 2018-11-13 22:49 UTC (permalink / raw)
  To: Kieran Bingham, linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Kieran, All,

below a few minor questions, and a big one at the bottom.

On 02/11/18 16:47, Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> 
> The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and
> CSI-2 output. The device supports multicamera streaming applications,
> and features the ability to synchronise the attached cameras.
> 
> CSI-2 output can be configured with 1 to 4 lanes, and a control channel
> is supported over I2C, which implements an I2C mux to facilitate
> communications with connected cameras across the reverse control
> channel.
> 
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

[...]

> +struct max9286_device {
> +	struct i2c_client *client;
> +	struct v4l2_subdev sd;
> +	struct media_pad pads[MAX9286_N_PADS];
> +	struct regulator *regulator;
> +	bool poc_enabled;
> +	int streaming;
> +
> +	struct i2c_mux_core *mux;
> +	unsigned int mux_channel;
> +
> +	struct v4l2_ctrl_handler ctrls;
> +
> +	struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];

5 pads, 4 formats. Why does the source node have no fmt?

> +static int max9286_init(struct device *dev, void *data)
> +{
> +	struct max9286_device *max9286;
> +	struct i2c_client *client;
> +	struct device_node *ep;
> +	unsigned int i;
> +	int ret;
> +
> +	/* Skip non-max9286 devices. */
> +	if (!dev->of_node || !of_match_node(max9286_dt_ids, dev->of_node))
> +		return 0;
> +
> +	client = to_i2c_client(dev);
> +	max9286 = i2c_get_clientdata(client);
> +
> +	/* Enable the bus power. */
> +	ret = regulator_enable(max9286->regulator);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "Unable to turn PoC on\n");
> +		return ret;
> +	}
> +
> +	max9286->poc_enabled = true;
> +
> +	ret = max9286_setup(max9286);
> +	if (ret) {
> +		dev_err(dev, "Unable to setup max9286\n");
> +		goto err_regulator;
> +	}
> +
> +	v4l2_i2c_subdev_init(&max9286->sd, client, &max9286_subdev_ops);
> +	max9286->sd.internal_ops = &max9286_subdev_internal_ops;
> +	max9286->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
                          ^

This way you're clearing the V4L2_SUBDEV_FL_IS_I2C set by
v4l2_i2c_subdev_init(), even though using devicetree I think this won't
matter in the current kernel code. However I think "max9286->sd.flags |=
..." is more correct here, and it's also what most other drivers do.

> +	v4l2_ctrl_handler_init(&max9286->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(&max9286->ctrls, NULL, V4L2_CID_PIXEL_RATE,
> +			  50000000, 50000000, 1, 50000000);
> +	max9286->sd.ctrl_handler = &max9286->ctrls;
> +	ret = max9286->ctrls.error;
> +	if (ret)
> +		goto err_regulator;
> +
> +	max9286->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;

According to the docs MEDIA_ENT_F_VID_IF_BRIDGE appears more fitting.

> +static int max9286_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *did)
> +{
> +	struct max9286_device *dev;
> +	unsigned int i;
> +	int ret;
> +
> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	dev->client = client;
> +	i2c_set_clientdata(client, dev);
> +
> +	for (i = 0; i < MAX9286_N_SINKS; i++)
> +		max9286_init_format(&dev->fmt[i]);
> +
> +	ret = max9286_parse_dt(dev);
> +	if (ret)
> +		return ret;
> +
> +	dev->regulator = regulator_get(&client->dev, "poc");
> +	if (IS_ERR(dev->regulator)) {
> +		if (PTR_ERR(dev->regulator) != -EPROBE_DEFER)
> +			dev_err(&client->dev,
> +				"Unable to get PoC regulator (%ld)\n",
> +				PTR_ERR(dev->regulator));
> +		ret = PTR_ERR(dev->regulator);
> +		goto err_free;
> +	}
> +
> +	/*
> +	 * We can have multiple MAX9286 instances on the same physical I2C
> +	 * bus, and I2C children behind ports of separate MAX9286 instances
> +	 * having the same I2C address. As the MAX9286 starts by default with
> +	 * all ports enabled, we need to disable all ports on all MAX9286
> +	 * instances before proceeding to further initialize the devices and
> +	 * instantiate children.
> +	 *
> +	 * Start by just disabling all channels on the current device. Then,
> +	 * if all other MAX9286 on the parent bus have been probed, proceed
> +	 * to initialize them all, including the current one.
> +	 */
> +	max9286_i2c_mux_close(dev);
> +
> +	/*
> +	 * The MAX9286 initialises with auto-acknowledge enabled by default.
> +	 * This means that if multiple MAX9286 devices are connected to an I2C
> +	 * bus, another MAX9286 could ack I2C transfers meant for a device on
> +	 * the other side of the GMSL links for this MAX9286 (such as a
> +	 * MAX9271). To prevent that disable auto-acknowledge early on; it
> +	 * will be enabled later as needed.
> +	 */
> +	max9286_configure_i2c(dev, false);
> +
> +	ret = device_for_each_child(client->dev.parent, &client->dev,
> +				    max9286_is_bound);
> +	if (ret)
> +		return 0;
> +
> +	dev_dbg(&client->dev,
> +		"All max9286 probed: start initialization sequence\n");
> +	ret = device_for_each_child(client->dev.parent, NULL,
> +				    max9286_init);

I can't manage to like this initialization sequence, sorry. If at all
possible, each max9286 should initialize itself independently from each
other, like any normal driver.

First, it requires that each chip on the remote side can configure its
own slave address. Not all chips do.

Second, using a static i2c address map does not scale well and limits
hotplugging, as I discussed in my reply to patch 1/4. The problem should
be solvable cleanly if the MAX9286 supports address translation like the
TI chips.

Thanks,
-- 
Luca


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

* Re: [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286
  2018-11-13 22:42   ` Luca Ceresoli
@ 2018-11-13 23:12     ` Kieran Bingham
  2018-11-14 10:04       ` Luca Ceresoli
  0 siblings, 1 reply; 26+ messages in thread
From: Kieran Bingham @ 2018-11-13 23:12 UTC (permalink / raw)
  To: Luca Ceresoli, linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Laurent Pinchart, Jacopo Mondi

Hi Luca,

On 13/11/2018 14:42, Luca Ceresoli wrote:
> Hi Kieran, All,
> 
> sorry for joining this late... See below my considerations.

I'd say you're on time - not late,

Thanks for joining :)

> 
> On 02/11/18 16:47, Kieran Bingham wrote:
>> From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>
>> The MAX9286 deserializes video data received on up to 4 Gigabit
>> Multimedia Serial Links (GMSL) and outputs them on a CSI-2 port using up
>> to 4 data lanes.
>>
>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>
>> ---
>> v3:
>>  - Update binding descriptions
>>
>> v4:
>>  - Define the use of a CSI2 D-PHY
>>  - Rename pwdn-gpios to enable-gpios (with inverted polarity)
>>  - Remove clock-lanes mapping support
>>  - rewrap text blocks
>>  - Fix typos
>> ---
>>  .../bindings/media/i2c/maxim,max9286.txt      | 182 ++++++++++++++++++
>>  1 file changed, 182 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>>
>> diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>> new file mode 100644
>> index 000000000000..672f6a4d417d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>> @@ -0,0 +1,182 @@
>> +Maxim Integrated Quad GMSL Deserializer
>> +---------------------------------------
>> +
>> +The MAX9286 deserializer receives video data on up to 4 Gigabit Multimedia
>> +Serial Links (GMSL) and outputs them on a CSI-2 D-PHY port using up to 4 data
>> +lanes.
>> +
>> +In addition to video data, the GMSL links carry a bidirectional control channel
>> +that encapsulates I2C messages. The MAX9286 forwards all I2C traffic not
>> +addressed to itself to the other side of the links, where a GMSL serializer
>> +will output it on a local I2C bus. In the other direction all I2C traffic
>> +received over GMSL by the MAX9286 is output on the local I2C bus.
>> +
>> +Required Properties:
>> +
>> +- compatible: Shall be "maxim,max9286"
>> +- reg: I2C device address
>> +
>> +Optional Properties:
>> +
>> +- poc-supply: Regulator providing Power over Coax to the cameras
>> +- enable-gpios: GPIO connected to the #PWDN pin with inverted polarity
>> +
>> +Required endpoint nodes:
>> +-----------------------
>> +
>> +The connections to the MAX9286 GMSL and its endpoint nodes are modeled using
>> +the OF graph bindings in accordance with the video interface bindings defined
>> +in Documentation/devicetree/bindings/media/video-interfaces.txt.
>> +
>> +The following table lists the port number corresponding to each device port.
>> +
>> +        Port            Description
>> +        ----------------------------------------
>> +        Port 0          GMSL Input 0
>> +        Port 1          GMSL Input 1
>> +        Port 2          GMSL Input 2
>> +        Port 3          GMSL Input 3
>> +        Port 4          CSI-2 Output
>> +
>> +Optional Endpoint Properties for GMSL Input Ports (Port [0-3]):
>> +
>> +- remote-endpoint: phandle to the remote GMSL source endpoint subnode in the
>> +  remote node port.
>> +
>> +Required Endpoint Properties for CSI-2 Output Port (Port 4):
>> +
>> +- remote-endpoint: phandle to the remote CSI-2 sink endpoint node.
>> +- data-lanes: array of physical CSI-2 data lane indexes.
>> +
>> +Required i2c-mux nodes:
>> +----------------------
>> +
>> +Each GMSL link is modeled as a child bus of an i2c bus multiplexer/switch, in
>> +accordance with bindings described in
>> +Documentation/devicetree/bindings/i2c/i2c-mux.txt. The serializer device on the
>> +remote end of the GMSL link shall be modelled as a child node of the
>> +corresponding I2C bus.
>> +
>> +Required i2c child bus properties:
>> +- all properties described as required i2c child bus nodes properties in
>> +  Documentation/devicetree/bindings/i2c/i2c-mux.txt.
>> +
>> +Example:
>> +-------
>> +
>> +	gmsl-deserializer@2c {
>> +		compatible = "maxim,max9286";Not at all late - Just on time 
>> +		reg = <0x2c>;
>> +		poc-supply = <&camera_poc_12v>;
>> +		enable-gpios = <&gpio 13 GPIO_ACTIVE_LOW>;
>> +
>> +		#address-cells = <1>;
>> +		#size-cells = <0>;
>> +
>> +		ports {
>> +			#address-cells = <1>;
>> +			#size-cells = <0>;
>> +
>> +			port@0 {
>> +				reg = <0>;
>> +				max9286_in0: endpoint {
>> +					remote-endpoint = <&rdacm20_out0>;
>> +				};
>> +			};
>> +
>> +			port@1 {
>> +				reg = <1>;
>> +				max9286_in1: endpoint {
>> +					remote-endpoint = <&rdacm20_out1>;
>> +				};
>> +			};
>> +
>> +			port@2 {
>> +				reg = <2>;
>> +				max9286_in2: endpoint {
>> +					remote-endpoint = <&rdacm20_out2>;
>> +				};
>> +			};
>> +
>> +			port@3 {
>> +				reg = <3>;
>> +				max9286_in3: endpoint {
>> +					remote-endpoint = <&rdacm20_out3>;
>> +				};
>> +			};
>> +
>> +			port@4 {
>> +				reg = <4>;
>> +				max9286_out: endpoint {
>> +					data-lanes = <1 2 3 4>;
>> +					remote-endpoint = <&csi40_in>;
>> +				};
>> +			};
>> +		};
>> +
>> +		i2c@0 {
>> +			#address-cells = <1>;
>> +			#size-cells = <0>;
>> +			reg = <0>;
>> +
>> +			camera@51 {
>> +				compatible = "imi,rdacm20";
>> +				reg = <0x51 0x61>;
> 
> I find this kind of address mapping is the weak point in this patchset.
> 
> The ser-deser chipset splits the world in "local" and "remote" side. The
> camera node belongs to the remote side, but the 0x51 and 0x61 addresses
> belong to the local side.

Well, in our use case - in fact the camera has a set of fixed addresses
(0x30,0x40,0x50) for each camera - and these are the addresses we are
requesting the camera to be updated to. Once the camera is communicated
with - the first step is to reprogram the device to respond to the
addresses specified here.


> Think about supporting N different main boards
> and M remote boards. 0x51 might be available on some main boards but not
> all. IMO under the camera@51 (even the i2c@0) node there should be only
> remote hardware description. To support the N*M possible combinations,
> there should be:

Of course - well in fact all of our I2C addresses across our two max9286
instances, and 8 camera devices share the same bus 'address space'.

It's crucial to provide this address on a per board level, which is why
it is specified in the DT.

I wonder if perhaps it was a mistake to include the camera description
in this part of the example, as it's not related to the max9286
specifically.

Rob has already suggested moving these to a lower 'i2c-node' level which
I like the sound of, and might make this separation more clear.


>  * a DT for the main board mentioning only addresses for the
>    local i2c bus, down to the i2c@0 with address-cells, size-cells and
>    reg properties
>  * a DT overlay for each remote board, mentioning the remote i2c
>    chips with their physical addresses, but no local addresses
> 
> The only way I could devise to be generic is to bind each physical
> remote address to a local address at runtime.
> 
> Also, to be implemented reliably, an address translation feature is
> required on the local (de)ser chip.
> 
> So the question is: can the max9286 chip do i2c address translation?
> 

Yes, The max9286 (deser) can do i2c address translation - but so too can
the max9271 (serialiser)

We do our address translation on the camera (serialiser) side.

The cameras *all* boot with the same i2c address (and thus all conflict)
 - We disable all links
 - We enable /one/ link
 - We initialise and reprogram the address of that camera to the address
   specified in the camera node. - Then we move to the next camera.

The reality is we 'just need' a spare address on the I2C bus - but as
yet - there is no mechanism in I2C core to request a spare address.

Thus it is the responsibility of the DT node to ensure there is no conflict.

For an example, here is our DT overlay file for our max9286 expansion board:

https://git.kernel.org/pub/scm/linux/kernel/git/kbingham/rcar.git/commit/?h=gmsl/v5&id=6f2ec549e128b3ca36e9cae59256723cc39df2b1


> Thanks,
> 

-- 
Regards
--
Kieran

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

* Re: [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-13 22:49   ` Luca Ceresoli
@ 2018-11-14  0:46     ` Kieran Bingham
  2018-11-14 10:04       ` Luca Ceresoli
  0 siblings, 1 reply; 26+ messages in thread
From: Kieran Bingham @ 2018-11-14  0:46 UTC (permalink / raw)
  To: Luca Ceresoli, linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Luca,

Thank you for your review,

On 13/11/2018 14:49, Luca Ceresoli wrote:
> Hi Kieran, All,
> 
> below a few minor questions, and a big one at the bottom.
> 
> On 02/11/18 16:47, Kieran Bingham wrote:
>> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>
>> The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and
>> CSI-2 output. The device supports multicamera streaming applications,
>> and features the ability to synchronise the attached cameras.
>>
>> CSI-2 output can be configured with 1 to 4 lanes, and a control channel
>> is supported over I2C, which implements an I2C mux to facilitate
>> communications with connected cameras across the reverse control
>> channel.
>>
>> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> 
> [...]
> 
>> +struct max9286_device {
>> +	struct i2c_client *client;
>> +	struct v4l2_subdev sd;
>> +	struct media_pad pads[MAX9286_N_PADS];
>> +	struct regulator *regulator;
>> +	bool poc_enabled;
>> +	int streaming;
>> +
>> +	struct i2c_mux_core *mux;
>> +	unsigned int mux_channel;
>> +
>> +	struct v4l2_ctrl_handler ctrls;
>> +
>> +	struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
> 
> 5 pads, 4 formats. Why does the source node have no fmt?

The source pad is a CSI2 link - so a 'frame format' would be inappropriate.


>> +static int max9286_init(struct device *dev, void *data)
>> +{
>> +	struct max9286_device *max9286;
>> +	struct i2c_client *client;
>> +	struct device_node *ep;
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	/* Skip non-max9286 devices. */
>> +	if (!dev->of_node || !of_match_node(max9286_dt_ids, dev->of_node))
>> +		return 0;
>> +
>> +	client = to_i2c_client(dev);
>> +	max9286 = i2c_get_clientdata(client);
>> +
>> +	/* Enable the bus power. */
>> +	ret = regulator_enable(max9286->regulator);
>> +	if (ret < 0) {
>> +		dev_err(&client->dev, "Unable to turn PoC on\n");
>> +		return ret;
>> +	}
>> +
>> +	max9286->poc_enabled = true;
>> +
>> +	ret = max9286_setup(max9286);
>> +	if (ret) {
>> +		dev_err(dev, "Unable to setup max9286\n");
>> +		goto err_regulator;
>> +	}
>> +
>> +	v4l2_i2c_subdev_init(&max9286->sd, client, &max9286_subdev_ops);
>> +	max9286->sd.internal_ops = &max9286_subdev_internal_ops;
>> +	max9286->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
>                           ^
> 
> This way you're clearing the V4L2_SUBDEV_FL_IS_I2C set by
> v4l2_i2c_subdev_init(), even though using devicetree I think this won't
> matter in the current kernel code. However I think "max9286->sd.flags |=
> ..." is more correct here, and it's also what most other drivers do.

A quick glance looks like you're right.
That looks like a good catch!

I've updated locally ready for v5.

>> +	v4l2_ctrl_handler_init(&max9286->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(&max9286->ctrls, NULL, V4L2_CID_PIXEL_RATE,
>> +			  50000000, 50000000, 1, 50000000);
>> +	max9286->sd.ctrl_handler = &max9286->ctrls;
>> +	ret = max9286->ctrls.error;
>> +	if (ret)
>> +		goto err_regulator;
>> +
>> +	max9286->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
> 
> According to the docs MEDIA_ENT_F_VID_IF_BRIDGE appears more fitting.

Yes, I agree. We recently updated the adv748x to this too.

Also updated locally to add to v5.


>> +static int max9286_probe(struct i2c_client *client,
>> +			 const struct i2c_device_id *did)
>> +{
>> +	struct max9286_device *dev;
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
>> +	if (!dev)
>> +		return -ENOMEM;
>> +
>> +	dev->client = client;
>> +	i2c_set_clientdata(client, dev);
>> +
>> +	for (i = 0; i < MAX9286_N_SINKS; i++)
>> +		max9286_init_format(&dev->fmt[i]);
>> +
>> +	ret = max9286_parse_dt(dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	dev->regulator = regulator_get(&client->dev, "poc");
>> +	if (IS_ERR(dev->regulator)) {
>> +		if (PTR_ERR(dev->regulator) != -EPROBE_DEFER)
>> +			dev_err(&client->dev,
>> +				"Unable to get PoC regulator (%ld)\n",
>> +				PTR_ERR(dev->regulator));
>> +		ret = PTR_ERR(dev->regulator);
>> +		goto err_free;
>> +	}
>> +
>> +	/*
>> +	 * We can have multiple MAX9286 instances on the same physical I2C
>> +	 * bus, and I2C children behind ports of separate MAX9286 instances
>> +	 * having the same I2C address. As the MAX9286 starts by default with
>> +	 * all ports enabled, we need to disable all ports on all MAX9286
>> +	 * instances before proceeding to further initialize the devices and
>> +	 * instantiate children.
>> +	 *
>> +	 * Start by just disabling all channels on the current device. Then,
>> +	 * if all other MAX9286 on the parent bus have been probed, proceed
>> +	 * to initialize them all, including the current one.
>> +	 */
>> +	max9286_i2c_mux_close(dev);
>> +
>> +	/*
>> +	 * The MAX9286 initialises with auto-acknowledge enabled by default.
>> +	 * This means that if multiple MAX9286 devices are connected to an I2C
>> +	 * bus, another MAX9286 could ack I2C transfers meant for a device on
>> +	 * the other side of the GMSL links for this MAX9286 (such as a
>> +	 * MAX9271). To prevent that disable auto-acknowledge early on; it
>> +	 * will be enabled later as needed.
>> +	 */
>> +	max9286_configure_i2c(dev, false);
>> +
>> +	ret = device_for_each_child(client->dev.parent, &client->dev,
>> +				    max9286_is_bound);
>> +	if (ret)
>> +		return 0;
>> +
>> +	dev_dbg(&client->dev,
>> +		"All max9286 probed: start initialization sequence\n");
>> +	ret = device_for_each_child(client->dev.parent, NULL,
>> +				    max9286_init);
> 
> I can't manage to like this initialization sequence, sorry. If at all
> possible, each max9286 should initialize itself independently from each
> other, like any normal driver.

Yes, I think we're in agreement here, but unfortunately this section is
a workaround for the fact that our devices share a common address space.

We (currently) *must* disable both devices before we start the
initialisation process for either on our platform currently...

That said - I think this section needs to be removed from the upstream
part at least for now. I think we should probably carry this
'workaround' separately.

This part is the core issue that I talked about in my presentation at
ALS-Japan [0]

 [0] https://sched.co/EaXa

> First, it requires that each chip on the remote side can configure its
> own slave address. Not all chips do.
> 
> Second, using a static i2c address map does not scale well and limits
> hotplugging, as I discussed in my reply to patch 1/4. The problem should
> be solvable cleanly if the MAX9286 supports address translation like the
> TI chips.

I don't think we can treat GMSL as hot-pluggable currently ... But as we
discussed - I see that we should think about this for FPD-Link

Also as a further aside here, we use "device_is_bound" which is not
exported, and means that this driver won't compile successfully as a
module currently (thanks to the kbuild test robot for highlighting that)


> Thanks,
> 

-- 
Regards
--
Kieran

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

* Re: [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286
  2018-11-13 23:12     ` Kieran Bingham
@ 2018-11-14 10:04       ` Luca Ceresoli
  2018-11-27 22:47         ` Kieran Bingham
  0 siblings, 1 reply; 26+ messages in thread
From: Luca Ceresoli @ 2018-11-14 10:04 UTC (permalink / raw)
  To: kieran.bingham, linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Laurent Pinchart, Jacopo Mondi

Hi Kieran,

On 14/11/18 00:12, Kieran Bingham wrote:
> Hi Luca,
> 
> On 13/11/2018 14:42, Luca Ceresoli wrote:
>> Hi Kieran, All,
>>
>> sorry for joining this late... See below my considerations.
> 
> I'd say you're on time - not late,
> 
> Thanks for joining :)
> 
>>
>> On 02/11/18 16:47, Kieran Bingham wrote:
>>> From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>>
>>> The MAX9286 deserializes video data received on up to 4 Gigabit
>>> Multimedia Serial Links (GMSL) and outputs them on a CSI-2 port using up
>>> to 4 data lanes.
>>>
>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
>>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>>
>>> ---
>>> v3:
>>>  - Update binding descriptions
>>>
>>> v4:
>>>  - Define the use of a CSI2 D-PHY
>>>  - Rename pwdn-gpios to enable-gpios (with inverted polarity)
>>>  - Remove clock-lanes mapping support
>>>  - rewrap text blocks
>>>  - Fix typos
>>> ---
>>>  .../bindings/media/i2c/maxim,max9286.txt      | 182 ++++++++++++++++++
>>>  1 file changed, 182 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>>> new file mode 100644
>>> index 000000000000..672f6a4d417d
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>>> @@ -0,0 +1,182 @@
>>> +Maxim Integrated Quad GMSL Deserializer
>>> +---------------------------------------
>>> +
>>> +The MAX9286 deserializer receives video data on up to 4 Gigabit Multimedia
>>> +Serial Links (GMSL) and outputs them on a CSI-2 D-PHY port using up to 4 data
>>> +lanes.
>>> +
>>> +In addition to video data, the GMSL links carry a bidirectional control channel
>>> +that encapsulates I2C messages. The MAX9286 forwards all I2C traffic not
>>> +addressed to itself to the other side of the links, where a GMSL serializer
>>> +will output it on a local I2C bus. In the other direction all I2C traffic
>>> +received over GMSL by the MAX9286 is output on the local I2C bus.
>>> +
>>> +Required Properties:
>>> +
>>> +- compatible: Shall be "maxim,max9286"
>>> +- reg: I2C device address
>>> +
>>> +Optional Properties:
>>> +
>>> +- poc-supply: Regulator providing Power over Coax to the cameras
>>> +- enable-gpios: GPIO connected to the #PWDN pin with inverted polarity
>>> +
>>> +Required endpoint nodes:
>>> +-----------------------
>>> +
>>> +The connections to the MAX9286 GMSL and its endpoint nodes are modeled using
>>> +the OF graph bindings in accordance with the video interface bindings defined
>>> +in Documentation/devicetree/bindings/media/video-interfaces.txt.
>>> +
>>> +The following table lists the port number corresponding to each device port.
>>> +
>>> +        Port            Description
>>> +        ----------------------------------------
>>> +        Port 0          GMSL Input 0
>>> +        Port 1          GMSL Input 1
>>> +        Port 2          GMSL Input 2
>>> +        Port 3          GMSL Input 3
>>> +        Port 4          CSI-2 Output
>>> +
>>> +Optional Endpoint Properties for GMSL Input Ports (Port [0-3]):
>>> +
>>> +- remote-endpoint: phandle to the remote GMSL source endpoint subnode in the
>>> +  remote node port.
>>> +
>>> +Required Endpoint Properties for CSI-2 Output Port (Port 4):
>>> +
>>> +- remote-endpoint: phandle to the remote CSI-2 sink endpoint node.
>>> +- data-lanes: array of physical CSI-2 data lane indexes.
>>> +
>>> +Required i2c-mux nodes:
>>> +----------------------
>>> +
>>> +Each GMSL link is modeled as a child bus of an i2c bus multiplexer/switch, in
>>> +accordance with bindings described in
>>> +Documentation/devicetree/bindings/i2c/i2c-mux.txt. The serializer device on the
>>> +remote end of the GMSL link shall be modelled as a child node of the
>>> +corresponding I2C bus.
>>> +
>>> +Required i2c child bus properties:
>>> +- all properties described as required i2c child bus nodes properties in
>>> +  Documentation/devicetree/bindings/i2c/i2c-mux.txt.
>>> +
>>> +Example:
>>> +-------
>>> +
>>> +	gmsl-deserializer@2c {
>>> +		compatible = "maxim,max9286";Not at all late - Just on time 
>>> +		reg = <0x2c>;
>>> +		poc-supply = <&camera_poc_12v>;
>>> +		enable-gpios = <&gpio 13 GPIO_ACTIVE_LOW>;
>>> +
>>> +		#address-cells = <1>;
>>> +		#size-cells = <0>;
>>> +
>>> +		ports {
>>> +			#address-cells = <1>;
>>> +			#size-cells = <0>;
>>> +
>>> +			port@0 {
>>> +				reg = <0>;
>>> +				max9286_in0: endpoint {
>>> +					remote-endpoint = <&rdacm20_out0>;
>>> +				};
>>> +			};
>>> +
>>> +			port@1 {
>>> +				reg = <1>;
>>> +				max9286_in1: endpoint {
>>> +					remote-endpoint = <&rdacm20_out1>;
>>> +				};
>>> +			};
>>> +
>>> +			port@2 {
>>> +				reg = <2>;
>>> +				max9286_in2: endpoint {
>>> +					remote-endpoint = <&rdacm20_out2>;
>>> +				};
>>> +			};
>>> +
>>> +			port@3 {
>>> +				reg = <3>;
>>> +				max9286_in3: endpoint {
>>> +					remote-endpoint = <&rdacm20_out3>;
>>> +				};
>>> +			};
>>> +
>>> +			port@4 {
>>> +				reg = <4>;
>>> +				max9286_out: endpoint {
>>> +					data-lanes = <1 2 3 4>;
>>> +					remote-endpoint = <&csi40_in>;
>>> +				};
>>> +			};
>>> +		};
>>> +
>>> +		i2c@0 {
>>> +			#address-cells = <1>;
>>> +			#size-cells = <0>;
>>> +			reg = <0>;
>>> +
>>> +			camera@51 {
>>> +				compatible = "imi,rdacm20";
>>> +				reg = <0x51 0x61>;
>>
>> I find this kind of address mapping is the weak point in this patchset.
>>
>> The ser-deser chipset splits the world in "local" and "remote" side. The
>> camera node belongs to the remote side, but the 0x51 and 0x61 addresses
>> belong to the local side.
> 
> Well, in our use case - in fact the camera has a set of fixed addresses
> (0x30,0x40,0x50) for each camera - and these are the addresses we are
> requesting the camera to be updated to. Once the camera is communicated
> with - the first step is to reprogram the device to respond to the
> addresses specified here.

Yes, the way it works is clear.

>> Think about supporting N different main boards
>> and M remote boards. 0x51 might be available on some main boards but not
>> all. IMO under the camera@51 (even the i2c@0) node there should be only
>> remote hardware description. To support the N*M possible combinations,
>> there should be:
> 
> Of course - well in fact all of our I2C addresses across our two max9286
> instances, and 8 camera devices share the same bus 'address space'.
> 
> It's crucial to provide this address on a per board level, which is why
> it is specified in the DT.
> 
> I wonder if perhaps it was a mistake to include the camera description
> in this part of the example, as it's not related to the max9286
> specifically.

Interesting point. In my case I'm thinking DT overlays, they help me a
lot in finding a proper generalization. With some generalization, camera
modules [the same would happen with display modules] are similar to
beaglebone capes or rpi hats:

 1. there can be different camera modules being designed over time
 2. there can be different base boards being designed over time
 3. there is a standard interconnection between them (mechanical,
    electrical, communication bus)
 4. camera modules and base boards are designed and sold independently
    (thanks to point 3)

Overlays are a natural choice in this case. Even bootloader-time
overlays will suffice for my reasoning, let's remove the hotplug mess
from this discussion.

Now, in this patch you are modeling the remote camera as if it were a
"normal" I2C device, except:

 a) it has 2 slave addresses (no problem with this)
 b) the 2 slave addresses in DT are not the physical ones

With this model it seems natural to write "camera@51/reg = <0x51 0x61>"
in the camera DT overlay. Except 0x51 and 0x61 do not exist on the
camera module, those numbers come from the base board, since you know
those two addresses are not used on the bus where gmsl-deserializer@2c
is. But it works.

Then one year later a random SBC vendor starts selling a new base board
that has on the same i2c bus a GMSL deser and a random i2c chip,
unrelated to cameras, at address 0x51. Bang, the camera sensor does not
work anymore, but there is no hardware reason for it not to work. Well,
easy to fix, find an address that is unused on all known base boards and
replace, say, 0x51->0x71 in the camera overlay. (OK, I violated the "DT
as a stable ABI" principle)

But then other boards appear and, taking this to an extreme, you can get
to a situation where every i2c address is used on at least one board.
How do you fix that?

Maybe this scenario is a bit too apocalyptic, and maybe too much for
current automotive uses, but I think it illustrates how the current
model is not generic enough. Since there is no existing code in the
kernel yet, I think we should strive to do better in order to minimize
future problems.


My approach is instead to clearly split the local and remote domain. The
latter is what could be moved to an overlay. For example:

&i2c0 {
    serializer@3d {
        reg = <0x3d>;
        ...

        /* Guaranteed not physically present on i2c0 */
        i2c-alias-pool = /bits/ 16 <0x20 0x21 0x22 0x23 0x24 0x25>;

        i2c-mux {
            #address-cells = <1>;
            #size-cells = <0>;

	    i2c@0 {
                reg = <0>;
                #address-cells = <1>;
                #size-cells = <0>;

                // ------8<------ this could be moved to an overlay
                sensor@50 {
                    reg = <0x50>;
                    ...
                    endpoint {...};
                };
                eeprom@51 {
                    reg = <0x51>;
                    ...
                };
                // ------8<------
            };

	    i2c@1 {
                reg = <1>;
                #address-cells = <1>;
                #size-cells = <0>;

                // ------8<------
                sensor@50 {
                    reg = <0x50>;
                    ...
                    endpoint {...};
                };
                eeprom@51 {
                    reg = <0x51>;
                    ...
                };
                // ------8<------
            };
        };
    };
};

The core difference is that I split the camera@51/reg property in two:

 * sensor@50/reg: the remote side (camera overlay);
   carries the physical i2c address (note both sensors are at 0x50)
 * serializer@3d/i2c-alias-pool: the local side (base board);
   lists a pool of addresses that are not used on the i2c bus

See how there is no mixing between local and remote. The pool will
differ from one base board to another.

To implement this, I developed an "i2c address translator" that maps
physical remote addresses to local addresses from the pool at runtime.
It still needs some work, but address translation it is working.

>>  * a DT for the main board mentioning only addresses for the
>>    local i2c bus, down to the i2c@0 with address-cells, size-cells and
>>    reg properties
>>  * a DT overlay for each remote board, mentioning the remote i2c
>>    chips with their physical addresses, but no local addresses
>>
>> The only way I could devise to be generic is to bind each physical
>> remote address to a local address at runtime.
>>
>> Also, to be implemented reliably, an address translation feature is
>> required on the local (de)ser chip.
>>
>> So the question is: can the max9286 chip do i2c address translation?
>>
> 
> Yes, The max9286 (deser) can do i2c address translation - but so too can
> the max9271 (serialiser)

Good!

> We do our address translation on the camera (serialiser) side.

By "address translation" I mean the i2c address is changed by some
device in the middle between the i2c master and the slave. In this sense
you are not doing address translation, you are rather modifying the chip
addresses. Then transactions happen with the new (0x51/0x61) address,
which does not get modified during subsequent transactions.

> The cameras *all* boot with the same i2c address (and thus all conflict)
>  - We disable all links
>  - We enable /one/ link
>  - We initialise and reprogram the address of that camera to the address
>    specified in the camera node. - Then we move to the next camera.
> 
> The reality is we 'just need' a spare address on the I2C bus - but as
> yet - there is no mechanism in I2C core to request a spare address.

Not a reliable one, definitely, since there could be i2c devices unknown
to the software. This is why I had to introduce the alias pool: the DT
writer is required to know which addresses are available and list them
in DT.

-- 
Luca

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

* Re: [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-14  0:46     ` Kieran Bingham
@ 2018-11-14 10:04       ` Luca Ceresoli
  2018-11-20  0:32         ` Kieran Bingham
  0 siblings, 1 reply; 26+ messages in thread
From: Luca Ceresoli @ 2018-11-14 10:04 UTC (permalink / raw)
  To: kieran.bingham, linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Kieran,

On 14/11/18 01:46, Kieran Bingham wrote:
> Hi Luca,
> 
> Thank you for your review,
> 
> On 13/11/2018 14:49, Luca Ceresoli wrote:
>> Hi Kieran, All,
>>
>> below a few minor questions, and a big one at the bottom.
>>
>> On 02/11/18 16:47, Kieran Bingham wrote:
>>> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>>
>>> The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and
>>> CSI-2 output. The device supports multicamera streaming applications,
>>> and features the ability to synchronise the attached cameras.
>>>
>>> CSI-2 output can be configured with 1 to 4 lanes, and a control channel
>>> is supported over I2C, which implements an I2C mux to facilitate
>>> communications with connected cameras across the reverse control
>>> channel.
>>>
>>> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
>>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
>>
>> [...]
>>
>>> +struct max9286_device {
>>> +	struct i2c_client *client;
>>> +	struct v4l2_subdev sd;
>>> +	struct media_pad pads[MAX9286_N_PADS];
>>> +	struct regulator *regulator;
>>> +	bool poc_enabled;
>>> +	int streaming;
>>> +
>>> +	struct i2c_mux_core *mux;
>>> +	unsigned int mux_channel;
>>> +
>>> +	struct v4l2_ctrl_handler ctrls;
>>> +
>>> +	struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
>>
>> 5 pads, 4 formats. Why does the source node have no fmt?
> 
> The source pad is a CSI2 link - so a 'frame format' would be inappropriate.

Ok, thanks for the clarification.

>>> +static int max9286_probe(struct i2c_client *client,
>>> +			 const struct i2c_device_id *did)
>>> +{
>>> +	struct max9286_device *dev;
>>> +	unsigned int i;
>>> +	int ret;
>>> +
>>> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
>>> +	if (!dev)
>>> +		return -ENOMEM;
>>> +
>>> +	dev->client = client;
>>> +	i2c_set_clientdata(client, dev);
>>> +
>>> +	for (i = 0; i < MAX9286_N_SINKS; i++)
>>> +		max9286_init_format(&dev->fmt[i]);
>>> +
>>> +	ret = max9286_parse_dt(dev);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	dev->regulator = regulator_get(&client->dev, "poc");
>>> +	if (IS_ERR(dev->regulator)) {
>>> +		if (PTR_ERR(dev->regulator) != -EPROBE_DEFER)
>>> +			dev_err(&client->dev,
>>> +				"Unable to get PoC regulator (%ld)\n",
>>> +				PTR_ERR(dev->regulator));
>>> +		ret = PTR_ERR(dev->regulator);
>>> +		goto err_free;
>>> +	}
>>> +
>>> +	/*
>>> +	 * We can have multiple MAX9286 instances on the same physical I2C
>>> +	 * bus, and I2C children behind ports of separate MAX9286 instances
>>> +	 * having the same I2C address. As the MAX9286 starts by default with
>>> +	 * all ports enabled, we need to disable all ports on all MAX9286
>>> +	 * instances before proceeding to further initialize the devices and
>>> +	 * instantiate children.
>>> +	 *
>>> +	 * Start by just disabling all channels on the current device. Then,
>>> +	 * if all other MAX9286 on the parent bus have been probed, proceed
>>> +	 * to initialize them all, including the current one.
>>> +	 */
>>> +	max9286_i2c_mux_close(dev);
>>> +
>>> +	/*
>>> +	 * The MAX9286 initialises with auto-acknowledge enabled by default.
>>> +	 * This means that if multiple MAX9286 devices are connected to an I2C
>>> +	 * bus, another MAX9286 could ack I2C transfers meant for a device on
>>> +	 * the other side of the GMSL links for this MAX9286 (such as a
>>> +	 * MAX9271). To prevent that disable auto-acknowledge early on; it
>>> +	 * will be enabled later as needed.
>>> +	 */
>>> +	max9286_configure_i2c(dev, false);
>>> +
>>> +	ret = device_for_each_child(client->dev.parent, &client->dev,
>>> +				    max9286_is_bound);
>>> +	if (ret)
>>> +		return 0;
>>> +
>>> +	dev_dbg(&client->dev,
>>> +		"All max9286 probed: start initialization sequence\n");
>>> +	ret = device_for_each_child(client->dev.parent, NULL,
>>> +				    max9286_init);
>>
>> I can't manage to like this initialization sequence, sorry. If at all
>> possible, each max9286 should initialize itself independently from each
>> other, like any normal driver.
> 
> Yes, I think we're in agreement here, but unfortunately this section is
> a workaround for the fact that our devices share a common address space.
> 
> We (currently) *must* disable both devices before we start the
> initialisation process for either on our platform currently...

The model I proposed in my review to patch 1/4 (split remote physical
address from local address pool) allows to avoid this workaround.

> That said - I think this section needs to be removed from the upstream
> part at least for now. I think we should probably carry this
> 'workaround' separately.
> 
> This part is the core issue that I talked about in my presentation at
> ALS-Japan [0]
> 
>  [0] https://sched.co/EaXa

Oh, interesting, I hadn't noticed that you gave this talk -- at the same
conference as Vladimir's talk! No video recording apparently, but are
slides available at least?

>> First, it requires that each chip on the remote side can configure its
>> own slave address. Not all chips do.
>>
>> Second, using a static i2c address map does not scale well and limits
>> hotplugging, as I discussed in my reply to patch 1/4. The problem should
>> be solvable cleanly if the MAX9286 supports address translation like the
>> TI chips.
> 
> I don't think we can treat GMSL as hot-pluggable currently ... But as we
> discussed - I see that we should think about this for FPD-Link

I've been mixing hotplug and DT overlays and that generated confusion,
sorry. My point exists even with no hotplug, see the reply to patch 1/4.

-- 
Luca

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

* [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-14 10:04       ` Luca Ceresoli
@ 2018-11-20  0:32         ` Kieran Bingham
  2018-11-20 10:51           ` Luca Ceresoli
  0 siblings, 1 reply; 26+ messages in thread
From: Kieran Bingham @ 2018-11-20  0:32 UTC (permalink / raw)
  To: Luca Ceresoli, linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Luca,

My apologies for my travel induced delay in responding here,



On 14/11/2018 02:04, Luca Ceresoli wrote:
> Hi Kieran,
> 
> On 14/11/18 01:46, Kieran Bingham wrote:
>> Hi Luca,
>>
>> Thank you for your review,
>>
>> On 13/11/2018 14:49, Luca Ceresoli wrote:
>>> Hi Kieran, All,
>>>
>>> below a few minor questions, and a big one at the bottom.
>>>
>>> On 02/11/18 16:47, Kieran Bingham wrote:
>>>> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>>>
>>>> The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and
>>>> CSI-2 output. The device supports multicamera streaming applications,
>>>> and features the ability to synchronise the attached cameras.
>>>>
>>>> CSI-2 output can be configured with 1 to 4 lanes, and a control channel
>>>> is supported over I2C, which implements an I2C mux to facilitate
>>>> communications with connected cameras across the reverse control
>>>> channel.
>>>>
>>>> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
>>>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>>> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
>>>
>>> [...]
>>>
>>>> +struct max9286_device {
>>>> +	struct i2c_client *client;
>>>> +	struct v4l2_subdev sd;
>>>> +	struct media_pad pads[MAX9286_N_PADS];
>>>> +	struct regulator *regulator;
>>>> +	bool poc_enabled;
>>>> +	int streaming;
>>>> +
>>>> +	struct i2c_mux_core *mux;
>>>> +	unsigned int mux_channel;
>>>> +
>>>> +	struct v4l2_ctrl_handler ctrls;
>>>> +
>>>> +	struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
>>>
>>> 5 pads, 4 formats. Why does the source node have no fmt?
>>
>> The source pad is a CSI2 link - so a 'frame format' would be inappropriate.
> 
> Ok, thanks for the clarification.
> 
>>>> +static int max9286_probe(struct i2c_client *client,
>>>> +			 const struct i2c_device_id *did)
>>>> +{
>>>> +	struct max9286_device *dev;
>>>> +	unsigned int i;
>>>> +	int ret;
>>>> +
>>>> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
>>>> +	if (!dev)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	dev->client = client;
>>>> +	i2c_set_clientdata(client, dev);
>>>> +
>>>> +	for (i = 0; i < MAX9286_N_SINKS; i++)
>>>> +		max9286_init_format(&dev->fmt[i]);
>>>> +
>>>> +	ret = max9286_parse_dt(dev);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	dev->regulator = regulator_get(&client->dev, "poc");
>>>> +	if (IS_ERR(dev->regulator)) {
>>>> +		if (PTR_ERR(dev->regulator) != -EPROBE_DEFER)
>>>> +			dev_err(&client->dev,
>>>> +				"Unable to get PoC regulator (%ld)\n",
>>>> +				PTR_ERR(dev->regulator));
>>>> +		ret = PTR_ERR(dev->regulator);
>>>> +		goto err_free;
>>>> +	}
>>>> +
>>>> +	/*
>>>> +	 * We can have multiple MAX9286 instances on the same physical I2C
>>>> +	 * bus, and I2C children behind ports of separate MAX9286 instances
>>>> +	 * having the same I2C address. As the MAX9286 starts by default with
>>>> +	 * all ports enabled, we need to disable all ports on all MAX9286
>>>> +	 * instances before proceeding to further initialize the devices and
>>>> +	 * instantiate children.
>>>> +	 *
>>>> +	 * Start by just disabling all channels on the current device. Then,
>>>> +	 * if all other MAX9286 on the parent bus have been probed, proceed
>>>> +	 * to initialize them all, including the current one.
>>>> +	 */
>>>> +	max9286_i2c_mux_close(dev);
>>>> +
>>>> +	/*
>>>> +	 * The MAX9286 initialises with auto-acknowledge enabled by default.
>>>> +	 * This means that if multiple MAX9286 devices are connected to an I2C
>>>> +	 * bus, another MAX9286 could ack I2C transfers meant for a device on
>>>> +	 * the other side of the GMSL links for this MAX9286 (such as a
>>>> +	 * MAX9271). To prevent that disable auto-acknowledge early on; it
>>>> +	 * will be enabled later as needed.
>>>> +	 */
>>>> +	max9286_configure_i2c(dev, false);
>>>> +
>>>> +	ret = device_for_each_child(client->dev.parent, &client->dev,
>>>> +				    max9286_is_bound);
>>>> +	if (ret)
>>>> +		return 0;
>>>> +
>>>> +	dev_dbg(&client->dev,
>>>> +		"All max9286 probed: start initialization sequence\n");
>>>> +	ret = device_for_each_child(client->dev.parent, NULL,
>>>> +				    max9286_init);
>>>
>>> I can't manage to like this initialization sequence, sorry. If at all
>>> possible, each max9286 should initialize itself independently from each
>>> other, like any normal driver.
>>
>> Yes, I think we're in agreement here, but unfortunately this section is
>> a workaround for the fact that our devices share a common address space.
>>
>> We (currently) *must* disable both devices before we start the
>> initialisation process for either on our platform currently...
> 
> The model I proposed in my review to patch 1/4 (split remote physical
> address from local address pool) allows to avoid this workaround.


Having just talked this through with Jacopo I think I see that we have
two topics getting intertwined here too.

 - Address translation so that we can separate the camera addressing

 - our 'device_is_bound/device_for_each_child()' workaround which lets
   us make sure the buses are closed before we power on any camera
   device.


For the upstream process of this driver - I will remove the
'device_is_bound()/device_for_each_child()' workarounds.


It is /ugly/ and needs more consideration, but I believe we do still
need it (or something similar) for our platform currently.



The other side of that is the address translation. I think I was wrong
earlier and may have said we have address translation on both sides.


I think I now see that we only have some minimal translation for two
addresses on the remote (max9271) side, not the local (max9286) side.

We have the ability to reprogram addresses through, and that's what we
are using.


There's a lot more local discussion going on here that I may have missed
so I hope Jacopo, Niklas, or Laurent may add more here if relevant :)




>> That said - I think this section needs to be removed from the upstream
>> part at least for now. I think we should probably carry this
>> 'workaround' separately.
>>
>> This part is the core issue that I talked about in my presentation at
>> ALS-Japan [0]
>>
>>  [0] https://sched.co/EaXa
> 
> Oh, interesting, I hadn't noticed that you gave this talk -- at the same
> conference as Vladimir's talk! No video recording apparently, but are
> slides available at least?

Hrm ... I was sure I uploaded to the conference so that they should have
been available on that URL - but they are not showing.

They are available here:

	https://www.slideshare.net/KieranBingham/gmsl-in-linux

(Please excuse the incorrect date on the first slide :D)

I had put a proposal in to give this talk again at ELCE but
unfortunately it didn't get accepted.

Seems it really would have been useful to have a slot. Lets hope next
ELCE is too late and we work out a good system by then :)


>>> First, it requires that each chip on the remote side can configure its
>>> own slave address. Not all chips do.
>>>
>>> Second, using a static i2c address map does not scale well and limits
>>> hotplugging, as I discussed in my reply to patch 1/4. The problem should
>>> be solvable cleanly if the MAX9286 supports address translation like the
>>> TI chips.
>>
>> I don't think we can treat GMSL as hot-pluggable currently ... But as we
>> discussed - I see that we should think about this for FPD-Link
> 
> I've been mixing hotplug and DT overlays and that generated confusion,
> sorry. My point exists even with no hotplug, see the reply to patch 1/4.


Ok - I understand how you were using the terminology / analogy's now.
Lets move back to patch 1/4 :)


-- 
Regards
--
Kieran



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

* Re: [PATCH v4 4/4] media: i2c: Add RDACM20 driver
  2018-11-02 15:47 ` [PATCH v4 4/4] media: i2c: Add RDACM20 driver Kieran Bingham
@ 2018-11-20  8:34   ` Sakari Ailus
  2018-11-28 12:51     ` Kieran Bingham
  0 siblings, 1 reply; 26+ messages in thread
From: Sakari Ailus @ 2018-11-20  8:34 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: linux-renesas-soc, linux-media, devicetree,
	Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Kieran,

On Fri, Nov 02, 2018 at 03:47:23PM +0000, Kieran Bingham wrote:
> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> 
> 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.
> 
> 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
> ---
>  MAINTAINERS                         |  10 +
>  drivers/media/i2c/Kconfig           |  11 +
>  drivers/media/i2c/Makefile          |   1 +
>  drivers/media/i2c/rdacm20-ov10635.h | 953 ++++++++++++++++++++++++++++
>  drivers/media/i2c/rdacm20.c         | 635 ++++++++++++++++++
>  5 files changed, 1610 insertions(+)
>  create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
>  create mode 100644 drivers/media/i2c/rdacm20.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 745f0fd1fff1..26ef20087a43 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12230,6 +12230,16 @@ S:	Supported
>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
>  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>

I'm happy to see this will be well maintained. :-)

> +L:	linux-media@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/media/i2c/rdacm20.txt
> +F:	drivers/media/i2c/rdacm20*
> +
>  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 eadc00bdd3bf..5eded5e337ec 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -989,6 +989,17 @@ config VIDEO_S5C73M3
>  	  This is a V4L2 sensor driver for Samsung S5C73M3
>  	  8 Mpixel camera.
>  
> +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"
>  
>  config VIDEO_ADP1653
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 4de7fe62b179..121d28283d45 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -111,5 +111,6 @@ obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
>  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_SDR_MAX2175) += max2175.o
> 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

A part of this driver looks like a driver for an OV camera sensor. Would
there be something that prevents separating the camera sensor driver from
this one?

> +
> +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..d96b2eb5ab1b
> --- /dev/null
> +++ b/drivers/media/i2c/rdacm20.c
> @@ -0,0 +1,635 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * IMI RDACM20 GMSL Camera Driver
> + *
> + * 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.
> + */
> +
> +/*
> + * The camera is mode 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"
> +
> +#define RDACM20_SENSOR_HARD_RESET
> +
> +#define MAX9271_I2C_ADDRESS		0x40
> +
> +/* 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_SETGPO			BIT(0)
> +/* Register 0x15 */
> +#define MAX9271_PCLKDET			BIT(0)
> +
> +#define MAXIM_I2C_I2C_SPEED_400KHZ	MAX9271_I2CMSTBT_339KBPS
> +#define MAXIM_I2C_I2C_SPEED_100KHZ	MAX9271_I2CMSTBT_105KBPS
> +#define MAXIM_I2C_SPEED			MAXIM_I2C_I2C_SPEED_100KHZ
> +
> +#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
> +/* #define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY10_2X10 */
> +
> +struct rdacm20_device {
> +	struct i2c_client		*client;
> +	struct i2c_client		*sensor;
> +	struct v4l2_subdev		sd;
> +	struct media_pad		pad;
> +	struct v4l2_ctrl_handler	ctrls;
> +};
> +
> +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 max9271_read(struct rdacm20_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 rdacm20_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;
> +}
> +
> +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->client->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->client->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->client->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->client->dev,
> +				"%s: register %u (0x%04x) write failed (%d)\n",
> +				__func__, i, regs[i].reg, ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * rdacm20_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 rdacm20_pclk_detect(struct rdacm20_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;
> +}
> +
> +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct rdacm20_device *dev = sd_to_rdacm20(sd);
> +	int ret;
> +
> +	if (enable) {
> +		ret = rdacm20_pclk_detect(dev);
> +		if (ret)
> +			return ret;
> +
> +		/* Enable the serial link. */
> +		max9271_write(dev, 0x04, MAX9271_SEREN | MAX9271_REVCCEN |
> +			      MAX9271_FWDCCEN);
> +	} else {
> +		/* Disable the serial link. */
> +		max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
> +			      MAX9271_FWDCCEN);
> +	}
> +
> +	return 0;
> +}
> +
> +static int rdacm20_g_mbus_config(struct v4l2_subdev *sd,
> +				 struct v4l2_mbus_config *cfg)
> +{
> +	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
> +		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
> +	cfg->type = V4L2_MBUS_CSI2_DPHY;

Hmm. What are you using g_mbus_config() for?

> +
> +	return 0;
> +}
> +
> +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,
> +	.g_mbus_config	= rdacm20_g_mbus_config,
> +};
> +
> +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 max9271_configure_i2c(struct rdacm20_device *dev)
> +{
> +	/*
> +	 * Configure the I2C bus:
> +	 *
> +	 * - Enable high thresholds on the reverse channel
> +	 * - Disable artificial ACK and set I2C speed
> +	 */
> +	max9271_write(dev, 0x08, MAX9271_REV_HIVTH);
> +	usleep_range(5000, 8000);
> +
> +	max9271_write(dev, 0x0d, MAX9271_I2CSLVSH_469NS_234NS |
> +		      MAX9271_I2CSLVTO_1024US | MAXIM_I2C_SPEED);
> +	usleep_range(5000, 8000);
> +
> +	return 0;
> +}
> +
> +static int max9271_configure_gmsl_link(struct rdacm20_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);
> +
> +	return 0;
> +}
> +
> +static int max9271_verify_id(struct rdacm20_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;
> +}
> +
> +static int max9271_configure_address(struct rdacm20_device *dev, u8 addr)
> +{
> +	int ret;
> +
> +	/* Change the MAX9271 I2C address. */
> +	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;
> +	}
> +	dev->client->addr = addr;
> +	usleep_range(3500, 5000);
> +
> +	return 0;
> +}
> +
> +static int rdacm20_initialize(struct rdacm20_device *dev)
> +{
> +	u32 addrs[2];
> +	int ret;
> +
> +	ret = of_property_read_u32_array(dev->client->dev.of_node, "reg",
> +					 addrs, ARRAY_SIZE(addrs));
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "Invalid DT reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * FIXME: The MAX9271 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 = MAX9271_I2C_ADDRESS;
> +
> +	/* Verify communication with the MAX9271. */
> +	i2c_smbus_read_byte(dev->client);	/* ping to wake-up */
> +
> +	/*
> +	 *  Ensure that we have a good link configuration before attempting to
> +	 *  identify the device.
> +	 */
> +	max9271_configure_i2c(dev);
> +	max9271_configure_gmsl_link(dev);
> +
> +	ret = max9271_verify_id(dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = max9271_configure_address(dev, addrs[0]);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Reset and verify communication with the OV10635. */
> +#ifdef RDACM20_SENSOR_HARD_RESET
> +	/* Cycle the OV10635 reset signal connected to the MAX9271 GPIO1. */
> +	max9271_write(dev, 0x0f, 0xff & ~(MAX9271_GPIO1OUT | MAX9271_SETGPO));
> +	mdelay(10);
> +	max9271_write(dev, 0x0f, 0xff & ~MAX9271_SETGPO);
> +	mdelay(10);

Do you need a busy loop? Could you use msleep()?

> +#else
> +	/* Perform a software reset. */
> +	ret = ov10635_write(dev, OV10635_SOFTWARE_RESET, 1);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "OV10635 reset failed (%d)\n", ret);
> +		return -ENXIO;
> +	}
> +
> +	udelay(100);
> +#endif
> +
> +	ret = ov10635_read16(dev, OV10635_PID);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "OV10635 ID read failed (%d)\n",
> +			ret);
> +		return -ENXIO;
> +	}
> +
> +	if (ret != OV10635_VERSION) {
> +		dev_err(&dev->client->dev, "OV10635 ID mismatch (0x%04x)\n",
> +			ret);
> +		return -ENXIO;
> +	}
> +
> +	dev_info(&dev->client->dev, "Identified MAX9271 + OV10635 device\n");
> +
> +	/* Change the sensor I2C address. */
> +	ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
> +			    (addrs[1] << 1) | OV10635_SC_CMMN_SCCB_ID_SELECT);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev,
> +			"OV10635 I2C address change failed (%d)\n", ret);
> +		return ret;
> +	}
> +	dev->sensor->addr = addrs[1];
> +	usleep_range(3500, 5000);
> +
> +	/* Program the 0V10635 initial configuration. */
> +	ret = ov10635_set_regs(dev, ov10635_regs_wizard,
> +			       ARRAY_SIZE(ov10635_regs_wizard));

return ov...(); ?

> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int rdacm20_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *did)
> +{
> +	struct rdacm20_device *dev;
> +	struct fwnode_handle *ep;
> +	int ret;
> +
> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);

You could use devm_kzalloc().

> +	if (!dev)
> +		return -ENOMEM;
> +
> +	dev->client = client;
> +
> +	/* Create the dummy I2C client for the sensor. */
> +	dev->sensor = i2c_new_dummy(client->adapter, OV10635_I2C_ADDRESS);
> +	if (!dev->sensor) {
> +		ret = -ENXIO;
> +		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;

|=, as in the other patch.

> +
> +	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;
> +
> +	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(ep);
> +error:

You're missing v4l2_ctrl_handler_free() here.

> +	media_entity_cleanup(&dev->sd.entity);
> +	if (dev->sensor)
> +		i2c_unregister_device(dev->sensor);
> +	kfree(dev);
> +
> +	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);

As well as here.

> +	media_entity_cleanup(&dev->sd.entity);
> +	i2c_unregister_device(dev->sensor);
> +	kfree(dev);
> +
> +	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 i2c_device_id rdacm20_id[] = {
> +	{ "rdacm20", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, rdacm20_id);
> +
> +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		= rdacm20_probe,

Could you use probe_new, so you could remove the i2c ID table? Or do you
need that for something?

> +	.remove		= rdacm20_remove,
> +	.shutdown	= rdacm20_shutdown,
> +	.id_table	= rdacm20_id,
> +};
> +
> +module_i2c_driver(rdacm20_i2c_driver);
> +
> +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
> +MODULE_AUTHOR("Vladimir Barinov");
> +MODULE_LICENSE("GPL");

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi

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

* Re: [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-20  0:32         ` Kieran Bingham
@ 2018-11-20 10:51           ` Luca Ceresoli
  2018-11-20 17:44             ` jacopo mondi
  0 siblings, 1 reply; 26+ messages in thread
From: Luca Ceresoli @ 2018-11-20 10:51 UTC (permalink / raw)
  To: kieran.bingham, linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Kieran,

On 20/11/18 01:32, Kieran Bingham wrote:
> Hi Luca,
> 
> My apologies for my travel induced delay in responding here,

No problem.

> On 14/11/2018 02:04, Luca Ceresoli wrote:
[...]
>>>>> +static int max9286_probe(struct i2c_client *client,
>>>>> +			 const struct i2c_device_id *did)
>>>>> +{
>>>>> +	struct max9286_device *dev;
>>>>> +	unsigned int i;
>>>>> +	int ret;
>>>>> +
>>>>> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
>>>>> +	if (!dev)
>>>>> +		return -ENOMEM;
>>>>> +
>>>>> +	dev->client = client;
>>>>> +	i2c_set_clientdata(client, dev);
>>>>> +
>>>>> +	for (i = 0; i < MAX9286_N_SINKS; i++)
>>>>> +		max9286_init_format(&dev->fmt[i]);
>>>>> +
>>>>> +	ret = max9286_parse_dt(dev);
>>>>> +	if (ret)
>>>>> +		return ret;
>>>>> +
>>>>> +	dev->regulator = regulator_get(&client->dev, "poc");
>>>>> +	if (IS_ERR(dev->regulator)) {
>>>>> +		if (PTR_ERR(dev->regulator) != -EPROBE_DEFER)
>>>>> +			dev_err(&client->dev,
>>>>> +				"Unable to get PoC regulator (%ld)\n",
>>>>> +				PTR_ERR(dev->regulator));
>>>>> +		ret = PTR_ERR(dev->regulator);
>>>>> +		goto err_free;
>>>>> +	}
>>>>> +
>>>>> +	/*
>>>>> +	 * We can have multiple MAX9286 instances on the same physical I2C
>>>>> +	 * bus, and I2C children behind ports of separate MAX9286 instances
>>>>> +	 * having the same I2C address. As the MAX9286 starts by default with
>>>>> +	 * all ports enabled, we need to disable all ports on all MAX9286
>>>>> +	 * instances before proceeding to further initialize the devices and
>>>>> +	 * instantiate children.
>>>>> +	 *
>>>>> +	 * Start by just disabling all channels on the current device. Then,
>>>>> +	 * if all other MAX9286 on the parent bus have been probed, proceed
>>>>> +	 * to initialize them all, including the current one.
>>>>> +	 */
>>>>> +	max9286_i2c_mux_close(dev);
>>>>> +
>>>>> +	/*
>>>>> +	 * The MAX9286 initialises with auto-acknowledge enabled by default.
>>>>> +	 * This means that if multiple MAX9286 devices are connected to an I2C
>>>>> +	 * bus, another MAX9286 could ack I2C transfers meant for a device on
>>>>> +	 * the other side of the GMSL links for this MAX9286 (such as a
>>>>> +	 * MAX9271). To prevent that disable auto-acknowledge early on; it
>>>>> +	 * will be enabled later as needed.
>>>>> +	 */
>>>>> +	max9286_configure_i2c(dev, false);
>>>>> +
>>>>> +	ret = device_for_each_child(client->dev.parent, &client->dev,
>>>>> +				    max9286_is_bound);
>>>>> +	if (ret)
>>>>> +		return 0;
>>>>> +
>>>>> +	dev_dbg(&client->dev,
>>>>> +		"All max9286 probed: start initialization sequence\n");
>>>>> +	ret = device_for_each_child(client->dev.parent, NULL,
>>>>> +				    max9286_init);
>>>>
>>>> I can't manage to like this initialization sequence, sorry. If at all
>>>> possible, each max9286 should initialize itself independently from each
>>>> other, like any normal driver.
>>>
>>> Yes, I think we're in agreement here, but unfortunately this section is
>>> a workaround for the fact that our devices share a common address space.
>>>
>>> We (currently) *must* disable both devices before we start the
>>> initialisation process for either on our platform currently...
>>
>> The model I proposed in my review to patch 1/4 (split remote physical
>> address from local address pool) allows to avoid this workaround.
> 
> 
> Having just talked this through with Jacopo I think I see that we have
> two topics getting intertwined here too.
> 
>  - Address translation so that we can separate the camera addressing
> 
>  - our 'device_is_bound/device_for_each_child()' workaround which lets
>    us make sure the buses are closed before we power on any camera
>    device.
> 
> 
> For the upstream process of this driver - I will remove the
> 'device_is_bound()/device_for_each_child()' workarounds.
> 
> 
> It is /ugly/ and needs more consideration, but I believe we do still
> need it (or something similar) for our platform currently.
> 
> 
> 
> The other side of that is the address translation. I think I was wrong
> earlier and may have said we have address translation on both sides.
> 
> 
> I think I now see that we only have some minimal translation for two
> addresses on the remote (max9271) side, not the local (max9286) side.

Can the remote (max9271) translate addresses for transactions
originating from the local side? This would make it possible to do a
proper address translation, although 2 addresses is a quite small amount.

BTW all the TI chips I'm looking at can do address translation but, as
far as I understand, only when acting as "slave proxy", i.e. when
attached to the bus master. If the Maxim chips do the same, the "remote
translation" would be unusable.

> We have the ability to reprogram addresses through, and that's what we
> are using.

Sadly, it looks pretty much unavoidable...

> There's a lot more local discussion going on here that I may have missed
> so I hope Jacopo, Niklas, or Laurent may add more here if relevant :)
> 
> 
> 
> 
>>> That said - I think this section needs to be removed from the upstream
>>> part at least for now. I think we should probably carry this
>>> 'workaround' separately.
>>>
>>> This part is the core issue that I talked about in my presentation at
>>> ALS-Japan [0]
>>>
>>>  [0] https://sched.co/EaXa
>>
>> Oh, interesting, I hadn't noticed that you gave this talk -- at the same
>> conference as Vladimir's talk! No video recording apparently, but are
>> slides available at least?
> 
> Hrm ... I was sure I uploaded to the conference so that they should have
> been available on that URL - but they are not showing.
> 
> They are available here:
> 
> 	https://www.slideshare.net/KieranBingham/gmsl-in-linux

Thanks.

> (Please excuse the incorrect date on the first slide :D)
> 
> I had put a proposal in to give this talk again at ELCE but
> unfortunately it didn't get accepted.
>
> Seems it really would have been useful to have a slot. Lets hope next
> ELCE is too late and we work out a good system by then :)

Indeed it would have been!

But hey, The FOSDEM CFPs are still open!

Bye,
-- 
Luca

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

* Re: [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-20 10:51           ` Luca Ceresoli
@ 2018-11-20 17:44             ` jacopo mondi
  2018-11-21 10:05               ` Luca Ceresoli
  0 siblings, 1 reply; 26+ messages in thread
From: jacopo mondi @ 2018-11-20 17:44 UTC (permalink / raw)
  To: Luca Ceresoli
  Cc: kieran.bingham, linux-renesas-soc, linux-media, devicetree,
	sakari.ailus, Niklas Söderlund, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

[-- Attachment #1: Type: text/plain, Size: 11705 bytes --]

Hi Luca,

On Tue, Nov 20, 2018 at 11:51:37AM +0100, Luca Ceresoli wrote:
> Hi Kieran,
>
> On 20/11/18 01:32, Kieran Bingham wrote:
> > Hi Luca,
> >
> > My apologies for my travel induced delay in responding here,
>
> No problem.
>
> > On 14/11/2018 02:04, Luca Ceresoli wrote:
> [...]
> >>>>> +static int max9286_probe(struct i2c_client *client,
> >>>>> +			 const struct i2c_device_id *did)
> >>>>> +{
> >>>>> +	struct max9286_device *dev;
> >>>>> +	unsigned int i;
> >>>>> +	int ret;
> >>>>> +
> >>>>> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> >>>>> +	if (!dev)
> >>>>> +		return -ENOMEM;
> >>>>> +
> >>>>> +	dev->client = client;
> >>>>> +	i2c_set_clientdata(client, dev);
> >>>>> +
> >>>>> +	for (i = 0; i < MAX9286_N_SINKS; i++)
> >>>>> +		max9286_init_format(&dev->fmt[i]);
> >>>>> +
> >>>>> +	ret = max9286_parse_dt(dev);
> >>>>> +	if (ret)
> >>>>> +		return ret;
> >>>>> +
> >>>>> +	dev->regulator = regulator_get(&client->dev, "poc");
> >>>>> +	if (IS_ERR(dev->regulator)) {
> >>>>> +		if (PTR_ERR(dev->regulator) != -EPROBE_DEFER)
> >>>>> +			dev_err(&client->dev,
> >>>>> +				"Unable to get PoC regulator (%ld)\n",
> >>>>> +				PTR_ERR(dev->regulator));
> >>>>> +		ret = PTR_ERR(dev->regulator);
> >>>>> +		goto err_free;
> >>>>> +	}
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * We can have multiple MAX9286 instances on the same physical I2C
> >>>>> +	 * bus, and I2C children behind ports of separate MAX9286 instances
> >>>>> +	 * having the same I2C address. As the MAX9286 starts by default with
> >>>>> +	 * all ports enabled, we need to disable all ports on all MAX9286
> >>>>> +	 * instances before proceeding to further initialize the devices and
> >>>>> +	 * instantiate children.
> >>>>> +	 *
> >>>>> +	 * Start by just disabling all channels on the current device. Then,
> >>>>> +	 * if all other MAX9286 on the parent bus have been probed, proceed
> >>>>> +	 * to initialize them all, including the current one.
> >>>>> +	 */
> >>>>> +	max9286_i2c_mux_close(dev);
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * The MAX9286 initialises with auto-acknowledge enabled by default.
> >>>>> +	 * This means that if multiple MAX9286 devices are connected to an I2C
> >>>>> +	 * bus, another MAX9286 could ack I2C transfers meant for a device on
> >>>>> +	 * the other side of the GMSL links for this MAX9286 (such as a
> >>>>> +	 * MAX9271). To prevent that disable auto-acknowledge early on; it
> >>>>> +	 * will be enabled later as needed.
> >>>>> +	 */
> >>>>> +	max9286_configure_i2c(dev, false);
> >>>>> +
> >>>>> +	ret = device_for_each_child(client->dev.parent, &client->dev,
> >>>>> +				    max9286_is_bound);
> >>>>> +	if (ret)
> >>>>> +		return 0;
> >>>>> +
> >>>>> +	dev_dbg(&client->dev,
> >>>>> +		"All max9286 probed: start initialization sequence\n");
> >>>>> +	ret = device_for_each_child(client->dev.parent, NULL,
> >>>>> +				    max9286_init);
> >>>>
> >>>> I can't manage to like this initialization sequence, sorry. If at all
> >>>> possible, each max9286 should initialize itself independently from each
> >>>> other, like any normal driver.
> >>>
> >>> Yes, I think we're in agreement here, but unfortunately this section is
> >>> a workaround for the fact that our devices share a common address space.
> >>>
> >>> We (currently) *must* disable both devices before we start the
> >>> initialisation process for either on our platform currently...
> >>
> >> The model I proposed in my review to patch 1/4 (split remote physical
> >> address from local address pool) allows to avoid this workaround.
> >
> >
> > Having just talked this through with Jacopo I think I see that we have
> > two topics getting intertwined here too.
> >
> >  - Address translation so that we can separate the camera addressing
> >
> >  - our 'device_is_bound/device_for_each_child()' workaround which lets
> >    us make sure the buses are closed before we power on any camera
> >    device.
> >
> >
> > For the upstream process of this driver - I will remove the
> > 'device_is_bound()/device_for_each_child()' workarounds.
> >
> >
> > It is /ugly/ and needs more consideration, but I believe we do still
> > need it (or something similar) for our platform currently.
> >
> >
> >
> > The other side of that is the address translation. I think I was wrong
> > earlier and may have said we have address translation on both sides.
> >
> >
> > I think I now see that we only have some minimal translation for two
> > addresses on the remote (max9271) side, not the local (max9286) side.
>
> Can the remote (max9271) translate addresses for transactions
> originating from the local side? This would make it possible to do a
> proper address translation, although 2 addresses is a quite small amount.
>

Yes, that's true for systems with a single max9286 [1]

We have a system with 2 de-serializers, and what happens is the
following:

The system starts with the following configuration:

1)
                    +------- max9271@40
                    +------- max9271@40
Soc ----> max9286 --+------- max9271@40
                    +------- max9271@40

with a single max9286 it would be easy. We operate on one channel at
the time, do the reprogramming (or set up the translation, for the TI
chip use case) when adding the adapter for the channel, and then we
can talk with all remotes, which now have a different address

2)
                    +-------- max9271@50
                    +--- / -- max9271@40
Soc ----> max9286 --+--- / -- max9271@40
                    +--- / -- max9271@40


                    +--- / -- max9271@50
                    +-------- max9271@51
Soc ----> max9286 --+--- / -- max9271@40
                    +--- / -- max9271@40

                    +--- / -- max9271@50
                    +--- / -- max9271@51
Soc ----> max9286 --+-------- max9271@52
                    +--- / -- max9271@40

                    +--- / -- max9271@50
                    +--- / -- max9271@51
Soc ----> max9286 --+--- / -- max9271@52
                    +-------- max9271@53

Of course, to do the reprogramming, we need to initially send messages
to the default 0x40 address each max9271 boots with. If we don't close
all channels but the one we intend to reprogram, all remotes would
receive the same message, and thus will be re-programmed to the same
address (not nice). [2]

Now, if you have two max9286, installed on the same i2c bus, then you
need to make sure all channels of the 'others' are closed, before you
can reprogram your remotes, otherwise, you would end up reprogramming
all the remotes of the 'others' when trying to reprogram yours, as our
local de-serializers, bounces everything they receives, not directed
to them, to their remote sides.

3)
                       +-------- max9271@50
                       +--- / -- max9271@40
Soc --+-> max9286@4c --+--- / -- max9271@40
      |                +--- / -- max9271@40
      |
      |-> max9286@6c --+-------- max9271@50  <-- not nice
                       +-------- max9271@50
                       +-------- max9271@50
                       +-------- max9271@50

                       +--- / -- max9271@50
                       +-------- max9271@51
Soc --+-> max9286@4c --+--- / -- max9271@40
      |                +--- / -- max9271@40
      |
      |-> max9286@6c --+-------- max9271@51 <-- not nice
                       +-------- max9271@51
                       +-------- max9271@51
                       +-------- max9271@51

....

With the (not nice) 'max9286_is_bound()' we make sure we close all
channels on all max9286 first

4)
                       +--- / -- max9271@40
                       +--- / -- max9271@40
Soc --+-> max9286@4c --+--- / -- max9271@40
      |                +--- / -- max9271@40
      |
      |-> max9286@6c --+--- / -- max9271@40
                       +--- / -- max9271@40
                       +--- / -- max9271@40
                       +--- / -- max9271@40

And then only the last one to probe calls the re-programming
phase for all its fellows de-serializers on the bus.

5)
                       +-------- max9271@50
                       +--- / -- max9271@40
Soc --+-> max9286@4c --+--- / -- max9271@40
      |                +--- / -- max9271@40
      |
      |-> max9286@6c --+--- / -- max9271@40
                       +--- / -- max9271@40
                       +--- / -- max9271@40
                       +--- / -- max9271@40
    ....


                       +--- / -- max9271@50
                       +--- / -- max9271@51
Soc --+-> max9286@4c --+--- / -- max9271@52
      |                +--- / -- max9271@53
      |
      |-> max9286@6c --+-------- max9271@54
                       +--- / -- max9271@40
                       +--- / -- max9271@40
                       +--- / -- max9271@40

When addr reprogramming is done, we enter the image streaming phase,
with all channels open, as now, all remotes, have a different i2c
address assigned.

Suggestions on how to better handle this are very welcome. The point
here is that, to me, this is a gmsl-specific implementation thing.

Do you think for your chips, if they do translations, can you easy mask
them with the i2c address you want (being that specified in the remote
node or selected from an i2c-addr-pool, or something else) without
having to care about others remotes to be accidentally programmed to
an i2c address they're not intended to be assigned to.

Hope this helps clarify your concerns, and I think the actual issue
to discuss, at least on bindings, would be the i2c-address assignment
method, as this impacts GMSL, as well as other implementation that
would use the same binding style as this patches.

Thanks
   j

[1] I still don't get why 'addr translation' >> 'addr reprogramming'.
Even the GMSL application development examples uses addr reprogramming,
so I guess this is how those chips are supposed to work.

[2] If your local side supports address translation, you don't need to
talk with the remote side to 'mask' it, so you don't need this workaround.

> BTW all the TI chips I'm looking at can do address translation but, as
> far as I understand, only when acting as "slave proxy", i.e. when
> attached to the bus master. If the Maxim chips do the same, the "remote
> translation" would be unusable.
>
> > We have the ability to reprogram addresses through, and that's what we
> > are using.
>
> Sadly, it looks pretty much unavoidable...
>
> > There's a lot more local discussion going on here that I may have missed
> > so I hope Jacopo, Niklas, or Laurent may add more here if relevant :)
> >
> >
> >
> >
> >>> That said - I think this section needs to be removed from the upstream
> >>> part at least for now. I think we should probably carry this
> >>> 'workaround' separately.
> >>>
> >>> This part is the core issue that I talked about in my presentation at
> >>> ALS-Japan [0]
> >>>
> >>>  [0] https://sched.co/EaXa
> >>
> >> Oh, interesting, I hadn't noticed that you gave this talk -- at the same
> >> conference as Vladimir's talk! No video recording apparently, but are
> >> slides available at least?
> >
> > Hrm ... I was sure I uploaded to the conference so that they should have
> > been available on that URL - but they are not showing.
> >
> > They are available here:
> >
> > 	https://www.slideshare.net/KieranBingham/gmsl-in-linux
>
> Thanks.
>
> > (Please excuse the incorrect date on the first slide :D)
> >
> > I had put a proposal in to give this talk again at ELCE but
> > unfortunately it didn't get accepted.
> >
> > Seems it really would have been useful to have a slot. Lets hope next
> > ELCE is too late and we work out a good system by then :)
>
> Indeed it would have been!
>
> But hey, The FOSDEM CFPs are still open!
>
> Bye,
> --
> Luca

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH v4 3/4] media: i2c: Add MAX9286 driver
  2018-11-20 17:44             ` jacopo mondi
@ 2018-11-21 10:05               ` Luca Ceresoli
  0 siblings, 0 replies; 26+ messages in thread
From: Luca Ceresoli @ 2018-11-21 10:05 UTC (permalink / raw)
  To: jacopo mondi
  Cc: kieran.bingham, linux-renesas-soc, linux-media, devicetree,
	sakari.ailus, Niklas Söderlund, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Jacopo,

On 20/11/18 18:44, jacopo mondi wrote:
> Hi Luca,
> 
> On Tue, Nov 20, 2018 at 11:51:37AM +0100, Luca Ceresoli wrote:
>> Hi Kieran,
>>
>> On 20/11/18 01:32, Kieran Bingham wrote:
>>> Hi Luca,
>>>
>>> My apologies for my travel induced delay in responding here,
>>
>> No problem.
>>
>>> On 14/11/2018 02:04, Luca Ceresoli wrote:
>> [...]
>>>>>>> +static int max9286_probe(struct i2c_client *client,
>>>>>>> +			 const struct i2c_device_id *did)
>>>>>>> +{
>>>>>>> +	struct max9286_device *dev;
>>>>>>> +	unsigned int i;
>>>>>>> +	int ret;
>>>>>>> +
>>>>>>> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
>>>>>>> +	if (!dev)
>>>>>>> +		return -ENOMEM;
>>>>>>> +
>>>>>>> +	dev->client = client;
>>>>>>> +	i2c_set_clientdata(client, dev);
>>>>>>> +
>>>>>>> +	for (i = 0; i < MAX9286_N_SINKS; i++)
>>>>>>> +		max9286_init_format(&dev->fmt[i]);
>>>>>>> +
>>>>>>> +	ret = max9286_parse_dt(dev);
>>>>>>> +	if (ret)
>>>>>>> +		return ret;
>>>>>>> +
>>>>>>> +	dev->regulator = regulator_get(&client->dev, "poc");
>>>>>>> +	if (IS_ERR(dev->regulator)) {
>>>>>>> +		if (PTR_ERR(dev->regulator) != -EPROBE_DEFER)
>>>>>>> +			dev_err(&client->dev,
>>>>>>> +				"Unable to get PoC regulator (%ld)\n",
>>>>>>> +				PTR_ERR(dev->regulator));
>>>>>>> +		ret = PTR_ERR(dev->regulator);
>>>>>>> +		goto err_free;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	/*
>>>>>>> +	 * We can have multiple MAX9286 instances on the same physical I2C
>>>>>>> +	 * bus, and I2C children behind ports of separate MAX9286 instances
>>>>>>> +	 * having the same I2C address. As the MAX9286 starts by default with
>>>>>>> +	 * all ports enabled, we need to disable all ports on all MAX9286
>>>>>>> +	 * instances before proceeding to further initialize the devices and
>>>>>>> +	 * instantiate children.
>>>>>>> +	 *
>>>>>>> +	 * Start by just disabling all channels on the current device. Then,
>>>>>>> +	 * if all other MAX9286 on the parent bus have been probed, proceed
>>>>>>> +	 * to initialize them all, including the current one.
>>>>>>> +	 */
>>>>>>> +	max9286_i2c_mux_close(dev);
>>>>>>> +
>>>>>>> +	/*
>>>>>>> +	 * The MAX9286 initialises with auto-acknowledge enabled by default.
>>>>>>> +	 * This means that if multiple MAX9286 devices are connected to an I2C
>>>>>>> +	 * bus, another MAX9286 could ack I2C transfers meant for a device on
>>>>>>> +	 * the other side of the GMSL links for this MAX9286 (such as a
>>>>>>> +	 * MAX9271). To prevent that disable auto-acknowledge early on; it
>>>>>>> +	 * will be enabled later as needed.
>>>>>>> +	 */
>>>>>>> +	max9286_configure_i2c(dev, false);
>>>>>>> +
>>>>>>> +	ret = device_for_each_child(client->dev.parent, &client->dev,
>>>>>>> +				    max9286_is_bound);
>>>>>>> +	if (ret)
>>>>>>> +		return 0;
>>>>>>> +
>>>>>>> +	dev_dbg(&client->dev,
>>>>>>> +		"All max9286 probed: start initialization sequence\n");
>>>>>>> +	ret = device_for_each_child(client->dev.parent, NULL,
>>>>>>> +				    max9286_init);
>>>>>>
>>>>>> I can't manage to like this initialization sequence, sorry. If at all
>>>>>> possible, each max9286 should initialize itself independently from each
>>>>>> other, like any normal driver.
>>>>>
>>>>> Yes, I think we're in agreement here, but unfortunately this section is
>>>>> a workaround for the fact that our devices share a common address space.
>>>>>
>>>>> We (currently) *must* disable both devices before we start the
>>>>> initialisation process for either on our platform currently...
>>>>
>>>> The model I proposed in my review to patch 1/4 (split remote physical
>>>> address from local address pool) allows to avoid this workaround.
>>>
>>>
>>> Having just talked this through with Jacopo I think I see that we have
>>> two topics getting intertwined here too.
>>>
>>>  - Address translation so that we can separate the camera addressing
>>>
>>>  - our 'device_is_bound/device_for_each_child()' workaround which lets
>>>    us make sure the buses are closed before we power on any camera
>>>    device.
>>>
>>>
>>> For the upstream process of this driver - I will remove the
>>> 'device_is_bound()/device_for_each_child()' workarounds.
>>>
>>>
>>> It is /ugly/ and needs more consideration, but I believe we do still
>>> need it (or something similar) for our platform currently.
>>>
>>>
>>>
>>> The other side of that is the address translation. I think I was wrong
>>> earlier and may have said we have address translation on both sides.
>>>
>>>
>>> I think I now see that we only have some minimal translation for two
>>> addresses on the remote (max9271) side, not the local (max9286) side.
>>
>> Can the remote (max9271) translate addresses for transactions
>> originating from the local side? This would make it possible to do a
>> proper address translation, although 2 addresses is a quite small amount.
>>
> 
> Yes, that's true for systems with a single max9286 [1]
> 
> We have a system with 2 de-serializers, and what happens is the
> following:
> 
> The system starts with the following configuration:
> 
> 1)
>                     +------- max9271@40
>                     +------- max9271@40
> Soc ----> max9286 --+------- max9271@40
>                     +------- max9271@40
> 
> with a single max9286 it would be easy. We operate on one channel at
> the time, do the reprogramming (or set up the translation, for the TI
> chip use case) when adding the adapter for the channel, and then we
> can talk with all remotes, which now have a different address
> 
> 2)
>                     +-------- max9271@50
>                     +--- / -- max9271@40
> Soc ----> max9286 --+--- / -- max9271@40
>                     +--- / -- max9271@40
> 
> 
>                     +--- / -- max9271@50
>                     +-------- max9271@51
> Soc ----> max9286 --+--- / -- max9271@40
>                     +--- / -- max9271@40
> 
>                     +--- / -- max9271@50
>                     +--- / -- max9271@51
> Soc ----> max9286 --+-------- max9271@52
>                     +--- / -- max9271@40
> 
>                     +--- / -- max9271@50
>                     +--- / -- max9271@51
> Soc ----> max9286 --+--- / -- max9271@52
>                     +-------- max9271@53
> 
> Of course, to do the reprogramming, we need to initially send messages
> to the default 0x40 address each max9271 boots with. If we don't close
> all channels but the one we intend to reprogram, all remotes would
> receive the same message, and thus will be re-programmed to the same
> address (not nice). [2]
> 
> Now, if you have two max9286, installed on the same i2c bus, then you
> need to make sure all channels of the 'others' are closed, before you
> can reprogram your remotes, otherwise, you would end up reprogramming
> all the remotes of the 'others' when trying to reprogram yours, as our
> local de-serializers, bounces everything they receives, not directed
> to them, to their remote sides.

This last sentence is the one point that makes things so hard on the
GMSL chips. In my previous email(s) I partially forgot about this, so I
was hoping  a better implementation could be possible. Thanks for
re-focusing me.

It would have been lovely if the hardware designers had at least put an
i2c mux between the soc and those chatty deserializers... :-\

> 3)
>                        +-------- max9271@50
>                        +--- / -- max9271@40
> Soc --+-> max9286@4c --+--- / -- max9271@40
>       |                +--- / -- max9271@40
>       |
>       |-> max9286@6c --+-------- max9271@50  <-- not nice
>                        +-------- max9271@50
>                        +-------- max9271@50
>                        +-------- max9271@50
> 
>                        +--- / -- max9271@50
>                        +-------- max9271@51
> Soc --+-> max9286@4c --+--- / -- max9271@40
>       |                +--- / -- max9271@40
>       |
>       |-> max9286@6c --+-------- max9271@51 <-- not nice
>                        +-------- max9271@51
>                        +-------- max9271@51
>                        +-------- max9271@51
> 
> ....
> 
> With the (not nice) 'max9286_is_bound()' we make sure we close all
> channels on all max9286 first
> 
> 4)
>                        +--- / -- max9271@40
>                        +--- / -- max9271@40
> Soc --+-> max9286@4c --+--- / -- max9271@40
>       |                +--- / -- max9271@40
>       |
>       |-> max9286@6c --+--- / -- max9271@40
>                        +--- / -- max9271@40
>                        +--- / -- max9271@40
>                        +--- / -- max9271@40
> 
> And then only the last one to probe calls the re-programming
> phase for all its fellows de-serializers on the bus.
> 
> 5)
>                        +-------- max9271@50
>                        +--- / -- max9271@40
> Soc --+-> max9286@4c --+--- / -- max9271@40
>       |                +--- / -- max9271@40
>       |
>       |-> max9286@6c --+--- / -- max9271@40
>                        +--- / -- max9271@40
>                        +--- / -- max9271@40
>                        +--- / -- max9271@40
>     ....
> 
> 
>                        +--- / -- max9271@50
>                        +--- / -- max9271@51
> Soc --+-> max9286@4c --+--- / -- max9271@52
>       |                +--- / -- max9271@53
>       |
>       |-> max9286@6c --+-------- max9271@54
>                        +--- / -- max9271@40
>                        +--- / -- max9271@40
>                        +--- / -- max9271@40
> 
> When addr reprogramming is done, we enter the image streaming phase,
> with all channels open, as now, all remotes, have a different i2c
> address assigned.
> 
> Suggestions on how to better handle this are very welcome. The point
> here is that, to me, this is a gmsl-specific implementation thing.
> 
> Do you think for your chips, if they do translations, can you easy mask
> them with the i2c address you want (being that specified in the remote
> node or selected from an i2c-addr-pool, or something else) without
> having to care about others remotes to be accidentally programmed to
> an i2c address they're not intended to be assigned to.

Yes. The TI chips have a "passthrough-all" option to propagate all
transactions with an unknown address, but it's mostly meant for
debugging. In normal usage the local chip will propagate (with addresses
translated) only transactions coming with a known slave address,
including its own address(es), the remote (de)ser aliases and the remote
chip aliases. All aliases are disabled until programmed.

> Hope this helps clarify your concerns, and I think the actual issue
> to discuss, at least on bindings, would be the i2c-address assignment
> method, as this impacts GMSL, as well as other implementation that
> would use the same binding style as this patches.

Absolutely.

Bye,
-- 
Luca

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

* [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286
  2018-11-14 10:04       ` Luca Ceresoli
@ 2018-11-27 22:47         ` Kieran Bingham
  2018-12-01 18:41           ` Luca Ceresoli
  0 siblings, 1 reply; 26+ messages in thread
From: Kieran Bingham @ 2018-11-27 22:47 UTC (permalink / raw)
  To: Luca Ceresoli, linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Laurent Pinchart, Jacopo Mondi,
	Wolfram Sang

+Wolfram,

Hi Luca,

On 14/11/2018 02:04, Luca Ceresoli wrote:
> Hi Kieran,
> 
> On 14/11/18 00:12, Kieran Bingham wrote:
>> Hi Luca,
>>
>> On 13/11/2018 14:42, Luca Ceresoli wrote:
>>> Hi Kieran, All,
>>>
>>> sorry for joining this late... See below my considerations.
>>
>> I'd say you're on time - not late,
>>
>> Thanks for joining :)
>>
>>>
>>> On 02/11/18 16:47, Kieran Bingham wrote:
>>>> From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>>>
>>>> The MAX9286 deserializes video data received on up to 4 Gigabit
>>>> Multimedia Serial Links (GMSL) and outputs them on a CSI-2 port using up
>>>> to 4 data lanes.
>>>>
>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>>> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
>>>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>>>
>>>> ---
>>>> v3:
>>>>  - Update binding descriptions
>>>>
>>>> v4:
>>>>  - Define the use of a CSI2 D-PHY
>>>>  - Rename pwdn-gpios to enable-gpios (with inverted polarity)
>>>>  - Remove clock-lanes mapping support
>>>>  - rewrap text blocks
>>>>  - Fix typos
>>>> ---
>>>>  .../bindings/media/i2c/maxim,max9286.txt      | 182 ++++++++++++++++++
>>>>  1 file changed, 182 insertions(+)
>>>>  create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>>>> new file mode 100644
>>>> index 000000000000..672f6a4d417d
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>>>> @@ -0,0 +1,182 @@
>>>> +Maxim Integrated Quad GMSL Deserializer
>>>> +---------------------------------------
>>>> +
>>>> +The MAX9286 deserializer receives video data on up to 4 Gigabit Multimedia
>>>> +Serial Links (GMSL) and outputs them on a CSI-2 D-PHY port using up to 4 data
>>>> +lanes.
>>>> +
>>>> +In addition to video data, the GMSL links carry a bidirectional control channel
>>>> +that encapsulates I2C messages. The MAX9286 forwards all I2C traffic not
>>>> +addressed to itself to the other side of the links, where a GMSL serializer
>>>> +will output it on a local I2C bus. In the other direction all I2C traffic
>>>> +received over GMSL by the MAX9286 is output on the local I2C bus.
>>>> +
>>>> +Required Properties:
>>>> +
>>>> +- compatible: Shall be "maxim,max9286"
>>>> +- reg: I2C device address
>>>> +
>>>> +Optional Properties:
>>>> +
>>>> +- poc-supply: Regulator providing Power over Coax to the cameras
>>>> +- enable-gpios: GPIO connected to the #PWDN pin with inverted polarity
>>>> +
>>>> +Required endpoint nodes:
>>>> +-----------------------
>>>> +
>>>> +The connections to the MAX9286 GMSL and its endpoint nodes are modeled using
>>>> +the OF graph bindings in accordance with the video interface bindings defined
>>>> +in Documentation/devicetree/bindings/media/video-interfaces.txt.
>>>> +
>>>> +The following table lists the port number corresponding to each device port.
>>>> +
>>>> +        Port            Description
>>>> +        ----------------------------------------
>>>> +        Port 0          GMSL Input 0
>>>> +        Port 1          GMSL Input 1
>>>> +        Port 2          GMSL Input 2
>>>> +        Port 3          GMSL Input 3
>>>> +        Port 4          CSI-2 Output
>>>> +
>>>> +Optional Endpoint Properties for GMSL Input Ports (Port [0-3]):
>>>> +
>>>> +- remote-endpoint: phandle to the remote GMSL source endpoint subnode in the
>>>> +  remote node port.
>>>> +
>>>> +Required Endpoint Properties for CSI-2 Output Port (Port 4):
>>>> +
>>>> +- remote-endpoint: phandle to the remote CSI-2 sink endpoint node.
>>>> +- data-lanes: array of physical CSI-2 data lane indexes.
>>>> +
>>>> +Required i2c-mux nodes:
>>>> +----------------------
>>>> +
>>>> +Each GMSL link is modeled as a child bus of an i2c bus multiplexer/switch, in
>>>> +accordance with bindings described in
>>>> +Documentation/devicetree/bindings/i2c/i2c-mux.txt. The serializer device on the
>>>> +remote end of the GMSL link shall be modelled as a child node of the
>>>> +corresponding I2C bus.
>>>> +
>>>> +Required i2c child bus properties:
>>>> +- all properties described as required i2c child bus nodes properties in
>>>> +  Documentation/devicetree/bindings/i2c/i2c-mux.txt.
>>>> +
>>>> +Example:
>>>> +-------
>>>> +
>>>> +	gmsl-deserializer@2c {
>>>> +		compatible = "maxim,max9286";Not at all late - Just on time 
>>>> +		reg = <0x2c>;
>>>> +		poc-supply = <&camera_poc_12v>;
>>>> +		enable-gpios = <&gpio 13 GPIO_ACTIVE_LOW>;
>>>> +
>>>> +		#address-cells = <1>;
>>>> +		#size-cells = <0>;
>>>> +
>>>> +		ports {
>>>> +			#address-cells = <1>;
>>>> +			#size-cells = <0>;
>>>> +
>>>> +			port@0 {
>>>> +				reg = <0>;
>>>> +				max9286_in0: endpoint {
>>>> +					remote-endpoint = <&rdacm20_out0>;
>>>> +				};
>>>> +			};
>>>> +
>>>> +			port@1 {
>>>> +				reg = <1>;
>>>> +				max9286_in1: endpoint {
>>>> +					remote-endpoint = <&rdacm20_out1>;
>>>> +				};
>>>> +			};
>>>> +
>>>> +			port@2 {
>>>> +				reg = <2>;
>>>> +				max9286_in2: endpoint {
>>>> +					remote-endpoint = <&rdacm20_out2>;
>>>> +				};
>>>> +			};
>>>> +
>>>> +			port@3 {
>>>> +				reg = <3>;
>>>> +				max9286_in3: endpoint {
>>>> +					remote-endpoint = <&rdacm20_out3>;
>>>> +				};
>>>> +			};
>>>> +
>>>> +			port@4 {
>>>> +				reg = <4>;
>>>> +				max9286_out: endpoint {
>>>> +					data-lanes = <1 2 3 4>;
>>>> +					remote-endpoint = <&csi40_in>;
>>>> +				};
>>>> +			};
>>>> +		};
>>>> +
>>>> +		i2c@0 {
>>>> +			#address-cells = <1>;
>>>> +			#size-cells = <0>;
>>>> +			reg = <0>;
>>>> +
>>>> +			camera@51 {
>>>> +				compatible = "imi,rdacm20";
>>>> +				reg = <0x51 0x61>;
>>>
>>> I find this kind of address mapping is the weak point in this patchset.
>>>
>>> The ser-deser chipset splits the world in "local" and "remote" side. The
>>> camera node belongs to the remote side, but the 0x51 and 0x61 addresses
>>> belong to the local side.
>>
>> Well, in our use case - in fact the camera has a set of fixed addresses
>> (0x30,0x40,0x50) for each camera - and these are the addresses we are
>> requesting the camera to be updated to. Once the camera is communicated
>> with - the first step is to reprogram the device to respond to the
>> addresses specified here.
> 
> Yes, the way it works is clear.
> 
>>> Think about supporting N different main boards
>>> and M remote boards. 0x51 might be available on some main boards but not
>>> all. IMO under the camera@51 (even the i2c@0) node there should be only
>>> remote hardware description. To support the N*M possible combinations,
>>> there should be:
>>
>> Of course - well in fact all of our I2C addresses across our two max9286
>> instances, and 8 camera devices share the same bus 'address space'.
>>
>> It's crucial to provide this address on a per board level, which is why
>> it is specified in the DT.
>>
>> I wonder if perhaps it was a mistake to include the camera description
>> in this part of the example, as it's not related to the max9286
>> specifically.
> 
> Interesting point. In my case I'm thinking DT overlays, they help me a
> lot in finding a proper generalization. With some generalization, camera
> modules [the same would happen with display modules] are similar to
> beaglebone capes or rpi hats:
> 

I agree - I too envisage that the camera's should be able to be
described as overlays.

In fact we have tried to model our expansion boards as if they are
overlays (although they are just direct 'include files' at the moment
rather than real overlays)

An example for the v3m is viewable at:

https://git.kernel.org/pub/scm/linux/kernel/git/kbingham/rcar.git/commit/?h=gmsl/v5&id=643d80a12edde3d7bd95ad7eff360ea820591430



Laurent has brought up an interesting topic here, that there is support
(or soon to be support) for overlay GPIO mappings, which we might be
able to translate to do I2C address mappings to help us.

(I look forward to someone posting a relevant link here:)


(re-edit: I think Wolfram has said this wouldn't be so easily applicable
to i2c)


>  1. there can be different camera modules being designed over time
>  2. there can be different base boards being designed over time
>  3. there is a standard interconnection between them (mechanical,
>     electrical, communication bus)
>  4. camera modules and base boards are designed and sold independently
>     (thanks to point 3)
> 
> Overlays are a natural choice in this case. Even bootloader-time
> overlays will suffice for my reasoning, let's remove the hotplug mess
> from this discussion.

Ok.

> Now, in this patch you are modeling the remote camera as if it were a
> "normal" I2C device, except:
> 
>  a) it has 2 slave addresses (no problem with this)
>  b) the 2 slave addresses in DT are not the physical ones
> 
> With this model it seems natural to write "camera@51/reg = <0x51 0x61>"
> in the camera DT overlay. Except 0x51 and 0x61 do not exist on the
> camera module, those numbers come from the base board, since you know
> those two addresses are not used on the bus where gmsl-deserializer@2c
> is. But it works.
> 
> Then one year later a random SBC vendor starts selling a new base board
> that has on the same i2c bus a GMSL deser and a random i2c chip,
> unrelated to cameras, at address 0x51. Bang, the camera sensor does not
> work anymore, but there is no hardware reason for it not to work. Well,
> easy to fix, find an address that is unused on all known base boards and
> replace, say, 0x51->0x71 in the camera overlay. (OK, I violated the "DT
> as a stable ABI" principle)
> 
> But then other boards appear and, taking this to an extreme, you can get
> to a situation where every i2c address is used on at least one board.
> How do you fix that?
> 
> Maybe this scenario is a bit too apocalyptic, and maybe too much for
> current automotive uses, but I think it illustrates how the current
> model is not generic enough. Since there is no existing code in the
> kernel yet, I think we should strive to do better in order to minimize
> future problems.

I like apocalyptic thinking :)

Yes, it's probably more than the automotive use case that we currently
have - but it's good to think this through now.


Lets find out of the DT mapping system Laurent has just talked about
(locally) can help us here.

or Bringing in Wolfram, as I think he sees some merit in the address
pool definitions.


> My approach is instead to clearly split the local and remote domain. The
> latter is what could be moved to an overlay. For example:
> 
> &i2c0 {
>     serializer@3d {
>         reg = <0x3d>;
>         ...
> 
>         /* Guaranteed not physically present on i2c0 */
>         i2c-alias-pool = /bits/ 16 <0x20 0x21 0x22 0x23 0x24 0x25>;
> 
>         i2c-mux {
>             #address-cells = <1>;
>             #size-cells = <0>;
> 
> 	    i2c@0 {
>                 reg = <0>;
>                 #address-cells = <1>;
>                 #size-cells = <0>;
> 
>                 // ------8<------ this could be moved to an overlay
>                 sensor@50 {
>                     reg = <0x50>;
>                     ...
>                     endpoint {...};
>                 };
>                 eeprom@51 {
>                     reg = <0x51>;
>                     ...
>                 };
>                 // ------8<------
>             };
> 
> 	    i2c@1 {
>                 reg = <1>;
>                 #address-cells = <1>;
>                 #size-cells = <0>;
> 
>                 // ------8<------
>                 sensor@50 {
>                     reg = <0x50>;
>                     ...
>                     endpoint {...};
>                 };
>                 eeprom@51 {
>                     reg = <0x51>;
>                     ...
>                 };
>                 // ------8<------
>             };
>         };
>     };
> };
> 
> The core difference is that I split the camera@51/reg property in two:
> 
>  * sensor@50/reg: the remote side (camera overlay);
>    carries the physical i2c address (note both sensors are at 0x50)
>  * serializer@3d/i2c-alias-pool: the local side (base board);
>    lists a pool of addresses that are not used on the i2c bus
> 
> See how there is no mixing between local and remote. The pool will
> differ from one base board to another.
> 
> To implement this, I developed an "i2c address translator" that maps
> physical remote addresses to local addresses from the pool at runtime.
> It still needs some work, but address translation it is working.
> 
>>>  * a DT for the main board mentioning only addresses for the
>>>    local i2c bus, down to the i2c@0 with address-cells, size-cells and
>>>    reg properties
>>>  * a DT overlay for each remote board, mentioning the remote i2c
>>>    chips with their physical addresses, but no local addresses
>>>
>>> The only way I could devise to be generic is to bind each physical
>>> remote address to a local address at runtime.
>>>
>>> Also, to be implemented reliably, an address translation feature is
>>> required on the local (de)ser chip.
>>>
>>> So the question is: can the max9286 chip do i2c address translation?
>>>
>>
>> Yes, The max9286 (deser) can do i2c address translation - but so too can
>> the max9271 (serialiser)
> 
> Good!
> 
>> We do our address translation on the camera (serialiser) side.
> 
> By "address translation" I mean the i2c address is changed by some
> device in the middle between the i2c master and the slave. In this sense
> you are not doing address translation, you are rather modifying the chip
> addresses. Then transactions happen with the new (0x51/0x61) address,
> which does not get modified during subsequent transactions.
> 
>> The cameras *all* boot with the same i2c address (and thus all conflict)
>>  - We disable all links
>>  - We enable /one/ link
>>  - We initialise and reprogram the address of that camera to the address
>>    specified in the camera node. - Then we move to the next camera.
>>
>> The reality is we 'just need' a spare address on the I2C bus - but as
>> yet - there is no mechanism in I2C core to request a spare address.
> 
> Not a reliable one, definitely, since there could be i2c devices unknown
> to the software. This is why I had to introduce the alias pool: the DT
> writer is required to know which addresses are available and list them
> in DT.

Ok - so in this model - even if we don't do address translation - we
could still 'request' the address from the allocator, and use that to
set the new 'physical' address?

I think if we were to use the i2c-alias-pool for our system we'd
probably want to put a pool at each i2c-mux-bus, to ensure that cameras
had addresses that were recognisable / identifiable perhaps:

&i2c0 {
    serializer@3d {
        reg = <0x3d>;
        ...

        i2c-mux {
	    i2c@0 {
                reg = <0>;
                #address-cells = <1>;
                #size-cells

	        i2c-alias-pool = /bits/ 16 <0x31 0x41 0x51>;
	    }

	    i2c@1 {
                reg = <1>;
                #address-cells = <1>;
                #size-cells

	        i2c-alias-pool = /bits/ 16 <0x32 0x42 0x52>;
	    }
	}
    }
}

or such ... but it seems feasible ... ?


-- 
Regards
--
Kieran



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

* Re: [PATCH v4 4/4] media: i2c: Add RDACM20 driver
  2018-11-20  8:34   ` Sakari Ailus
@ 2018-11-28 12:51     ` Kieran Bingham
  2018-12-20 23:11       ` Sakari Ailus
  0 siblings, 1 reply; 26+ messages in thread
From: Kieran Bingham @ 2018-11-28 12:51 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-renesas-soc, linux-media, devicetree,
	Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hi Sakari,

Thank you for your review,

On 20/11/2018 08:34, Sakari Ailus wrote:
> Hi Kieran,
> 
> On Fri, Nov 02, 2018 at 03:47:23PM +0000, Kieran Bingham wrote:
>> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>
>> 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.
>>
>> 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
>> ---
>>  MAINTAINERS                         |  10 +
>>  drivers/media/i2c/Kconfig           |  11 +
>>  drivers/media/i2c/Makefile          |   1 +
>>  drivers/media/i2c/rdacm20-ov10635.h | 953 ++++++++++++++++++++++++++++
>>  drivers/media/i2c/rdacm20.c         | 635 ++++++++++++++++++
>>  5 files changed, 1610 insertions(+)
>>  create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
>>  create mode 100644 drivers/media/i2c/rdacm20.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 745f0fd1fff1..26ef20087a43 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -12230,6 +12230,16 @@ S:	Supported
>>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
>>  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>
> 
> I'm happy to see this will be well maintained. :-)

Well it means /someone/ should always be able to pick it up :D

I didn't know who to put here - so I put all of the current blamees ;)
We've all put a lot of time and work in to the GMSL bring up and
refactoring.

If you think it's overkill, I can reduce the names. Same on max9286.


>> +L:	linux-media@vger.kernel.org
>> +S:	Maintained
>> +F:	Documentation/devicetree/bindings/media/i2c/rdacm20.txt
>> +F:	drivers/media/i2c/rdacm20*
>> +
>>  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 eadc00bdd3bf..5eded5e337ec 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -989,6 +989,17 @@ config VIDEO_S5C73M3
>>  	  This is a V4L2 sensor driver for Samsung S5C73M3
>>  	  8 Mpixel camera.
>>  
>> +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"
>>  
>>  config VIDEO_ADP1653
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index 4de7fe62b179..121d28283d45 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -111,5 +111,6 @@ obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
>>  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_SDR_MAX2175) += max2175.o
>> 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
> 
> A part of this driver looks like a driver for an OV camera sensor. Would
> there be something that prevents separating the camera sensor driver from
> this one?

I don't think there's anything preventing it - except (a fair bit of)
development time.

We also have the RDACM21 to support, which uses the max9271 and an
OV10640. At that time - this will absolutely have to be split. We
shouldn't replicate the max9271 code.

I mentioned briefly in the cover letter:

> Further anticipated work in this area includes supporting the RDACM21
> camera, at which point the RDACM20 will be adapted to separate out the
> MAX9271 and the OV10635 sensor components.

But to get more dedicated time to work this - we need to show some
progress on GMSL up-streaming, so the max9286 and bindings take priority
for now.

A little bit catch 22 ... :D

I'm sure there will be overlap between GMSL and FPD-Link with the RDACM
range [0] of cameras too, which also provide TI-FPD Link serialisers.

I currently envisage that we would have an RDACM20 'driver' which would
know that it has a max9271 serialiser and an OV10635 sensor, and would
handle the links of any subdevices internally.

As the RDACM20 is an object itself, I think this makes sense ... unless
anyone suggests that each part should be broken down into the DT
directly ? (I think that would possibly be a bit too much)


[0]
https://www.global-imi.com/sites/default/files/Generic-Minicube-Catalogue-1020151.pdf


>> +
>> +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..d96b2eb5ab1b
>> --- /dev/null
>> +++ b/drivers/media/i2c/rdacm20.c
>> @@ -0,0 +1,635 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * IMI RDACM20 GMSL Camera Driver
>> + *
>> + * 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.
>> + */
>> +
>> +/*
>> + * The camera is mode 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"
>> +
>> +#define RDACM20_SENSOR_HARD_RESET
>> +
>> +#define MAX9271_I2C_ADDRESS		0x40
>> +
>> +/* 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_SETGPO			BIT(0)
>> +/* Register 0x15 */
>> +#define MAX9271_PCLKDET			BIT(0)
>> +
>> +#define MAXIM_I2C_I2C_SPEED_400KHZ	MAX9271_I2CMSTBT_339KBPS
>> +#define MAXIM_I2C_I2C_SPEED_100KHZ	MAX9271_I2CMSTBT_105KBPS
>> +#define MAXIM_I2C_SPEED			MAXIM_I2C_I2C_SPEED_100KHZ
>> +
>> +#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
>> +/* #define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY10_2X10 */
>> +
>> +struct rdacm20_device {
>> +	struct i2c_client		*client;
>> +	struct i2c_client		*sensor;
>> +	struct v4l2_subdev		sd;
>> +	struct media_pad		pad;
>> +	struct v4l2_ctrl_handler	ctrls;
>> +};
>> +
>> +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 max9271_read(struct rdacm20_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 rdacm20_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;
>> +}
>> +
>> +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->client->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->client->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->client->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->client->dev,
>> +				"%s: register %u (0x%04x) write failed (%d)\n",
>> +				__func__, i, regs[i].reg, ret);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * rdacm20_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 rdacm20_pclk_detect(struct rdacm20_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;
>> +}
>> +
>> +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
>> +{
>> +	struct rdacm20_device *dev = sd_to_rdacm20(sd);
>> +	int ret;
>> +
>> +	if (enable) {
>> +		ret = rdacm20_pclk_detect(dev);
>> +		if (ret)
>> +			return ret;
>> +
>> +		/* Enable the serial link. */
>> +		max9271_write(dev, 0x04, MAX9271_SEREN | MAX9271_REVCCEN |
>> +			      MAX9271_FWDCCEN);
>> +	} else {
>> +		/* Disable the serial link. */
>> +		max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
>> +			      MAX9271_FWDCCEN);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int rdacm20_g_mbus_config(struct v4l2_subdev *sd,
>> +				 struct v4l2_mbus_config *cfg)
>> +{
>> +	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
>> +		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
>> +	cfg->type = V4L2_MBUS_CSI2_DPHY;
> 
> Hmm. What are you using g_mbus_config() for?

Good point here ... I assumed it was passed through up to the VIN - but
it's really not applicable here.

Or if it is - then it should be describing the GMSL bus link!

I'll bet this isn't even getting called and can likely be removed.


> 
>> +
>> +	return 0;
>> +}
>> +
>> +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,
>> +	.g_mbus_config	= rdacm20_g_mbus_config,
>> +};
>> +
>> +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 max9271_configure_i2c(struct rdacm20_device *dev)
>> +{
>> +	/*
>> +	 * Configure the I2C bus:
>> +	 *
>> +	 * - Enable high thresholds on the reverse channel
>> +	 * - Disable artificial ACK and set I2C speed
>> +	 */
>> +	max9271_write(dev, 0x08, MAX9271_REV_HIVTH);
>> +	usleep_range(5000, 8000);
>> +
>> +	max9271_write(dev, 0x0d, MAX9271_I2CSLVSH_469NS_234NS |
>> +		      MAX9271_I2CSLVTO_1024US | MAXIM_I2C_SPEED);
>> +	usleep_range(5000, 8000);
>> +
>> +	return 0;
>> +}
>> +
>> +static int max9271_configure_gmsl_link(struct rdacm20_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);
>> +
>> +	return 0;
>> +}
>> +
>> +static int max9271_verify_id(struct rdacm20_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;
>> +}
>> +
>> +static int max9271_configure_address(struct rdacm20_device *dev, u8 addr)
>> +{
>> +	int ret;
>> +
>> +	/* Change the MAX9271 I2C address. */
>> +	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;
>> +	}
>> +	dev->client->addr = addr;
>> +	usleep_range(3500, 5000);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rdacm20_initialize(struct rdacm20_device *dev)
>> +{
>> +	u32 addrs[2];
>> +	int ret;
>> +
>> +	ret = of_property_read_u32_array(dev->client->dev.of_node, "reg",
>> +					 addrs, ARRAY_SIZE(addrs));
>> +	if (ret < 0) {
>> +		dev_err(&dev->client->dev, "Invalid DT reg property\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/*
>> +	 * FIXME: The MAX9271 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 = MAX9271_I2C_ADDRESS;
>> +
>> +	/* Verify communication with the MAX9271. */
>> +	i2c_smbus_read_byte(dev->client);	/* ping to wake-up */
>> +
>> +	/*
>> +	 *  Ensure that we have a good link configuration before attempting to
>> +	 *  identify the device.
>> +	 */
>> +	max9271_configure_i2c(dev);
>> +	max9271_configure_gmsl_link(dev);
>> +
>> +	ret = max9271_verify_id(dev);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = max9271_configure_address(dev, addrs[0]);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Reset and verify communication with the OV10635. */
>> +#ifdef RDACM20_SENSOR_HARD_RESET
>> +	/* Cycle the OV10635 reset signal connected to the MAX9271 GPIO1. */
>> +	max9271_write(dev, 0x0f, 0xff & ~(MAX9271_GPIO1OUT | MAX9271_SETGPO));
>> +	mdelay(10);
>> +	max9271_write(dev, 0x0f, 0xff & ~MAX9271_SETGPO);
>> +	mdelay(10);
> 
> Do you need a busy loop? Could you use msleep()?

Checkpatch warns here:

WARNING: msleep < 20ms can sleep for up to 20ms; see
Documentation/timers/timers-howto.txt
#10: FILE: drivers/media/i2c/rdacm20.c:461:
+       msleep(10);

I think for this context, msleep(10) even with the warning is fine here,
but perhaps we can meet that with a usleep_range(10000, 20000); too.


>> +#else
>> +	/* Perform a software reset. */
>> +	ret = ov10635_write(dev, OV10635_SOFTWARE_RESET, 1);
>> +	if (ret < 0) {
>> +		dev_err(&dev->client->dev, "OV10635 reset failed (%d)\n", ret);
>> +		return -ENXIO;
>> +	}
>> +
>> +	udelay(100);
>> +#endif
>> +
>> +	ret = ov10635_read16(dev, OV10635_PID);
>> +	if (ret < 0) {
>> +		dev_err(&dev->client->dev, "OV10635 ID read failed (%d)\n",
>> +			ret);
>> +		return -ENXIO;
>> +	}
>> +
>> +	if (ret != OV10635_VERSION) {
>> +		dev_err(&dev->client->dev, "OV10635 ID mismatch (0x%04x)\n",
>> +			ret);
>> +		return -ENXIO;
>> +	}
>> +
>> +	dev_info(&dev->client->dev, "Identified MAX9271 + OV10635 device\n");
>> +
>> +	/* Change the sensor I2C address. */
>> +	ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
>> +			    (addrs[1] << 1) | OV10635_SC_CMMN_SCCB_ID_SELECT);
>> +	if (ret < 0) {
>> +		dev_err(&dev->client->dev,
>> +			"OV10635 I2C address change failed (%d)\n", ret);
>> +		return ret;
>> +	}
>> +	dev->sensor->addr = addrs[1];
>> +	usleep_range(3500, 5000);
>> +
>> +	/* Program the 0V10635 initial configuration. */
>> +	ret = ov10635_set_regs(dev, ov10635_regs_wizard,
>> +			       ARRAY_SIZE(ov10635_regs_wizard));
> 
> return ov...(); ?

Yes, that would be nicer.

> 
>> +	if (ret)
>> +		return ret;
>> +
>> +	return 0;
>> +}
>> +
>> +static int rdacm20_probe(struct i2c_client *client,
>> +			 const struct i2c_device_id *did)
>> +{
>> +	struct rdacm20_device *dev;
>> +	struct fwnode_handle *ep;
>> +	int ret;
>> +
>> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> 
> You could use devm_kzalloc().

Will change.

> 
>> +	if (!dev)
>> +		return -ENOMEM;
>> +
>> +	dev->client = client;
>> +
>> +	/* Create the dummy I2C client for the sensor. */
>> +	dev->sensor = i2c_new_dummy(client->adapter, OV10635_I2C_ADDRESS);
>> +	if (!dev->sensor) {
>> +		ret = -ENXIO;
>> +		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;
> 
> |=, as in the other patch.

Bah - yes :)


> 
>> +
>> +	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;
>> +
>> +	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(ep);
>> +error:
> 
> You're missing v4l2_ctrl_handler_free() here.

Good spot. Thanks

> 
>> +	media_entity_cleanup(&dev->sd.entity);
>> +	if (dev->sensor)
>> +		i2c_unregister_device(dev->sensor);
>> +	kfree(dev);
>> +
>> +	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);
> 
> As well as here.


Ack.


> 
>> +	media_entity_cleanup(&dev->sd.entity);
>> +	i2c_unregister_device(dev->sensor);
>> +	kfree(dev);
>> +
>> +	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 i2c_device_id rdacm20_id[] = {
>> +	{ "rdacm20", 0 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, rdacm20_id);
>> +
>> +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		= rdacm20_probe,
> 
> Could you use probe_new, so you could remove the i2c ID table? Or do you
> need that for something?

I believe probe_new is probably fine.

I should really resurrect my i2c-probe-coccinelle patch and get that
conversion task done, so we can get to removing and replacing .probe :)

(note to self ... starting projects when unemployed becomes difficult to
continue when someone else gives you projects to work on all the time ...)


> 
>> +	.remove		= rdacm20_remove,
>> +	.shutdown	= rdacm20_shutdown,
>> +	.id_table	= rdacm20_id,
>> +};
>> +
>> +module_i2c_driver(rdacm20_i2c_driver);
>> +
>> +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
>> +MODULE_AUTHOR("Vladimir Barinov");
>> +MODULE_LICENSE("GPL");

Changes described above made and tested on a *single MAX9286* capturing
4 cameras simultaneously, now on my rcar.git gmsl/v5 branch ... :D


-- 
Regards
--
Kieran

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

* Re: [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286
  2018-11-27 22:47         ` Kieran Bingham
@ 2018-12-01 18:41           ` Luca Ceresoli
  0 siblings, 0 replies; 26+ messages in thread
From: Luca Ceresoli @ 2018-12-01 18:41 UTC (permalink / raw)
  To: kieran.bingham, linux-renesas-soc, linux-media, devicetree, sakari.ailus
  Cc: Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Laurent Pinchart, Jacopo Mondi,
	Wolfram Sang

Hi Kieran,

On 27/11/18 23:47, Kieran Bingham wrote:
> +Wolfram,
> 
> Hi Luca,
> 
> On 14/11/2018 02:04, Luca Ceresoli wrote:
>> Hi Kieran,
>>
>> On 14/11/18 00:12, Kieran Bingham wrote:
>>> Hi Luca,
>>>
>>> On 13/11/2018 14:42, Luca Ceresoli wrote:
>>>> Hi Kieran, All,
>>>>
>>>> sorry for joining this late... See below my considerations.
>>>
>>> I'd say you're on time - not late,
>>>
>>> Thanks for joining :)
>>>
>>>>
>>>> On 02/11/18 16:47, Kieran Bingham wrote:
>>>>> From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>>>>
>>>>> The MAX9286 deserializes video data received on up to 4 Gigabit
>>>>> Multimedia Serial Links (GMSL) and outputs them on a CSI-2 port using up
>>>>> to 4 data lanes.
>>>>>
>>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
>>>>> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
>>>>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>>>>
>>>>> ---
>>>>> v3:
>>>>>  - Update binding descriptions
>>>>>
>>>>> v4:
>>>>>  - Define the use of a CSI2 D-PHY
>>>>>  - Rename pwdn-gpios to enable-gpios (with inverted polarity)
>>>>>  - Remove clock-lanes mapping support
>>>>>  - rewrap text blocks
>>>>>  - Fix typos
>>>>> ---
>>>>>  .../bindings/media/i2c/maxim,max9286.txt      | 182 ++++++++++++++++++
>>>>>  1 file changed, 182 insertions(+)
>>>>>  create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>>>>> new file mode 100644
>>>>> index 000000000000..672f6a4d417d
>>>>> --- /dev/null
>>>>> +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.txt
>>>>> @@ -0,0 +1,182 @@
>>>>> +Maxim Integrated Quad GMSL Deserializer
>>>>> +---------------------------------------
>>>>> +
>>>>> +The MAX9286 deserializer receives video data on up to 4 Gigabit Multimedia
>>>>> +Serial Links (GMSL) and outputs them on a CSI-2 D-PHY port using up to 4 data
>>>>> +lanes.
>>>>> +
>>>>> +In addition to video data, the GMSL links carry a bidirectional control channel
>>>>> +that encapsulates I2C messages. The MAX9286 forwards all I2C traffic not
>>>>> +addressed to itself to the other side of the links, where a GMSL serializer
>>>>> +will output it on a local I2C bus. In the other direction all I2C traffic
>>>>> +received over GMSL by the MAX9286 is output on the local I2C bus.
>>>>> +
>>>>> +Required Properties:
>>>>> +
>>>>> +- compatible: Shall be "maxim,max9286"
>>>>> +- reg: I2C device address
>>>>> +
>>>>> +Optional Properties:
>>>>> +
>>>>> +- poc-supply: Regulator providing Power over Coax to the cameras
>>>>> +- enable-gpios: GPIO connected to the #PWDN pin with inverted polarity
>>>>> +
>>>>> +Required endpoint nodes:
>>>>> +-----------------------
>>>>> +
>>>>> +The connections to the MAX9286 GMSL and its endpoint nodes are modeled using
>>>>> +the OF graph bindings in accordance with the video interface bindings defined
>>>>> +in Documentation/devicetree/bindings/media/video-interfaces.txt.
>>>>> +
>>>>> +The following table lists the port number corresponding to each device port.
>>>>> +
>>>>> +        Port            Description
>>>>> +        ----------------------------------------
>>>>> +        Port 0          GMSL Input 0
>>>>> +        Port 1          GMSL Input 1
>>>>> +        Port 2          GMSL Input 2
>>>>> +        Port 3          GMSL Input 3
>>>>> +        Port 4          CSI-2 Output
>>>>> +
>>>>> +Optional Endpoint Properties for GMSL Input Ports (Port [0-3]):
>>>>> +
>>>>> +- remote-endpoint: phandle to the remote GMSL source endpoint subnode in the
>>>>> +  remote node port.
>>>>> +
>>>>> +Required Endpoint Properties for CSI-2 Output Port (Port 4):
>>>>> +
>>>>> +- remote-endpoint: phandle to the remote CSI-2 sink endpoint node.
>>>>> +- data-lanes: array of physical CSI-2 data lane indexes.
>>>>> +
>>>>> +Required i2c-mux nodes:
>>>>> +----------------------
>>>>> +
>>>>> +Each GMSL link is modeled as a child bus of an i2c bus multiplexer/switch, in
>>>>> +accordance with bindings described in
>>>>> +Documentation/devicetree/bindings/i2c/i2c-mux.txt. The serializer device on the
>>>>> +remote end of the GMSL link shall be modelled as a child node of the
>>>>> +corresponding I2C bus.
>>>>> +
>>>>> +Required i2c child bus properties:
>>>>> +- all properties described as required i2c child bus nodes properties in
>>>>> +  Documentation/devicetree/bindings/i2c/i2c-mux.txt.
>>>>> +
>>>>> +Example:
>>>>> +-------
>>>>> +
>>>>> +	gmsl-deserializer@2c {
>>>>> +		compatible = "maxim,max9286";Not at all late - Just on time 
>>>>> +		reg = <0x2c>;
>>>>> +		poc-supply = <&camera_poc_12v>;
>>>>> +		enable-gpios = <&gpio 13 GPIO_ACTIVE_LOW>;
>>>>> +
>>>>> +		#address-cells = <1>;
>>>>> +		#size-cells = <0>;
>>>>> +
>>>>> +		ports {
>>>>> +			#address-cells = <1>;
>>>>> +			#size-cells = <0>;
>>>>> +
>>>>> +			port@0 {
>>>>> +				reg = <0>;
>>>>> +				max9286_in0: endpoint {
>>>>> +					remote-endpoint = <&rdacm20_out0>;
>>>>> +				};
>>>>> +			};
>>>>> +
>>>>> +			port@1 {
>>>>> +				reg = <1>;
>>>>> +				max9286_in1: endpoint {
>>>>> +					remote-endpoint = <&rdacm20_out1>;
>>>>> +				};
>>>>> +			};
>>>>> +
>>>>> +			port@2 {
>>>>> +				reg = <2>;
>>>>> +				max9286_in2: endpoint {
>>>>> +					remote-endpoint = <&rdacm20_out2>;
>>>>> +				};
>>>>> +			};
>>>>> +
>>>>> +			port@3 {
>>>>> +				reg = <3>;
>>>>> +				max9286_in3: endpoint {
>>>>> +					remote-endpoint = <&rdacm20_out3>;
>>>>> +				};
>>>>> +			};
>>>>> +
>>>>> +			port@4 {
>>>>> +				reg = <4>;
>>>>> +				max9286_out: endpoint {
>>>>> +					data-lanes = <1 2 3 4>;
>>>>> +					remote-endpoint = <&csi40_in>;
>>>>> +				};
>>>>> +			};
>>>>> +		};
>>>>> +
>>>>> +		i2c@0 {
>>>>> +			#address-cells = <1>;
>>>>> +			#size-cells = <0>;
>>>>> +			reg = <0>;
>>>>> +
>>>>> +			camera@51 {
>>>>> +				compatible = "imi,rdacm20";
>>>>> +				reg = <0x51 0x61>;
>>>>
>>>> I find this kind of address mapping is the weak point in this patchset.
>>>>
>>>> The ser-deser chipset splits the world in "local" and "remote" side. The
>>>> camera node belongs to the remote side, but the 0x51 and 0x61 addresses
>>>> belong to the local side.
>>>
>>> Well, in our use case - in fact the camera has a set of fixed addresses
>>> (0x30,0x40,0x50) for each camera - and these are the addresses we are
>>> requesting the camera to be updated to. Once the camera is communicated
>>> with - the first step is to reprogram the device to respond to the
>>> addresses specified here.
>>
>> Yes, the way it works is clear.
>>
>>>> Think about supporting N different main boards
>>>> and M remote boards. 0x51 might be available on some main boards but not
>>>> all. IMO under the camera@51 (even the i2c@0) node there should be only
>>>> remote hardware description. To support the N*M possible combinations,
>>>> there should be:
>>>
>>> Of course - well in fact all of our I2C addresses across our two max9286
>>> instances, and 8 camera devices share the same bus 'address space'.
>>>
>>> It's crucial to provide this address on a per board level, which is why
>>> it is specified in the DT.
>>>
>>> I wonder if perhaps it was a mistake to include the camera description
>>> in this part of the example, as it's not related to the max9286
>>> specifically.
>>
>> Interesting point. In my case I'm thinking DT overlays, they help me a
>> lot in finding a proper generalization. With some generalization, camera
>> modules [the same would happen with display modules] are similar to
>> beaglebone capes or rpi hats:
>>
> 
> I agree - I too envisage that the camera's should be able to be
> described as overlays.
> 
> In fact we have tried to model our expansion boards as if they are
> overlays (although they are just direct 'include files' at the moment
> rather than real overlays)
> 
> An example for the v3m is viewable at:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/kbingham/rcar.git/commit/?h=gmsl/v5&id=643d80a12edde3d7bd95ad7eff360ea820591430
> 
> 
> 
> Laurent has brought up an interesting topic here, that there is support
> (or soon to be support) for overlay GPIO mappings, which we might be
> able to translate to do I2C address mappings to help us.
> 
> (I look forward to someone posting a relevant link here:)
> 
> 
> (re-edit: I think Wolfram has said this wouldn't be so easily applicable
> to i2c)
> 
> 
>>  1. there can be different camera modules being designed over time
>>  2. there can be different base boards being designed over time
>>  3. there is a standard interconnection between them (mechanical,
>>     electrical, communication bus)
>>  4. camera modules and base boards are designed and sold independently
>>     (thanks to point 3)
>>
>> Overlays are a natural choice in this case. Even bootloader-time
>> overlays will suffice for my reasoning, let's remove the hotplug mess
>> from this discussion.
> 
> Ok.
> 
>> Now, in this patch you are modeling the remote camera as if it were a
>> "normal" I2C device, except:
>>
>>  a) it has 2 slave addresses (no problem with this)
>>  b) the 2 slave addresses in DT are not the physical ones
>>
>> With this model it seems natural to write "camera@51/reg = <0x51 0x61>"
>> in the camera DT overlay. Except 0x51 and 0x61 do not exist on the
>> camera module, those numbers come from the base board, since you know
>> those two addresses are not used on the bus where gmsl-deserializer@2c
>> is. But it works.
>>
>> Then one year later a random SBC vendor starts selling a new base board
>> that has on the same i2c bus a GMSL deser and a random i2c chip,
>> unrelated to cameras, at address 0x51. Bang, the camera sensor does not
>> work anymore, but there is no hardware reason for it not to work. Well,
>> easy to fix, find an address that is unused on all known base boards and
>> replace, say, 0x51->0x71 in the camera overlay. (OK, I violated the "DT
>> as a stable ABI" principle)
>>
>> But then other boards appear and, taking this to an extreme, you can get
>> to a situation where every i2c address is used on at least one board.
>> How do you fix that?
>>
>> Maybe this scenario is a bit too apocalyptic, and maybe too much for
>> current automotive uses, but I think it illustrates how the current
>> model is not generic enough. Since there is no existing code in the
>> kernel yet, I think we should strive to do better in order to minimize
>> future problems.
> 
> I like apocalyptic thinking :)
> 
> Yes, it's probably more than the automotive use case that we currently
> have - but it's good to think this through now.
> 
> 
> Lets find out of the DT mapping system Laurent has just talked about
> (locally) can help us here.
> 
> or Bringing in Wolfram, as I think he sees some merit in the address
> pool definitions.
> 
> 
>> My approach is instead to clearly split the local and remote domain. The
>> latter is what could be moved to an overlay. For example:
>>
>> &i2c0 {
>>     serializer@3d {
>>         reg = <0x3d>;
>>         ...
>>
>>         /* Guaranteed not physically present on i2c0 */
>>         i2c-alias-pool = /bits/ 16 <0x20 0x21 0x22 0x23 0x24 0x25>;
>>
>>         i2c-mux {
>>             #address-cells = <1>;
>>             #size-cells = <0>;
>>
>> 	    i2c@0 {
>>                 reg = <0>;
>>                 #address-cells = <1>;
>>                 #size-cells = <0>;
>>
>>                 // ------8<------ this could be moved to an overlay
>>                 sensor@50 {
>>                     reg = <0x50>;
>>                     ...
>>                     endpoint {...};
>>                 };
>>                 eeprom@51 {
>>                     reg = <0x51>;
>>                     ...
>>                 };
>>                 // ------8<------
>>             };
>>
>> 	    i2c@1 {
>>                 reg = <1>;
>>                 #address-cells = <1>;
>>                 #size-cells = <0>;
>>
>>                 // ------8<------
>>                 sensor@50 {
>>                     reg = <0x50>;
>>                     ...
>>                     endpoint {...};
>>                 };
>>                 eeprom@51 {
>>                     reg = <0x51>;
>>                     ...
>>                 };
>>                 // ------8<------
>>             };
>>         };
>>     };
>> };
>>
>> The core difference is that I split the camera@51/reg property in two:
>>
>>  * sensor@50/reg: the remote side (camera overlay);
>>    carries the physical i2c address (note both sensors are at 0x50)
>>  * serializer@3d/i2c-alias-pool: the local side (base board);
>>    lists a pool of addresses that are not used on the i2c bus
>>
>> See how there is no mixing between local and remote. The pool will
>> differ from one base board to another.
>>
>> To implement this, I developed an "i2c address translator" that maps
>> physical remote addresses to local addresses from the pool at runtime.
>> It still needs some work, but address translation it is working.
>>
>>>>  * a DT for the main board mentioning only addresses for the
>>>>    local i2c bus, down to the i2c@0 with address-cells, size-cells and
>>>>    reg properties
>>>>  * a DT overlay for each remote board, mentioning the remote i2c
>>>>    chips with their physical addresses, but no local addresses
>>>>
>>>> The only way I could devise to be generic is to bind each physical
>>>> remote address to a local address at runtime.
>>>>
>>>> Also, to be implemented reliably, an address translation feature is
>>>> required on the local (de)ser chip.
>>>>
>>>> So the question is: can the max9286 chip do i2c address translation?
>>>>
>>>
>>> Yes, The max9286 (deser) can do i2c address translation - but so too can
>>> the max9271 (serialiser)
>>
>> Good!
>>
>>> We do our address translation on the camera (serialiser) side.
>>
>> By "address translation" I mean the i2c address is changed by some
>> device in the middle between the i2c master and the slave. In this sense
>> you are not doing address translation, you are rather modifying the chip
>> addresses. Then transactions happen with the new (0x51/0x61) address,
>> which does not get modified during subsequent transactions.
>>
>>> The cameras *all* boot with the same i2c address (and thus all conflict)
>>>  - We disable all links
>>>  - We enable /one/ link
>>>  - We initialise and reprogram the address of that camera to the address
>>>    specified in the camera node. - Then we move to the next camera.
>>>
>>> The reality is we 'just need' a spare address on the I2C bus - but as
>>> yet - there is no mechanism in I2C core to request a spare address.
>>
>> Not a reliable one, definitely, since there could be i2c devices unknown
>> to the software. This is why I had to introduce the alias pool: the DT
>> writer is required to know which addresses are available and list them
>> in DT.
> 
> Ok - so in this model - even if we don't do address translation - we
> could still 'request' the address from the allocator, and use that to
> set the new 'physical' address?

Yes. Indeed the pool can be seen as a list of physical addresses that:
 - are unused by other chips on the local bus
 - and given to the ser/des to use for the remote devices
 - will physycally appear on the local bus to talk to remote devices

Whether they are runtime translated or reprogrammed on the remode
devices is not much relevant for the local bus.

> I think if we were to use the i2c-alias-pool for our system we'd
> probably want to put a pool at each i2c-mux-bus, to ensure that cameras
> had addresses that were recognisable / identifiable perhaps:
> 
> &i2c0 {
>     serializer@3d {
>         reg = <0x3d>;
>         ...
> 
>         i2c-mux {
> 	    i2c@0 {
>                 reg = <0>;
>                 #address-cells = <1>;
>                 #size-cells
> 
> 	        i2c-alias-pool = /bits/ 16 <0x31 0x41 0x51>;
> 	    }
> 
> 	    i2c@1 {
>                 reg = <1>;
>                 #address-cells = <1>;
>                 #size-cells
> 
> 	        i2c-alias-pool = /bits/ 16 <0x32 0x42 0x52>;
> 	    }
> 	}
>     }
> }
> 
> or such ... but it seems feasible ... ?

I don't foresee any problem in moving from a large pool at the
serializer to small pools at each port.

-- 
Luca

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

* Re: [PATCH v4 4/4] media: i2c: Add RDACM20 driver
  2018-11-28 12:51     ` Kieran Bingham
@ 2018-12-20 23:11       ` Sakari Ailus
  0 siblings, 0 replies; 26+ messages in thread
From: Sakari Ailus @ 2018-12-20 23:11 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: linux-renesas-soc, linux-media, devicetree,
	Niklas Söderlund, Jacopo Mondi, Laurent Pinchart,
	Kieran Bingham, linux-kernel, Jacopo Mondi, Laurent Pinchart,
	Niklas Söderlund

Hu Kieran,

Apologies for the late reply.

On Wed, Nov 28, 2018 at 12:51:39PM +0000, Kieran Bingham wrote:
> Hi Sakari,
> 
> Thank you for your review,
> 
> On 20/11/2018 08:34, Sakari Ailus wrote:
> > Hi Kieran,
> > 
> > On Fri, Nov 02, 2018 at 03:47:23PM +0000, Kieran Bingham wrote:
> >> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> >>
> >> 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.
> >>
> >> 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
> >> ---
> >>  MAINTAINERS                         |  10 +
> >>  drivers/media/i2c/Kconfig           |  11 +
> >>  drivers/media/i2c/Makefile          |   1 +
> >>  drivers/media/i2c/rdacm20-ov10635.h | 953 ++++++++++++++++++++++++++++
> >>  drivers/media/i2c/rdacm20.c         | 635 ++++++++++++++++++
> >>  5 files changed, 1610 insertions(+)
> >>  create mode 100644 drivers/media/i2c/rdacm20-ov10635.h
> >>  create mode 100644 drivers/media/i2c/rdacm20.c
> >>
> >> diff --git a/MAINTAINERS b/MAINTAINERS
> >> index 745f0fd1fff1..26ef20087a43 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -12230,6 +12230,16 @@ S:	Supported
> >>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
> >>  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>
> > 
> > I'm happy to see this will be well maintained. :-)
> 
> Well it means /someone/ should always be able to pick it up :D
> 
> I didn't know who to put here - so I put all of the current blamees ;)
> We've all put a lot of time and work in to the GMSL bring up and
> refactoring.
> 
> If you think it's overkill, I can reduce the names. Same on max9286.

Not at all. There are too many drivers that do not receive the attention
they'd need. :-I

> 
> 
> >> +L:	linux-media@vger.kernel.org
> >> +S:	Maintained
> >> +F:	Documentation/devicetree/bindings/media/i2c/rdacm20.txt
> >> +F:	drivers/media/i2c/rdacm20*
> >> +
> >>  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 eadc00bdd3bf..5eded5e337ec 100644
> >> --- a/drivers/media/i2c/Kconfig
> >> +++ b/drivers/media/i2c/Kconfig
> >> @@ -989,6 +989,17 @@ config VIDEO_S5C73M3
> >>  	  This is a V4L2 sensor driver for Samsung S5C73M3
> >>  	  8 Mpixel camera.
> >>  
> >> +config VIDEO_RDACM20

Btw. I think this might be worth a new comment to tell what devices can be
found here --- it's not a camera sensor as such really. But I wonder how
should it be called. We do have "Miscellaneous helper chips" at the end.
I'm not sure that'd be better. As-is could be fine, too.

> >> +	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 4de7fe62b179..121d28283d45 100644
> >> --- a/drivers/media/i2c/Makefile
> >> +++ b/drivers/media/i2c/Makefile
> >> @@ -111,5 +111,6 @@ obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
> >>  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_SDR_MAX2175) += max2175.o
> >> 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
> > 
> > A part of this driver looks like a driver for an OV camera sensor. Would
> > there be something that prevents separating the camera sensor driver from
> > this one?
> 
> I don't think there's anything preventing it - except (a fair bit of)
> development time.
> 
> We also have the RDACM21 to support, which uses the max9271 and an
> OV10640. At that time - this will absolutely have to be split. We
> shouldn't replicate the max9271 code.
> 
> I mentioned briefly in the cover letter:
> 
> > Further anticipated work in this area includes supporting the RDACM21
> > camera, at which point the RDACM20 will be adapted to separate out the
> > MAX9271 and the OV10635 sensor components.

Oh, sorry; I missed that.

Does the DT currently contain all the necessary information for the drivers
to get everything they need, if you separated them?

> 
> But to get more dedicated time to work this - we need to show some
> progress on GMSL up-streaming, so the max9286 and bindings take priority
> for now.
> 
> A little bit catch 22 ... :D
> 
> I'm sure there will be overlap between GMSL and FPD-Link with the RDACM
> range [0] of cameras too, which also provide TI-FPD Link serialisers.
> 
> I currently envisage that we would have an RDACM20 'driver' which would
> know that it has a max9271 serialiser and an OV10635 sensor, and would
> handle the links of any subdevices internally.

I don't remember all the details, but my understanding is that RDACM20 is
much more than just a box that contains the serialiser and the sensors. So
very probably it'll need its own driver, too. Powering on the sensors, for
instance, seemed hard to make generic.

> 
> As the RDACM20 is an object itself, I think this makes sense ... unless
> anyone suggests that each part should be broken down into the DT
> directly ? (I think that would possibly be a bit too much)
> 
> 
> [0]
> https://www.global-imi.com/sites/default/files/Generic-Minicube-Catalogue-1020151.pdf
> 
> 
> >> +
> >> +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..d96b2eb5ab1b
> >> --- /dev/null
> >> +++ b/drivers/media/i2c/rdacm20.c
> >> @@ -0,0 +1,635 @@
> >> +// SPDX-License-Identifier: GPL-2.0+
> >> +/*
> >> + * IMI RDACM20 GMSL Camera Driver
> >> + *
> >> + * 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.
> >> + */
> >> +
> >> +/*
> >> + * The camera is mode 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"
> >> +
> >> +#define RDACM20_SENSOR_HARD_RESET
> >> +
> >> +#define MAX9271_I2C_ADDRESS		0x40
> >> +
> >> +/* 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_SETGPO			BIT(0)
> >> +/* Register 0x15 */
> >> +#define MAX9271_PCLKDET			BIT(0)
> >> +
> >> +#define MAXIM_I2C_I2C_SPEED_400KHZ	MAX9271_I2CMSTBT_339KBPS
> >> +#define MAXIM_I2C_I2C_SPEED_100KHZ	MAX9271_I2CMSTBT_105KBPS
> >> +#define MAXIM_I2C_SPEED			MAXIM_I2C_I2C_SPEED_100KHZ
> >> +
> >> +#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
> >> +/* #define OV10635_FORMAT			MEDIA_BUS_FMT_UYVY10_2X10 */
> >> +
> >> +struct rdacm20_device {
> >> +	struct i2c_client		*client;
> >> +	struct i2c_client		*sensor;
> >> +	struct v4l2_subdev		sd;
> >> +	struct media_pad		pad;
> >> +	struct v4l2_ctrl_handler	ctrls;
> >> +};
> >> +
> >> +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 max9271_read(struct rdacm20_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 rdacm20_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;
> >> +}
> >> +
> >> +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->client->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->client->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->client->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->client->dev,
> >> +				"%s: register %u (0x%04x) write failed (%d)\n",
> >> +				__func__, i, regs[i].reg, ret);
> >> +			return ret;
> >> +		}
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +/*
> >> + * rdacm20_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 rdacm20_pclk_detect(struct rdacm20_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;
> >> +}
> >> +
> >> +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
> >> +{
> >> +	struct rdacm20_device *dev = sd_to_rdacm20(sd);
> >> +	int ret;
> >> +
> >> +	if (enable) {
> >> +		ret = rdacm20_pclk_detect(dev);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		/* Enable the serial link. */
> >> +		max9271_write(dev, 0x04, MAX9271_SEREN | MAX9271_REVCCEN |
> >> +			      MAX9271_FWDCCEN);
> >> +	} else {
> >> +		/* Disable the serial link. */
> >> +		max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
> >> +			      MAX9271_FWDCCEN);
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rdacm20_g_mbus_config(struct v4l2_subdev *sd,
> >> +				 struct v4l2_mbus_config *cfg)
> >> +{
> >> +	cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
> >> +		     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
> >> +	cfg->type = V4L2_MBUS_CSI2_DPHY;
> > 
> > Hmm. What are you using g_mbus_config() for?
> 
> Good point here ... I assumed it was passed through up to the VIN - but
> it's really not applicable here.
> 
> Or if it is - then it should be describing the GMSL bus link!
> 
> I'll bet this isn't even getting called and can likely be removed.

There are two use cases I know for this --- SoC camera and something that
changes dynamically. The former is obsolete and the latter is better
addressed by the frame descriptors I'd like to see go in for 4.22.

> 
> 
> > 
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +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,
> >> +	.g_mbus_config	= rdacm20_g_mbus_config,
> >> +};
> >> +
> >> +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 max9271_configure_i2c(struct rdacm20_device *dev)
> >> +{
> >> +	/*
> >> +	 * Configure the I2C bus:
> >> +	 *
> >> +	 * - Enable high thresholds on the reverse channel
> >> +	 * - Disable artificial ACK and set I2C speed
> >> +	 */
> >> +	max9271_write(dev, 0x08, MAX9271_REV_HIVTH);
> >> +	usleep_range(5000, 8000);
> >> +
> >> +	max9271_write(dev, 0x0d, MAX9271_I2CSLVSH_469NS_234NS |
> >> +		      MAX9271_I2CSLVTO_1024US | MAXIM_I2C_SPEED);
> >> +	usleep_range(5000, 8000);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int max9271_configure_gmsl_link(struct rdacm20_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);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int max9271_verify_id(struct rdacm20_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;
> >> +}
> >> +
> >> +static int max9271_configure_address(struct rdacm20_device *dev, u8 addr)
> >> +{
> >> +	int ret;
> >> +
> >> +	/* Change the MAX9271 I2C address. */
> >> +	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;
> >> +	}
> >> +	dev->client->addr = addr;
> >> +	usleep_range(3500, 5000);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rdacm20_initialize(struct rdacm20_device *dev)
> >> +{
> >> +	u32 addrs[2];
> >> +	int ret;
> >> +
> >> +	ret = of_property_read_u32_array(dev->client->dev.of_node, "reg",
> >> +					 addrs, ARRAY_SIZE(addrs));
> >> +	if (ret < 0) {
> >> +		dev_err(&dev->client->dev, "Invalid DT reg property\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	/*
> >> +	 * FIXME: The MAX9271 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 = MAX9271_I2C_ADDRESS;
> >> +
> >> +	/* Verify communication with the MAX9271. */
> >> +	i2c_smbus_read_byte(dev->client);	/* ping to wake-up */
> >> +
> >> +	/*
> >> +	 *  Ensure that we have a good link configuration before attempting to
> >> +	 *  identify the device.
> >> +	 */
> >> +	max9271_configure_i2c(dev);
> >> +	max9271_configure_gmsl_link(dev);
> >> +
> >> +	ret = max9271_verify_id(dev);
> >> +	if (ret < 0)
> >> +		return ret;
> >> +
> >> +	ret = max9271_configure_address(dev, addrs[0]);
> >> +	if (ret < 0)
> >> +		return ret;
> >> +
> >> +	/* Reset and verify communication with the OV10635. */
> >> +#ifdef RDACM20_SENSOR_HARD_RESET
> >> +	/* Cycle the OV10635 reset signal connected to the MAX9271 GPIO1. */
> >> +	max9271_write(dev, 0x0f, 0xff & ~(MAX9271_GPIO1OUT | MAX9271_SETGPO));
> >> +	mdelay(10);
> >> +	max9271_write(dev, 0x0f, 0xff & ~MAX9271_SETGPO);
> >> +	mdelay(10);
> > 
> > Do you need a busy loop? Could you use msleep()?
> 
> Checkpatch warns here:
> 
> WARNING: msleep < 20ms can sleep for up to 20ms; see
> Documentation/timers/timers-howto.txt
> #10: FILE: drivers/media/i2c/rdacm20.c:461:
> +       msleep(10);
> 
> I think for this context, msleep(10) even with the warning is fine here,
> but perhaps we can meet that with a usleep_range(10000, 20000); too.

usleep_range(), then, but just setting the delay to precisely 10 ms is much
better than a 10 ms busy loop.

> 
> 
> >> +#else
> >> +	/* Perform a software reset. */
> >> +	ret = ov10635_write(dev, OV10635_SOFTWARE_RESET, 1);
> >> +	if (ret < 0) {
> >> +		dev_err(&dev->client->dev, "OV10635 reset failed (%d)\n", ret);
> >> +		return -ENXIO;
> >> +	}
> >> +
> >> +	udelay(100);
> >> +#endif
> >> +
> >> +	ret = ov10635_read16(dev, OV10635_PID);
> >> +	if (ret < 0) {
> >> +		dev_err(&dev->client->dev, "OV10635 ID read failed (%d)\n",
> >> +			ret);
> >> +		return -ENXIO;
> >> +	}
> >> +
> >> +	if (ret != OV10635_VERSION) {
> >> +		dev_err(&dev->client->dev, "OV10635 ID mismatch (0x%04x)\n",
> >> +			ret);
> >> +		return -ENXIO;
> >> +	}
> >> +
> >> +	dev_info(&dev->client->dev, "Identified MAX9271 + OV10635 device\n");
> >> +
> >> +	/* Change the sensor I2C address. */
> >> +	ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
> >> +			    (addrs[1] << 1) | OV10635_SC_CMMN_SCCB_ID_SELECT);
> >> +	if (ret < 0) {
> >> +		dev_err(&dev->client->dev,
> >> +			"OV10635 I2C address change failed (%d)\n", ret);
> >> +		return ret;
> >> +	}
> >> +	dev->sensor->addr = addrs[1];
> >> +	usleep_range(3500, 5000);
> >> +
> >> +	/* Program the 0V10635 initial configuration. */
> >> +	ret = ov10635_set_regs(dev, ov10635_regs_wizard,
> >> +			       ARRAY_SIZE(ov10635_regs_wizard));
> > 
> > return ov...(); ?
> 
> Yes, that would be nicer.
> 
> > 
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rdacm20_probe(struct i2c_client *client,
> >> +			 const struct i2c_device_id *did)
> >> +{
> >> +	struct rdacm20_device *dev;
> >> +	struct fwnode_handle *ep;
> >> +	int ret;
> >> +
> >> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> > 
> > You could use devm_kzalloc().
> 
> Will change.
> 
> > 
> >> +	if (!dev)
> >> +		return -ENOMEM;
> >> +
> >> +	dev->client = client;
> >> +
> >> +	/* Create the dummy I2C client for the sensor. */
> >> +	dev->sensor = i2c_new_dummy(client->adapter, OV10635_I2C_ADDRESS);
> >> +	if (!dev->sensor) {
> >> +		ret = -ENXIO;
> >> +		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;
> > 
> > |=, as in the other patch.
> 
> Bah - yes :)
> 
> 
> > 
> >> +
> >> +	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;
> >> +
> >> +	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(ep);
> >> +error:
> > 
> > You're missing v4l2_ctrl_handler_free() here.
> 
> Good spot. Thanks
> 
> > 
> >> +	media_entity_cleanup(&dev->sd.entity);
> >> +	if (dev->sensor)
> >> +		i2c_unregister_device(dev->sensor);
> >> +	kfree(dev);
> >> +
> >> +	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);
> > 
> > As well as here.
> 
> 
> Ack.
> 
> 
> > 
> >> +	media_entity_cleanup(&dev->sd.entity);
> >> +	i2c_unregister_device(dev->sensor);
> >> +	kfree(dev);
> >> +
> >> +	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 i2c_device_id rdacm20_id[] = {
> >> +	{ "rdacm20", 0 },
> >> +	{ }
> >> +};
> >> +MODULE_DEVICE_TABLE(i2c, rdacm20_id);
> >> +
> >> +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		= rdacm20_probe,
> > 
> > Could you use probe_new, so you could remove the i2c ID table? Or do you
> > need that for something?
> 
> I believe probe_new is probably fine.
> 
> I should really resurrect my i2c-probe-coccinelle patch and get that
> conversion task done, so we can get to removing and replacing .probe :)

That'd be nice!

> 
> (note to self ... starting projects when unemployed becomes difficult to
> continue when someone else gives you projects to work on all the time ...)
> 
> 
> > 
> >> +	.remove		= rdacm20_remove,
> >> +	.shutdown	= rdacm20_shutdown,
> >> +	.id_table	= rdacm20_id,
> >> +};
> >> +
> >> +module_i2c_driver(rdacm20_i2c_driver);
> >> +
> >> +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
> >> +MODULE_AUTHOR("Vladimir Barinov");
> >> +MODULE_LICENSE("GPL");
> 
> Changes described above made and tested on a *single MAX9286* capturing
> 4 cameras simultaneously, now on my rcar.git gmsl/v5 branch ... :D

-- 
Regards,

Sakari Ailus

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

end of thread, other threads:[~2018-12-20 23:12 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-02 15:47 [PATCH v4 0/4] MAX9286 GMSL Support Kieran Bingham
2018-11-02 15:47 ` [PATCH v4 1/4] dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286 Kieran Bingham
2018-11-05 20:02   ` Rob Herring
2018-11-13 22:42   ` Luca Ceresoli
2018-11-13 23:12     ` Kieran Bingham
2018-11-14 10:04       ` Luca Ceresoli
2018-11-27 22:47         ` Kieran Bingham
2018-12-01 18:41           ` Luca Ceresoli
2018-11-02 15:47 ` [PATCH v4 2/4] dt-bindings: media: i2c: Add bindings for IMI RDACM20 Kieran Bingham
2018-11-05 20:13   ` Rob Herring
2018-11-02 15:47 ` [PATCH v4 3/4] media: i2c: Add MAX9286 driver Kieran Bingham
2018-11-07 15:06   ` Kieran Bingham
2018-11-07 17:24     ` Luca Ceresoli
2018-11-08 10:11       ` jacopo mondi
2018-11-08 11:24         ` Luca Ceresoli
2018-11-13 22:49   ` Luca Ceresoli
2018-11-14  0:46     ` Kieran Bingham
2018-11-14 10:04       ` Luca Ceresoli
2018-11-20  0:32         ` Kieran Bingham
2018-11-20 10:51           ` Luca Ceresoli
2018-11-20 17:44             ` jacopo mondi
2018-11-21 10:05               ` Luca Ceresoli
2018-11-02 15:47 ` [PATCH v4 4/4] media: i2c: Add RDACM20 driver Kieran Bingham
2018-11-20  8:34   ` Sakari Ailus
2018-11-28 12:51     ` Kieran Bingham
2018-12-20 23:11       ` Sakari Ailus

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).