linux-renesas-soc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 0/5] media: i2c: Add MAX9271 subdevice driver
@ 2021-08-17  7:26 Jacopo Mondi
  2021-08-17  7:26 ` [RFC 1/5] media: i2c: max9271: Rename max9271 library driver Jacopo Mondi
                   ` (6 more replies)
  0 siblings, 7 replies; 21+ messages in thread
From: Jacopo Mondi @ 2021-08-17  7:26 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Kieran Bingham, Laurent Pinchart,
	Niklas Söderlund
  Cc: Jacopo Mondi, Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam,
	Thomas NIZAN, linux-renesas-soc, linux-media

Hello,
   as noticed during the inclusion of the RDACM20/21 cameras, their driver make
use of a library driver that exports functions to control the MAX9271 GMSL
serializer embedded in the camera module.

This series attempts to create an i2c subdevice driver for the MAX9271
serializer, to support the camera module operations using the v4l2 subdev
operations.

The series is based on the currently in-review:
https://patchwork.linuxtv.org/project/linux-media/list/?series=5847
https://patchwork.linuxtv.org/project/linux-media/list/?series=5949

The series:
1) Introduced an i2c subdev driver for the MAX9271 GMSL serializer
2) Adapt the RDACM20 driver by removing the MAX9271 handling from there
3) Modify the DTS layout to describe the MAX9271 chip and the camera module
   separately

To be done:
- bindings
- handling of reset lines between max9271 and image sensor
- the camera module drivers could be made sensor drivers

However I'm not fully convinced this really brings any benefit as the serializer
and the image sensor are actually packed together in the same camera module
and are tightly coupled.

The biggest issue I'm facing, and for which I would be happy to receive pointers
to is the following one.

The new DTS layout now looks like

	max9286 {

		i2c-mux {
			i2c@0 {
				max9271 {
				}

				rdacm20{
				}
			}
		}
	}

If I do rely on the probe sequence implemented by the instantiation of the
i2c-mux child nodes:

	- max9286
		-max9271
		-sensor

		-max9271
		-sensor

		...

As per each i2c-mux subnode the max9271 and the connected sensor are probed once
after the other.

This unfortunately doesn't play well with the requirements of GMSL bus, for
which the post_register operation is being introduced. With the current
RDACM20/21 drivers and post_register in place with two cameras connected to the
system, the desired initialization sequence looks like:

            MAX9286                  RDACM20/21

            probe()
               |
               ---------------------> |
                                      camera 1 probe() {
                                         enable_threshold()
                                      }
               |<--------------------|
            v4l2 async bound {
		completed = no
               |
               ---------------------> |
                                      camera 2 probe() {
                                         enable_threshold()
                                      }
               |<--------------------|
		completed = yes

                compensate_amplitude()

                call post_register()
               |-------------------->|
                                     camera 1 post_register()
                                         access camera registers()
                                    }
               |<-------------------
               |-------------------->|
                                     camera 2 post_register()
                                         access camera registers()
                                    }
               |<-------------------
            }

Which guarantees that the bulk access to the camera registers happens after the
deserializer has compensated the channel amplitude.

With the new model I do have a race between the sensor probe and the
post_register() of the serializer in case a single camera is connected.

What happes is that as soon as the max9271 registers its async subdev the
max9286 notifier completes an call max9271->post_register(). But at that time
the sensor subdev has not probed yet, so there is no subdev on which to call
post_register in the max9271

following:

    MAX9286                  MAX9271			SENSOR

    probe()
       |
       ---------------------> |
			      probe() {
				 enable_threshold()
			      }
      }
       |<--------------------|
    v4l2 async bound {
	completed = yes
 	subdev->post_register()
       |-------------------->|
			     post_register()
				gmsl_bus_config()
				subdev->post_register(NULL)
				segfault
			    }
							probe()
    }

If I instead do not use post_register() between the max9271 and the sensor,
then the model works for a single camera only (as it is implemented in this
version)

    MAX9286                  MAX9271			SENSOR

    probe()
       |
       ---------------------> |
			      probe() {
				 enable_threshold()
			      }
      }
       |<--------------------|
    v4l2 async bound {
	completed = no
       |-------------------->|
							probe() {
							   i2c writes to
							   the sensor without
							   GMSL configuration
							}
    }

So, my question is: are there examples on how to have the max9271 driver
control the probe time the connected sensor without relying on the probe
sequence of the I2C-mux device nodes ? If I could do so, what I would like to
realize looks like

    MAX9286                  MAX9271			SENSOR

    probe()
       |
       ---------------------> |
			      camera 1 probe() {
				--------------------->|
							 sensor probe()
				 enable_threshold()
			      }
      }
       |<--------------------|
    v4l2 async bound {
	completed = no
       |-------------------->|
			     camera 2 probe() {
				--------------------->|
							sensor probe()
				 enable_threshold()
			      }
       |<--------------------|
	completed = yes

	compensate_amplitude()
	for (subdev)
	   subdev->post_register()
          |----------------->|
			     camera 1 post_register()
				subdev->post_register()
				--------------------->|
							post_register()
								i2c writes
	   subdev->post_register()
          |----------------->|
			     camera 2 post_register()
				subdev->post_register()
				--------------------->|
							post_register()
								i2c writes
    }


I recall Mauro pointed me to an example when he first suggested to make the
MAX9271 library a proper i2c subdevice driver. Do you happen to recall which one
was it ?

Thanks
   j

Jacopo Mondi (5):
  media: i2c: max9271: Rename max9271 library driver
  media: i2c: Add MAX9271 I2C driver
  media: i2c: rdacm20: Adapt to work with MAX9271
  media: i2c: max9286: Fetch PIXEL_RATE in s_stream
  arm64: dts: GMSL: Adapt to the use max9271 driver

 MAINTAINERS                                   |  17 +-
 arch/arm64/boot/dts/renesas/gmsl-cameras.dtsi |  34 +-
 .../arm64/boot/dts/renesas/r8a77970-eagle.dts |   6 +-
 drivers/media/i2c/Kconfig                     |  12 +
 drivers/media/i2c/Makefile                    |   3 +-
 drivers/media/i2c/max9271-lib.c               | 374 +++++++++++++
 .../media/i2c/{max9271.h => max9271-lib.h}    |   0
 drivers/media/i2c/max9271.c                   | 528 +++++++++++++++---
 drivers/media/i2c/max9286.c                   |   6 +-
 drivers/media/i2c/rdacm20.c                   | 139 +----
 drivers/media/i2c/rdacm21.c                   |   2 +-
 11 files changed, 917 insertions(+), 204 deletions(-)
 create mode 100644 drivers/media/i2c/max9271-lib.c
 rename drivers/media/i2c/{max9271.h => max9271-lib.h} (100%)

--
2.32.0


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

* [RFC 1/5] media: i2c: max9271: Rename max9271 library driver
  2021-08-17  7:26 [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Jacopo Mondi
@ 2021-08-17  7:26 ` Jacopo Mondi
  2021-08-18 12:48   ` Kieran Bingham
  2021-08-23  2:09   ` Laurent Pinchart
  2021-08-17  7:27 ` [RFC 2/5] media: i2c: Add MAX9271 I2C driver Jacopo Mondi
                   ` (5 subsequent siblings)
  6 siblings, 2 replies; 21+ messages in thread
From: Jacopo Mondi @ 2021-08-17  7:26 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Kieran Bingham, Laurent Pinchart,
	Niklas Söderlund
  Cc: Jacopo Mondi, Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam,
	Thomas NIZAN, linux-renesas-soc, linux-media

Support for the MAX9271 GMSL serializer was provided in the form of a
library driver, with the RDACM20 and RDACM21 camera module drivers using
the functions exported by the library.

In preparation to introduce an i2c subdevice driver to support the
MAX9271 serializer, rename the library driver from max9271 to
max9271-lib.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
 MAINTAINERS                                    | 8 ++++----
 drivers/media/i2c/Makefile                     | 2 +-
 drivers/media/i2c/{max9271.c => max9271-lib.c} | 2 +-
 drivers/media/i2c/{max9271.h => max9271-lib.h} | 0
 drivers/media/i2c/rdacm20.c                    | 2 +-
 drivers/media/i2c/rdacm21.c                    | 2 +-
 6 files changed, 8 insertions(+), 8 deletions(-)
 rename drivers/media/i2c/{max9271.c => max9271-lib.c} (99%)
 rename drivers/media/i2c/{max9271.h => max9271-lib.h} (100%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 524eabe50d79..7ad89cac19b7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15625,8 +15625,8 @@ M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
 L:	linux-media@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
-F:	drivers/media/i2c/max9271.c
-F:	drivers/media/i2c/max9271.h
+F:	drivers/media/i2c/max9271-lib.c
+F:	drivers/media/i2c/max9271-lib.h
 F:	drivers/media/i2c/rdacm20.c
 
 RDACM21 Camera Sensor
@@ -15637,8 +15637,8 @@ M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
 L:	linux-media@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
-F:	drivers/media/i2c/max9271.c
-F:	drivers/media/i2c/max9271.h
+F:	drivers/media/i2c/max9271-lib.c
+F:	drivers/media/i2c/max9271-lib.h
 F:	drivers/media/i2c/rdacm21.c
 
 RDC R-321X SoC
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 83268f20aa3a..4d879373bd48 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -129,7 +129,7 @@ obj-$(CONFIG_VIDEO_IMX335)	+= imx335.o
 obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
 obj-$(CONFIG_VIDEO_IMX412)	+= imx412.o
 obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
-obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271.o
+obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271-lib.o
 obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
 obj-$(CONFIG_VIDEO_RDACM21)	+= rdacm21.o
 obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271-lib.c
similarity index 99%
rename from drivers/media/i2c/max9271.c
rename to drivers/media/i2c/max9271-lib.c
index ff86c8c4ea61..c554bb0f42f4 100644
--- a/drivers/media/i2c/max9271.c
+++ b/drivers/media/i2c/max9271-lib.c
@@ -20,7 +20,7 @@
 #include <linux/i2c.h>
 #include <linux/module.h>
 
-#include "max9271.h"
+#include "max9271-lib.h"
 
 static int max9271_read(struct max9271_device *dev, u8 reg)
 {
diff --git a/drivers/media/i2c/max9271.h b/drivers/media/i2c/max9271-lib.h
similarity index 100%
rename from drivers/media/i2c/max9271.h
rename to drivers/media/i2c/max9271-lib.h
diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
index eb0e3dc22cc3..bf06a1c50306 100644
--- a/drivers/media/i2c/rdacm20.c
+++ b/drivers/media/i2c/rdacm20.c
@@ -27,7 +27,7 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-subdev.h>
 
-#include "max9271.h"
+#include "max9271-lib.h"
 
 #define OV10635_I2C_ADDRESS		0x30
 
diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c
index 35217782f693..3a05abe4e96c 100644
--- a/drivers/media/i2c/rdacm21.c
+++ b/drivers/media/i2c/rdacm21.c
@@ -21,7 +21,7 @@
 #include <media/v4l2-async.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-subdev.h>
-#include "max9271.h"
+#include "max9271-lib.h"
 
 #define MAX9271_RESET_CYCLES		10
 
-- 
2.32.0


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

* [RFC 2/5] media: i2c: Add MAX9271 I2C driver
  2021-08-17  7:26 [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Jacopo Mondi
  2021-08-17  7:26 ` [RFC 1/5] media: i2c: max9271: Rename max9271 library driver Jacopo Mondi
@ 2021-08-17  7:27 ` Jacopo Mondi
  2021-08-17 15:49   ` Kieran Bingham
  2021-08-17  7:27 ` [RFC 3/5] media: i2c: rdacm20: Adapt to work with MAX9271 Jacopo Mondi
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 21+ messages in thread
From: Jacopo Mondi @ 2021-08-17  7:27 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Kieran Bingham, Laurent Pinchart,
	Niklas Söderlund
  Cc: Jacopo Mondi, Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam,
	Thomas NIZAN, linux-renesas-soc, linux-media

The MAX9271 is a GMSL serializer that serializes a video stream
received from an image sensor through the parallel video bus.

The serializer it's usually found embedded with an image sensor and
other ancillary chips in camera modules like RDACM20 and RDACM21.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
 MAINTAINERS                 |   9 +
 drivers/media/i2c/Kconfig   |  12 +
 drivers/media/i2c/Makefile  |   1 +
 drivers/media/i2c/max9271.c | 756 ++++++++++++++++++++++++++++++++++++
 4 files changed, 778 insertions(+)
 create mode 100644 drivers/media/i2c/max9271.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 7ad89cac19b7..2dab25a08c9c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11244,6 +11244,15 @@ F:	Documentation/hwmon/max6697.rst
 F:	drivers/hwmon/max6697.c
 F:	include/linux/platform_data/max6697.h
 
+MAX9271 GMSL SERIALIZER 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:	drivers/media/i2c/max9271.c
+
 MAX9286 QUAD GMSL DESERIALIZER DRIVER
 M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
 M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 08feb3e8c1bf..b793d1f322d9 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -1300,6 +1300,18 @@ source "drivers/media/i2c/m5mols/Kconfig"
 config VIDEO_MAX9271_LIB
 	tristate
 
+config VIDEO_MAX9271
+	tristate "MAX9271 GMSL serializer support"
+	depends on I2C
+	select V4L2_FWNODE
+	select VIDEO_V4L2_SUBDEV_API
+	select MEDIA_CONTROLLER
+	help
+	  This driver supports the Maxim MAX9271 GMSL serializer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called max9271.
+
 config VIDEO_RDACM20
 	tristate "IMI RDACM20 camera support"
 	depends on I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 4d879373bd48..37bb51065574 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -130,6 +130,7 @@ obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
 obj-$(CONFIG_VIDEO_IMX412)	+= imx412.o
 obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
 obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271-lib.o
+obj-$(CONFIG_VIDEO_MAX9271)	+= max9271.o
 obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
 obj-$(CONFIG_VIDEO_RDACM21)	+= rdacm21.o
 obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c
new file mode 100644
index 000000000000..64987cba3d3e
--- /dev/null
+++ b/drivers/media/i2c/max9271.c
@@ -0,0 +1,756 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017-2021 Jacopo Mondi
+ * Copyright (C) 2017-2020 Kieran Bingham
+ * Copyright (C) 2017-2019 Laurent Pinchart
+ * Copyright (C) 2017-2019 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/fwnode.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/property.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define MAX9271_DEFAULT_ADDR	0x40
+
+/* Register 0x02 */
+#define MAX9271_SPREAD_SPECT_0		(0 << 5)
+#define MAX9271_SPREAD_SPECT_05		(1 << 5)
+#define MAX9271_SPREAD_SPECT_15		(2 << 5)
+#define MAX9271_SPREAD_SPECT_1		(5 << 5)
+#define MAX9271_SPREAD_SPECT_2		(3 << 5)
+#define MAX9271_SPREAD_SPECT_3		(6 << 5)
+#define MAX9271_SPREAD_SPECT_4		(7 << 5)
+#define MAX9271_R02_RES			BIT(4)
+#define MAX9271_PCLK_AUTODETECT		(3 << 2)
+#define MAX9271_SERIAL_AUTODETECT	(0x03)
+/* Register 0x04 */
+#define MAX9271_SEREN			BIT(7)
+#define MAX9271_CLINKEN			BIT(6)
+#define MAX9271_PRBSEN			BIT(5)
+#define MAX9271_SLEEP			BIT(4)
+#define MAX9271_INTTYPE_I2C		(0 << 2)
+#define MAX9271_INTTYPE_UART		(1 << 2)
+#define MAX9271_INTTYPE_NONE		(2 << 2)
+#define MAX9271_REVCCEN			BIT(1)
+#define MAX9271_FWDCCEN			BIT(0)
+/* Register 0x07 */
+#define MAX9271_DBL			BIT(7)
+#define MAX9271_DRS			BIT(6)
+#define MAX9271_BWS			BIT(5)
+#define MAX9271_ES			BIT(4)
+#define MAX9271_HVEN			BIT(2)
+#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
+#define MAX9271_EDC_6BIT_CRC		(1 << 0)
+#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
+/* Register 0x08 */
+#define MAX9271_INVVS			BIT(7)
+#define MAX9271_INVHS			BIT(6)
+#define MAX9271_REV_LOGAIN		BIT(3)
+#define MAX9271_REV_HIVTH		BIT(0)
+/* Register 0x09 */
+#define MAX9271_ID			0x09
+/* Register 0x0d */
+#define MAX9271_I2CLOCACK		BIT(7)
+#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
+#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
+#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
+#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
+#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
+#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
+#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
+#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
+#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
+#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
+#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
+#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
+#define MAX9271_I2CSLVTO_NONE		(3 << 0)
+#define MAX9271_I2CSLVTO_1024US		(2 << 0)
+#define MAX9271_I2CSLVTO_256US		(1 << 0)
+#define MAX9271_I2CSLVTO_64US		(0 << 0)
+/* Register 0x0f */
+#define MAX9271_GPIO5OUT		BIT(5)
+#define MAX9271_GPIO4OUT		BIT(4)
+#define MAX9271_GPIO3OUT		BIT(3)
+#define MAX9271_GPIO2OUT		BIT(2)
+#define MAX9271_GPIO1OUT		BIT(1)
+#define MAX9271_GPO			BIT(0)
+/* Register 0x15 */
+#define MAX9271_PCLKDET			BIT(0)
+
+struct max9271_device {
+	struct device			*dev;
+	struct i2c_client		*client;
+	struct v4l2_subdev		sd;
+#define MAX9271_SOURCE_PAD	0
+#define MAX9271_SINK_PAD	1
+	struct media_pad		pads[2];
+	struct v4l2_async_notifier	notifier;
+	struct v4l2_async_subdev	*asd;
+	struct v4l2_ctrl_handler	ctrls;
+	struct v4l2_subdev		*sensor;
+};
+
+static inline struct max9271_device *sd_to_max9271(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct max9271_device, sd);
+}
+
+static inline struct max9271_device *i2c_to_max9271(struct i2c_client *client)
+{
+	return sd_to_max9271(i2c_get_clientdata(client));
+}
+
+static inline struct max9271_device *notifier_to_max9271(
+						struct v4l2_async_notifier *nf)
+{
+	return container_of(nf, struct max9271_device, notifier);
+}
+
+/* --- MAX9271 hardware operations --- */
+
+static int max9271_read(struct max9271_device *dev, u8 reg)
+{
+	int ret;
+
+	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
+
+	ret = i2c_smbus_read_byte_data(dev->client, reg);
+	if (ret < 0)
+		dev_dbg(&dev->client->dev,
+			"%s: register 0x%02x read failed (%d)\n",
+			__func__, reg, ret);
+
+	return ret;
+}
+
+static int max9271_write(struct max9271_device *dev, u8 reg, u8 val)
+{
+	int ret;
+
+	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
+
+	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
+	if (ret < 0)
+		dev_err(&dev->client->dev,
+			"%s: register 0x%02x write failed (%d)\n",
+			__func__, reg, ret);
+
+	return ret;
+}
+
+/*
+ * max9271_pclk_detect() - Detect valid pixel clock from image sensor
+ *
+ * Wait up to 10ms for a valid pixel clock.
+ *
+ * Returns 0 for success, < 0 for pixel clock not properly detected
+ */
+static int max9271_pclk_detect(struct max9271_device *dev)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < 100; i++) {
+		ret = max9271_read(dev, 0x15);
+		if (ret < 0)
+			return ret;
+
+		if (ret & MAX9271_PCLKDET)
+			return 0;
+
+		usleep_range(50, 100);
+	}
+
+	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
+
+	return -EIO;
+}
+
+static void max9271_wake_up(struct max9271_device *dev)
+{
+	/*
+	 * Use the chip default address as this function has to be called
+	 * before any other one.
+	 */
+	dev->client->addr = MAX9271_DEFAULT_ADDR;
+	i2c_smbus_read_byte(dev->client);
+	usleep_range(5000, 8000);
+}
+
+static int max9271_set_serial_link(struct max9271_device *dev, bool enable)
+{
+	int ret;
+	u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN;
+
+	if (enable) {
+		ret = max9271_pclk_detect(dev);
+		if (ret)
+			return ret;
+
+		val |= MAX9271_SEREN;
+	} else {
+		val |= MAX9271_CLINKEN;
+	}
+
+	/*
+	 * 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.
+	 */
+	ret = max9271_write(dev, 0x04, val);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(5000, 8000);
+
+	return 0;
+}
+
+static int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config)
+{
+	int ret;
+
+	ret = max9271_write(dev, 0x0d, i2c_config);
+	if (ret < 0)
+		return ret;
+
+	/* The delay required after an I2C bus configuration change is not
+	 * characterized in the serializer manual. Sleep up to 5msec to
+	 * stay safe.
+	 */
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+
+static int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
+{
+	int ret;
+
+	ret = max9271_read(dev, 0x08);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Enable or disable reverse channel high threshold to increase
+	 * immunity to power supply noise.
+	 */
+	ret = max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
+	if (ret < 0)
+		return ret;
+
+	usleep_range(2000, 2500);
+
+	return 0;
+}
+
+static int max9271_configure_gmsl_link(struct max9271_device *dev)
+{
+	int ret;
+
+	/*
+	 * 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
+	 *
+	 * TODO: Make the GMSL link configuration parametric.
+	 */
+	ret = max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
+			    MAX9271_EDC_1BIT_PARITY);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(5000, 8000);
+
+	/*
+	 * Adjust spread spectrum to +4% and auto-detect pixel clock
+	 * and serial link rate.
+	 */
+	ret = max9271_write(dev, 0x02,
+			    MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
+			    MAX9271_PCLK_AUTODETECT |
+			    MAX9271_SERIAL_AUTODETECT);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(5000, 8000);
+
+	return 0;
+}
+
+static int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+	int ret;
+
+	ret = max9271_read(dev, 0x0f);
+	if (ret < 0)
+		return 0;
+
+	ret |= gpio_mask;
+	ret = max9271_write(dev, 0x0f, ret);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret);
+		return ret;
+	}
+
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+
+static int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+	int ret;
+
+	ret = max9271_read(dev, 0x0f);
+	if (ret < 0)
+		return 0;
+
+	ret &= ~gpio_mask;
+	ret = max9271_write(dev, 0x0f, ret);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret);
+		return ret;
+	}
+
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+
+static int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+	int ret;
+
+	ret = max9271_read(dev, 0x0e);
+	if (ret < 0)
+		return 0;
+
+	/* BIT(0) reserved: GPO is always enabled. */
+	ret |= (gpio_mask & ~BIT(0));
+	ret = max9271_write(dev, 0x0e, ret);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret);
+		return ret;
+	}
+
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+
+static int max9271_verify_id(struct max9271_device *dev)
+{
+	int ret;
+
+	ret = max9271_read(dev, 0x1e);
+	if (ret < 0) {
+		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
+			ret);
+		return ret;
+	}
+
+	if (ret != MAX9271_ID) {
+		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
+			ret);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int max9271_set_address(struct max9271_device *dev, u8 addr)
+{
+	int ret;
+
+	ret = max9271_write(dev, 0x00, addr << 1);
+	if (ret < 0) {
+		dev_err(&dev->client->dev,
+			"MAX9271 I2C address change failed (%d)\n", ret);
+		return ret;
+	}
+	usleep_range(3500, 5000);
+
+	return 0;
+}
+
+/* --- V4L2 Subdev Ops --- */
+
+static int max9271_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct max9271_device *max9271 = sd_to_max9271(sd);
+
+	return max9271_set_serial_link(max9271, enable);
+}
+
+static int max9271_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *sd_state,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct max9271_device *max9271 = sd_to_max9271(sd);
+
+	return v4l2_subdev_call(max9271->sensor, pad, enum_mbus_code, NULL,
+				code);
+}
+
+static int max9271_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_state *sd_state,
+			   struct v4l2_subdev_format *format)
+{
+	struct max9271_device *max9271 = sd_to_max9271(sd);
+
+	return v4l2_subdev_call(max9271->sensor, pad, get_fmt, NULL,
+				format);
+}
+
+static int max9271_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_state *sd_state,
+			   struct v4l2_subdev_format *format)
+{
+	struct max9271_device *max9271 = sd_to_max9271(sd);
+
+	return v4l2_subdev_call(max9271->sensor, pad, set_fmt, NULL,
+				format);
+}
+
+static int max9271_post_register(struct v4l2_subdev *sd)
+{
+	struct max9271_device *max9271 = sd_to_max9271(sd);
+	int ret;
+
+	ret = max9271_verify_id(max9271);
+	if (ret < 0)
+		return ret;
+
+	ret = max9271_enable_gpios(max9271, MAX9271_GPIO1OUT);
+	if (ret)
+		return ret;
+
+	ret = max9271_configure_gmsl_link(max9271);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops max9271_video_ops = {
+	.s_stream	= max9271_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops max9271_subdev_pad_ops = {
+	.enum_mbus_code = max9271_enum_mbus_code,
+	.get_fmt	= max9271_get_fmt,
+	.set_fmt	= max9271_set_fmt,
+};
+
+static const struct v4l2_subdev_core_ops max9271_core_ops = {
+	.post_register	= max9271_post_register,
+};
+
+static const struct v4l2_subdev_ops max9271_subdev_ops = {
+	.core		= &max9271_core_ops,
+	.video		= &max9271_video_ops,
+	.pad		= &max9271_subdev_pad_ops,
+};
+
+/* --- V4L2 Async Notifier --- */
+
+static int max9271_notify_bound(struct v4l2_async_notifier *notifier,
+				struct v4l2_subdev *subdev,
+				struct v4l2_async_subdev *asd)
+{
+	struct max9271_device *max9271 = notifier_to_max9271(notifier);
+	int ret, pad;
+
+	/*
+	 * Reserve more space than necessary for controls inherited by the
+	 * remote subdev.
+	 */
+	ret = v4l2_ctrl_handler_init(&max9271->ctrls, 16);
+	if (ret < 0) {
+		dev_err(max9271->dev,
+			"Unable to initialize control handler: %d\n", ret);
+		return ret;
+	}
+
+	ret = v4l2_ctrl_add_handler(&max9271->ctrls, subdev->ctrl_handler,
+				    NULL, true);
+	if (ret < 0) {
+		dev_err(max9271->dev,
+			"Unable to add subdev control handler: %d\n", ret);
+		goto error_free_handler;
+	}
+	max9271->sd.ctrl_handler = &max9271->ctrls;
+
+	/* Create media link with the remote sensor source pad. */
+	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
+					  MEDIA_PAD_FL_SOURCE);
+	if (pad < 0) {
+		dev_err(max9271->dev,
+			"Failed to find source pad for %s\n", subdev->name);
+		ret = pad;
+		goto error_free_handler;
+	}
+
+	ret = media_create_pad_link(&subdev->entity, pad,
+				    &max9271->sd.entity, MAX9271_SINK_PAD,
+				    MEDIA_LNK_FL_ENABLED |
+				    MEDIA_LNK_FL_IMMUTABLE);
+	if (ret)
+		goto error_free_handler;
+
+	max9271->sensor = subdev;
+
+	/*
+	 * Hold OV10635 in reset during max9271 configuration. The reset signal
+	 * has to be asserted for at least 200 microseconds.
+	 */
+	ret = max9271_clear_gpios(max9271, MAX9271_GPIO1OUT);
+	if (ret)
+		return ret;
+	usleep_range(200, 500);
+
+	/*
+	 * Release ov10635 from reset and initialize it. The image sensor
+	 * requires at least 2048 XVCLK cycles (85 micro-seconds at 24MHz)
+	 * before being available. Stay safe and wait up to 500 micro-seconds.
+	 */
+	ret = max9271_set_gpios(max9271, MAX9271_GPIO1OUT);
+	if (ret)
+		return ret;
+	usleep_range(100, 500);
+
+	/*
+	 * Call the sensor post_register operation to complete its
+	 * initialization.
+	 */
+	ret = v4l2_subdev_call(max9271->sensor, core, post_register);
+	if (ret) {
+		dev_err(max9271->dev, "Failed to initialize sensor %u\n", ret);
+		goto error_remove_link;
+	}
+
+	return 0;
+
+error_remove_link:
+	media_entity_remove_links(&max9271->sd.entity);
+	max9271->sensor = NULL;
+
+error_free_handler:
+	v4l2_ctrl_handler_free(&max9271->ctrls);
+	max9271->sd.ctrl_handler = NULL;
+
+	return ret;
+}
+
+static void max9271_notify_unbind(struct v4l2_async_notifier *notifier,
+				  struct v4l2_subdev *subdev,
+				  struct v4l2_async_subdev *asd)
+{
+	struct max9271_device *max9271 = notifier_to_max9271(notifier);
+
+	media_entity_remove_links(&max9271->sd.entity);
+	max9271->sensor = NULL;
+}
+
+static const struct v4l2_async_notifier_operations max9271_notifier_ops = {
+	.bound = max9271_notify_bound,
+	.unbind = max9271_notify_unbind,
+};
+
+static int max9271_parse_dt(struct max9271_device *max9271)
+{
+	struct fwnode_handle *ep, *remote;
+	struct v4l2_fwnode_endpoint vep = {
+		.bus_type = V4L2_MBUS_PARALLEL,
+	};
+	int ret;
+
+	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 1, 0, 0);
+	if (!ep) {
+		dev_err(max9271->dev, "Unable to get sensor endpoint: %pOF\n",
+			max9271->dev->of_node);
+		return -ENOENT;
+	}
+
+	remote = fwnode_graph_get_remote_endpoint(ep);
+	if (!remote) {
+		dev_err(max9271->dev, "Unable to get remote endpoint: %pOF\n",
+			max9271->dev->of_node);
+		return -ENOENT;
+	}
+
+	ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+	fwnode_handle_put(ep);
+	if (ret) {
+		fwnode_handle_put(remote);
+		dev_err(max9271->dev, "Unable to parse endpoint: %pOF\n",
+			to_of_node(ep));
+		return ret;
+	}
+
+	v4l2_async_notifier_init(&max9271->notifier);
+	max9271->asd = v4l2_async_notifier_add_fwnode_subdev(&max9271->notifier,
+					      remote, struct v4l2_async_subdev);
+	fwnode_handle_put(remote);
+	if (IS_ERR(max9271->asd))
+		return PTR_ERR(max9271->asd);
+
+	max9271->notifier.ops = &max9271_notifier_ops;
+	max9271->notifier.flags = V4L2_ASYNC_NOTIFIER_DEFER_POST_REGISTER;
+	ret = v4l2_async_subdev_notifier_register(&max9271->sd,
+						  &max9271->notifier);
+	if (ret < 0) {
+		v4l2_async_notifier_cleanup(&max9271->notifier);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int max9271_init(struct max9271_device *max9271)
+{
+	int ret;
+	u8 addr;
+
+	max9271_wake_up(max9271);
+
+	/* Re-program the chip address. */
+	addr = max9271->client->addr;
+	max9271->client->addr = MAX9271_DEFAULT_ADDR;
+	ret = max9271_set_address(max9271, addr);
+	if (ret < 0)
+		return ret;
+	max9271->client->addr = addr;
+
+	/* Serial link disabled during conf as it needs a valid pixel clock. */
+	ret = max9271_set_serial_link(max9271, false);
+	if (ret)
+		return ret;
+
+	/*
+	 *  Ensure that we have a good link configuration before attempting to
+	 *  identify the device.
+	 */
+	ret = max9271_configure_i2c(max9271, MAX9271_I2CSLVSH_469NS_234NS |
+					     MAX9271_I2CSLVTO_1024US |
+					     MAX9271_I2CMSTBT_105KBPS);
+	if (ret)
+		return ret;
+
+	/*
+	 * Set reverse channel high threshold to increase noise immunity.
+	 *
+	 * This should be compensated by increasing the reverse channel
+	 * amplitude on the remote deserializer side.
+	 */
+	return max9271_set_high_threshold(max9271, true);
+}
+
+static int max9271_probe(struct i2c_client *client)
+{
+	struct max9271_device *max9271;
+	struct fwnode_handle *ep;
+	int ret;
+
+	max9271 = devm_kzalloc(&client->dev, sizeof(*max9271), GFP_KERNEL);
+	if (!max9271)
+		return -ENOMEM;
+	max9271->dev = &client->dev;
+	max9271->client = client;
+
+	/* Initialize and register the subdevice. */
+	v4l2_i2c_subdev_init(&max9271->sd, client, &max9271_subdev_ops);
+	max9271->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	max9271->pads[MAX9271_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
+	max9271->pads[MAX9271_SINK_PAD].flags = MEDIA_PAD_FL_SINK;
+	max9271->sd.entity.flags |= MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+	ret = media_entity_pads_init(&max9271->sd.entity, 2, max9271->pads);
+	if (ret < 0)
+		return ret;
+
+	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 0, 0, 0);
+	if (!ep) {
+		dev_err(max9271->dev, "Unable to get endpoint 0: %pOF\n",
+			max9271->dev->of_node);
+		ret = -ENODEV;
+		goto error_media_entity;
+	}
+
+	max9271->sd.fwnode = ep;
+	ret = v4l2_async_register_subdev(&max9271->sd);
+	if (ret)
+		goto error_put_node;
+
+	ret = max9271_parse_dt(max9271);
+	if (ret)
+		goto error_unregister_subdev;
+
+	ret = max9271_init(max9271);
+	if (ret)
+		goto error_unregister_subdev;
+
+	return 0;
+
+error_unregister_subdev:
+	v4l2_async_unregister_subdev(&max9271->sd);
+error_put_node:
+	fwnode_handle_put(max9271->sd.fwnode);
+error_media_entity:
+	media_entity_cleanup(&max9271->sd.entity);
+
+	return ret;
+}
+
+static int max9271_remove(struct i2c_client *client)
+{
+	struct max9271_device *max9271 = i2c_to_max9271(client);
+
+	v4l2_ctrl_handler_free(&max9271->ctrls);
+	v4l2_async_notifier_cleanup(&max9271->notifier);
+	v4l2_async_unregister_subdev(&max9271->sd);
+	fwnode_handle_put(max9271->sd.fwnode);
+	media_entity_cleanup(&max9271->sd.entity);
+
+	return 0;
+}
+
+static const struct of_device_id max9271_of_ids[] = {
+	{ .compatible = "imi,max9271", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, max9271_of_ids);
+
+static struct i2c_driver max9271_i2c_driver = {
+	.driver	= {
+		.name	= "max9271",
+		.of_match_table = max9271_of_ids,
+	},
+	.probe_new	= max9271_probe,
+	.remove		= max9271_remove,
+};
+
+module_i2c_driver(max9271_i2c_driver);
+
+MODULE_DESCRIPTION("MAX9271 GMSL serializer subdevice driver");
+MODULE_AUTHOR("Jacopo Mondi");
+MODULE_LICENSE("GPL");
-- 
2.32.0


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

* [RFC 3/5] media: i2c: rdacm20: Adapt to work with MAX9271
  2021-08-17  7:26 [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Jacopo Mondi
  2021-08-17  7:26 ` [RFC 1/5] media: i2c: max9271: Rename max9271 library driver Jacopo Mondi
  2021-08-17  7:27 ` [RFC 2/5] media: i2c: Add MAX9271 I2C driver Jacopo Mondi
@ 2021-08-17  7:27 ` Jacopo Mondi
  2021-08-17  7:27 ` [RFC 4/5] media: i2c: max9286: Fetch PIXEL_RATE in s_stream Jacopo Mondi
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 21+ messages in thread
From: Jacopo Mondi @ 2021-08-17  7:27 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Kieran Bingham, Laurent Pinchart,
	Niklas Söderlund
  Cc: Jacopo Mondi, Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam,
	Thomas NIZAN, linux-renesas-soc, linux-media

The rdacm20 camera module driver was designed to work with the MAX9271
library driver now named max9271-lib.

With the introduction of an i2c subdevice driver for the MAX9271
serializer adapt the camera module driver to work with it by removing
calls to the max9271 handling functions.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
 drivers/media/i2c/rdacm20.c | 139 ++++++------------------------------
 1 file changed, 23 insertions(+), 116 deletions(-)

diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
index bf06a1c50306..1cc8a7478b6f 100644
--- a/drivers/media/i2c/rdacm20.c
+++ b/drivers/media/i2c/rdacm20.c
@@ -27,8 +27,6 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-subdev.h>
 
-#include "max9271-lib.h"
-
 #define OV10635_I2C_ADDRESS		0x30
 
 #define OV10635_SOFTWARE_RESET		0x0103
@@ -312,8 +310,7 @@ static const struct ov10635_reg {
 
 struct rdacm20_device {
 	struct device			*dev;
-	struct max9271_device		serializer;
-	struct i2c_client		*sensor;
+	struct i2c_client		*client;
 	struct v4l2_subdev		sd;
 	struct media_pad		pad;
 	struct v4l2_ctrl_handler	ctrls;
@@ -335,14 +332,14 @@ 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);
+	ret = i2c_master_send(dev->client, buf, 2);
 	if (ret != 2) {
 		dev_dbg(dev->dev, "%s: register 0x%04x write failed (%d)\n",
 			__func__, reg, ret);
 		return ret;
 	}
 
-	ret = i2c_master_recv(dev->sensor, buf, 2);
+	ret = i2c_master_recv(dev->client, buf, 2);
 	if (ret < 0) {
 		dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n",
 			__func__, reg, ret);
@@ -359,7 +356,7 @@ static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
 
 	dev_dbg(dev->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
 
-	ret = i2c_master_send(dev->sensor, buf, 3);
+	ret = i2c_master_send(dev->client, buf, 3);
 	return ret < 0 ? ret : 0;
 }
 
@@ -379,11 +376,17 @@ static int ov10635_set_regs(struct rdacm20_device *dev,
 			    const struct ov10635_reg *regs,
 			    unsigned int nr_regs)
 {
+	unsigned int retry;
 	unsigned int i;
 	int ret;
 
 	for (i = 0; i < nr_regs; i++) {
+		retry = 3;
+again:
 		ret = __ov10635_write(dev, regs[i].reg, regs[i].val);
+		if (ret && retry--)
+			goto again;
+
 		if (ret) {
 			dev_err(dev->dev,
 				"%s: register %u (0x%04x) write failed (%d)\n",
@@ -397,9 +400,7 @@ static int ov10635_set_regs(struct rdacm20_device *dev,
 
 static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
 {
-	struct rdacm20_device *dev = sd_to_rdacm20(sd);
-
-	return max9271_set_serial_link(&dev->serializer, enable);
+	return 0;
 }
 
 static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd,
@@ -440,37 +441,10 @@ static int rdacm20_post_register(struct v4l2_subdev *sd)
 	struct rdacm20_device *dev = sd_to_rdacm20(sd);
 	unsigned int retry = 3;
 	int ret;
+	u8 addr;
 
-	/*
-	 * Hold OV10635 in reset during max9271 configuration. The reset signal
-	 * has to be asserted for at least 200 microseconds.
-	 */
-	ret = max9271_enable_gpios(&dev->serializer, MAX9271_GPIO1OUT);
-	if (ret)
-		return ret;
-
-	ret = max9271_clear_gpios(&dev->serializer, MAX9271_GPIO1OUT);
-	if (ret)
-		return ret;
-	usleep_range(200, 500);
-
-	ret = max9271_configure_gmsl_link(&dev->serializer);
-	if (ret)
-		return ret;
-
-	ret = max9271_verify_id(&dev->serializer);
-	if (ret < 0)
-		return ret;
-
-	/*
-	 * Release ov10635 from reset and initialize it. The image sensor
-	 * requires at least 2048 XVCLK cycles (85 micro-seconds at 24MHz)
-	 * before being available. Stay safe and wait up to 500 micro-seconds.
-	 */
-	ret = max9271_set_gpios(&dev->serializer, MAX9271_GPIO1OUT);
-	if (ret)
-		return ret;
-	usleep_range(100, 500);
+	addr = dev->client->addr;
+	dev->client->addr = OV10635_I2C_ADDRESS;
 
 again:
 	ret = ov10635_read16(dev, OV10635_PID);
@@ -492,23 +466,22 @@ static int rdacm20_post_register(struct v4l2_subdev *sd)
 		return -ENXIO;
 	}
 
+	/* Program the 0V10635 initial configuration. */
+	ret = ov10635_set_regs(dev, ov10635_regs_wizard,
+			       ARRAY_SIZE(ov10635_regs_wizard));
+	if (ret)
+		return ret;
+
 	/* Change the sensor I2C address. */
 	ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
-			    (dev->addrs[1] << 1) |
-			    OV10635_SC_CMMN_SCCB_ID_SELECT);
+			    (addr << 1) | OV10635_SC_CMMN_SCCB_ID_SELECT);
 	if (ret < 0) {
 		dev_err(dev->dev,
 			"OV10635 I2C address change failed (%d)\n", ret);
 		return ret;
 	}
-	dev->sensor->addr = dev->addrs[1];
 	usleep_range(3500, 5000);
-
-	/* Program the 0V10635 initial configuration. */
-	ret = ov10635_set_regs(dev, ov10635_regs_wizard,
-			       ARRAY_SIZE(ov10635_regs_wizard));
-	if (ret)
-		return ret;
+	dev->client->addr = addr;
 
 	dev_info(dev->dev, "Identified RDACM20 camera module\n");
 
@@ -535,48 +508,6 @@ static const struct v4l2_subdev_ops rdacm20_subdev_ops = {
 	.pad		= &rdacm20_subdev_pad_ops,
 };
 
-static int rdacm20_initialize(struct rdacm20_device *dev)
-{
-	int ret;
-
-	max9271_wake_up(&dev->serializer);
-
-	/* Serial link disabled during config as it needs a valid pixel clock. */
-	ret = max9271_set_serial_link(&dev->serializer, false);
-	if (ret)
-		return ret;
-
-	/*
-	 *  Ensure that we have a good link configuration before attempting to
-	 *  identify the device.
-	 */
-	ret = max9271_configure_i2c(&dev->serializer,
-				    MAX9271_I2CSLVSH_469NS_234NS |
-				    MAX9271_I2CSLVTO_1024US |
-				    MAX9271_I2CMSTBT_105KBPS);
-	if (ret)
-		return ret;
-
-	ret = max9271_set_address(&dev->serializer, dev->addrs[0]);
-	if (ret < 0)
-		return ret;
-	dev->serializer.client->addr = dev->addrs[0];
-
-	/*
-	 * Set reverse channel high threshold to increase noise immunity.
-	 *
-	 * This should be compensated by increasing the reverse channel
-	 * amplitude on the remote deserializer side.
-	 *
-	 * TODO Inspect the embedded MCU programming sequence to make sure
-	 * there are no conflicts with the configuration applied here.
-	 *
-	 * TODO Clarify the embedded MCU startup delay to avoid write
-	 * collisions on the I2C bus.
-	 */
-	return max9271_set_high_threshold(&dev->serializer, true);
-}
-
 static int rdacm20_probe(struct i2c_client *client)
 {
 	struct rdacm20_device *dev;
@@ -587,27 +518,7 @@ static int rdacm20_probe(struct i2c_client *client)
 	if (!dev)
 		return -ENOMEM;
 	dev->dev = &client->dev;
-	dev->serializer.client = client;
-
-	ret = of_property_read_u32_array(client->dev.of_node, "reg",
-					 dev->addrs, 2);
-	if (ret < 0) {
-		dev_err(dev->dev, "Invalid DT reg property: %d\n", ret);
-		return -EINVAL;
-	}
-
-	/* Create the dummy I2C client for the sensor. */
-	dev->sensor = i2c_new_dummy_device(client->adapter,
-					   OV10635_I2C_ADDRESS);
-	if (IS_ERR(dev->sensor)) {
-		ret = PTR_ERR(dev->sensor);
-		goto error;
-	}
-
-	/* Initialize the hardware. */
-	ret = rdacm20_initialize(dev);
-	if (ret < 0)
-		goto error;
+	dev->client  = client;
 
 	/* Initialize and register the subdevice. */
 	v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops);
@@ -649,10 +560,7 @@ static int rdacm20_probe(struct i2c_client *client)
 	fwnode_handle_put(ep);
 error_free_ctrls:
 	v4l2_ctrl_handler_free(&dev->ctrls);
-error:
 	media_entity_cleanup(&dev->sd.entity);
-	if (dev->sensor)
-		i2c_unregister_device(dev->sensor);
 
 	dev_err(&client->dev, "probe failed\n");
 
@@ -667,7 +575,6 @@ static int rdacm20_remove(struct i2c_client *client)
 	v4l2_async_unregister_subdev(&dev->sd);
 	v4l2_ctrl_handler_free(&dev->ctrls);
 	media_entity_cleanup(&dev->sd.entity);
-	i2c_unregister_device(dev->sensor);
 
 	return 0;
 }
-- 
2.32.0


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

* [RFC 4/5] media: i2c: max9286: Fetch PIXEL_RATE in s_stream
  2021-08-17  7:26 [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Jacopo Mondi
                   ` (2 preceding siblings ...)
  2021-08-17  7:27 ` [RFC 3/5] media: i2c: rdacm20: Adapt to work with MAX9271 Jacopo Mondi
@ 2021-08-17  7:27 ` Jacopo Mondi
  2021-08-23  2:17   ` Laurent Pinchart
  2021-08-17  7:27 ` [RFC 5/5] arm64: dts: GMSL: Adapt to the use max9271 driver Jacopo Mondi
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 21+ messages in thread
From: Jacopo Mondi @ 2021-08-17  7:27 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Kieran Bingham, Laurent Pinchart,
	Niklas Söderlund
  Cc: Jacopo Mondi, Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam,
	Thomas NIZAN, linux-renesas-soc, linux-media

The max9286 driver needs to fetch the remote serializer PIXEL_RATE
control value in order to compute its own one, as the sum of the
values reported by the connected subdevices.

Currently the control is verified to be present at notifier's bound
time, which requires the serializer driver to register the control at
probe time. As the serializer driver might need to register the control
later, by adding the control handler of its connected sensor, post-pone
the max9286 check for the control availability at start stream time.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
 drivers/media/i2c/max9286.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 1b92d18a1f94..98fc90339a9e 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -595,7 +595,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
 	max9286_check_config_link(priv, priv->source_mask);
 	max9286_configure_i2c(priv, false);
 
-	return max9286_set_pixelrate(priv);
+	return 0;
 }
 
 static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
@@ -674,6 +674,10 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
 	int ret;
 
 	if (enable) {
+		ret = max9286_set_pixelrate(priv);
+		if (ret)
+			return ret;
+
 		/*
 		 * The frame sync between cameras is transmitted across the
 		 * reverse channel as GPIO. We must open all channels while
-- 
2.32.0


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

* [RFC 5/5] arm64: dts: GMSL: Adapt to the use max9271 driver
  2021-08-17  7:26 [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Jacopo Mondi
                   ` (3 preceding siblings ...)
  2021-08-17  7:27 ` [RFC 4/5] media: i2c: max9286: Fetch PIXEL_RATE in s_stream Jacopo Mondi
@ 2021-08-17  7:27 ` Jacopo Mondi
  2021-08-18 12:47 ` [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Kieran Bingham
  2021-08-23  2:06 ` Laurent Pinchart
  6 siblings, 0 replies; 21+ messages in thread
From: Jacopo Mondi @ 2021-08-17  7:27 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Kieran Bingham, Laurent Pinchart,
	Niklas Söderlund
  Cc: Jacopo Mondi, Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam,
	Thomas NIZAN, linux-renesas-soc, linux-media

Currently the whole RDACM20/21 camera module was handled by a single
driver and a single device node entry was required in DTS to describe
it.

With the introduction of the max9271 subdevice driver the camera module
is now described by two device nodes, one for the serializer and one for
the image sensor connected to it.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
 arch/arm64/boot/dts/renesas/gmsl-cameras.dtsi | 34 ++++++++++++++++---
 .../arm64/boot/dts/renesas/r8a77970-eagle.dts |  6 ++--
 2 files changed, 32 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/boot/dts/renesas/gmsl-cameras.dtsi b/arch/arm64/boot/dts/renesas/gmsl-cameras.dtsi
index d45f072f8cdf..7d8f2e979134 100644
--- a/arch/arm64/boot/dts/renesas/gmsl-cameras.dtsi
+++ b/arch/arm64/boot/dts/renesas/gmsl-cameras.dtsi
@@ -131,13 +131,37 @@ i2c-mux {
 		i2c@0 {
 			status = "okay";
 
-			camera@51 {
-				compatible = GMSL_CAMERA_MODEL;
-				reg = <0x51>, <0x61>;
+			serializer@51 {
+				compatible = "maxim,max9271";
+				reg = <0x51>;
+
+				ports {
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					port@0 {
+						reg = <0>;
+						fakra_con0: endpoint {
+							remote-endpoint = <&max9286_in0>;
+						};
+					};
+
+					port@1 {
+						reg = <1>;
+						sensor_in0: endpoint {
+							remote-endpoint = <&sensor_out0>;
+						};
+					};
+				};
+			};
+
+			camera@61 {
+				compatible = "imi,rdacm20";
+				reg = <0x61>;
 
 				port {
-					fakra_con0: endpoint {
-						remote-endpoint = <&max9286_in0>;
+					sensor_out0: endpoint {
+						remote-endpoint = <&sensor_in0>;
 					};
 				};
 			};
diff --git a/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts b/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts
index 96c807bf868c..22bb04914159 100644
--- a/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts
+++ b/arch/arm64/boot/dts/renesas/r8a77970-eagle.dts
@@ -388,7 +388,7 @@ &scif0 {
 /* FAKRA Overlay */
 #define GMSL_CAMERA_RDACM20
 #define GMSL_CAMERA_0
-#define GMSL_CAMERA_1
-#define GMSL_CAMERA_2
-#define GMSL_CAMERA_3
+//#define GMSL_CAMERA_1
+//#define GMSL_CAMERA_2
+//#define GMSL_CAMERA_3
 #include "gmsl-cameras.dtsi"
-- 
2.32.0


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

* Re: [RFC 2/5] media: i2c: Add MAX9271 I2C driver
  2021-08-17  7:27 ` [RFC 2/5] media: i2c: Add MAX9271 I2C driver Jacopo Mondi
@ 2021-08-17 15:49   ` Kieran Bingham
  2021-08-18  8:27     ` Jacopo Mondi
  0 siblings, 1 reply; 21+ messages in thread
From: Kieran Bingham @ 2021-08-17 15:49 UTC (permalink / raw)
  To: Jacopo Mondi, Mauro Carvalho Chehab, Kieran Bingham,
	Laurent Pinchart, Niklas Söderlund
  Cc: Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	linux-renesas-soc, linux-media

Hi Jacopo,

On 17/08/2021 08:27, Jacopo Mondi wrote:
> The MAX9271 is a GMSL serializer that serializes a video stream
> received from an image sensor through the parallel video bus.


https://datasheets.maximintegrated.com/en/ds/MAX9271.pdf calls it a
"16-Bit GMSL Serializer with Coas or STP Cable Drive"


> The serializer it's usually found embedded with an image sensor and

s/it's/is/

> other ancillary chips in camera modules like RDACM20 and RDACM21.
> 
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> ---
>  MAINTAINERS                 |   9 +
>  drivers/media/i2c/Kconfig   |  12 +
>  drivers/media/i2c/Makefile  |   1 +
>  drivers/media/i2c/max9271.c | 756 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 778 insertions(+)
>  create mode 100644 drivers/media/i2c/max9271.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7ad89cac19b7..2dab25a08c9c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11244,6 +11244,15 @@ F:	Documentation/hwmon/max6697.rst
>  F:	drivers/hwmon/max6697.c
>  F:	include/linux/platform_data/max6697.h
>  
> +MAX9271 GMSL SERIALIZER 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:	drivers/media/i2c/max9271.c
> +
>  MAX9286 QUAD GMSL DESERIALIZER DRIVER
>  M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
>  M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 08feb3e8c1bf..b793d1f322d9 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -1300,6 +1300,18 @@ source "drivers/media/i2c/m5mols/Kconfig"
>  config VIDEO_MAX9271_LIB
>  	tristate
>  
> +config VIDEO_MAX9271
> +	tristate "MAX9271 GMSL serializer support"
> +	depends on I2C
> +	select V4L2_FWNODE
> +	select VIDEO_V4L2_SUBDEV_API
> +	select MEDIA_CONTROLLER
> +	help
> +	  This driver supports the Maxim MAX9271 GMSL serializer.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called max9271.
> +
>  config VIDEO_RDACM20
>  	tristate "IMI RDACM20 camera support"
>  	depends on I2C
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 4d879373bd48..37bb51065574 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -130,6 +130,7 @@ obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
>  obj-$(CONFIG_VIDEO_IMX412)	+= imx412.o
>  obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
>  obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271-lib.o
> +obj-$(CONFIG_VIDEO_MAX9271)	+= max9271.o
>  obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
>  obj-$(CONFIG_VIDEO_RDACM21)	+= rdacm21.o
>  obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
> diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c
> new file mode 100644
> index 000000000000..64987cba3d3e
> --- /dev/null
> +++ b/drivers/media/i2c/max9271.c
> @@ -0,0 +1,756 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2017-2021 Jacopo Mondi
> + * Copyright (C) 2017-2020 Kieran Bingham
> + * Copyright (C) 2017-2019 Laurent Pinchart
> + * Copyright (C) 2017-2019 Niklas Söderlund
> + * Copyright (C) 2016 Renesas Electronics Corporation
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/fwnode.h>
> +#include <linux/init.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/property.h>
> +
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +#define MAX9271_DEFAULT_ADDR	0x40
> +
> +/* Register 0x02 */
> +#define MAX9271_SPREAD_SPECT_0		(0 << 5)
> +#define MAX9271_SPREAD_SPECT_05		(1 << 5)
> +#define MAX9271_SPREAD_SPECT_15		(2 << 5)
> +#define MAX9271_SPREAD_SPECT_1		(5 << 5)
> +#define MAX9271_SPREAD_SPECT_2		(3 << 5)
> +#define MAX9271_SPREAD_SPECT_3		(6 << 5)
> +#define MAX9271_SPREAD_SPECT_4		(7 << 5)
> +#define MAX9271_R02_RES			BIT(4)
> +#define MAX9271_PCLK_AUTODETECT		(3 << 2)
> +#define MAX9271_SERIAL_AUTODETECT	(0x03)
> +/* Register 0x04 */
> +#define MAX9271_SEREN			BIT(7)
> +#define MAX9271_CLINKEN			BIT(6)
> +#define MAX9271_PRBSEN			BIT(5)
> +#define MAX9271_SLEEP			BIT(4)
> +#define MAX9271_INTTYPE_I2C		(0 << 2)
> +#define MAX9271_INTTYPE_UART		(1 << 2)
> +#define MAX9271_INTTYPE_NONE		(2 << 2)
> +#define MAX9271_REVCCEN			BIT(1)
> +#define MAX9271_FWDCCEN			BIT(0)
> +/* Register 0x07 */
> +#define MAX9271_DBL			BIT(7)
> +#define MAX9271_DRS			BIT(6)
> +#define MAX9271_BWS			BIT(5)
> +#define MAX9271_ES			BIT(4)
> +#define MAX9271_HVEN			BIT(2)
> +#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
> +#define MAX9271_EDC_6BIT_CRC		(1 << 0)
> +#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
> +/* Register 0x08 */
> +#define MAX9271_INVVS			BIT(7)
> +#define MAX9271_INVHS			BIT(6)
> +#define MAX9271_REV_LOGAIN		BIT(3)
> +#define MAX9271_REV_HIVTH		BIT(0)
> +/* Register 0x09 */
> +#define MAX9271_ID			0x09
> +/* Register 0x0d */
> +#define MAX9271_I2CLOCACK		BIT(7)
> +#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
> +#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
> +#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
> +#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
> +#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
> +#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
> +#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
> +#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
> +#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
> +#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
> +#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
> +#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
> +#define MAX9271_I2CSLVTO_NONE		(3 << 0)
> +#define MAX9271_I2CSLVTO_1024US		(2 << 0)
> +#define MAX9271_I2CSLVTO_256US		(1 << 0)
> +#define MAX9271_I2CSLVTO_64US		(0 << 0)
> +/* Register 0x0f */
> +#define MAX9271_GPIO5OUT		BIT(5)
> +#define MAX9271_GPIO4OUT		BIT(4)
> +#define MAX9271_GPIO3OUT		BIT(3)
> +#define MAX9271_GPIO2OUT		BIT(2)
> +#define MAX9271_GPIO1OUT		BIT(1)
> +#define MAX9271_GPO			BIT(0)
> +/* Register 0x15 */
> +#define MAX9271_PCLKDET			BIT(0)
> +
> +struct max9271_device {
> +	struct device			*dev;
> +	struct i2c_client		*client;
> +	struct v4l2_subdev		sd;
> +#define MAX9271_SOURCE_PAD	0
> +#define MAX9271_SINK_PAD	1
> +	struct media_pad		pads[2];
> +	struct v4l2_async_notifier	notifier;
> +	struct v4l2_async_subdev	*asd;
> +	struct v4l2_ctrl_handler	ctrls;
> +	struct v4l2_subdev		*sensor;
> +};
> +
> +static inline struct max9271_device *sd_to_max9271(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct max9271_device, sd);
> +}
> +
> +static inline struct max9271_device *i2c_to_max9271(struct i2c_client *client)
> +{
> +	return sd_to_max9271(i2c_get_clientdata(client));
> +}
> +
> +static inline struct max9271_device *notifier_to_max9271(
> +						struct v4l2_async_notifier *nf)
> +{
> +	return container_of(nf, struct max9271_device, notifier);
> +}
> +
> +/* --- MAX9271 hardware operations --- */
> +
> +static int max9271_read(struct max9271_device *dev, u8 reg)
> +{
> +	int ret;
> +
> +	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
> +
> +	ret = i2c_smbus_read_byte_data(dev->client, reg);
> +	if (ret < 0)
> +		dev_dbg(&dev->client->dev,
> +			"%s: register 0x%02x read failed (%d)\n",
> +			__func__, reg, ret);
> +
> +	return ret;
> +}
> +
> +static int max9271_write(struct max9271_device *dev, u8 reg, u8 val)
> +{
> +	int ret;
> +
> +	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
> +
> +	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
> +	if (ret < 0)
> +		dev_err(&dev->client->dev,
> +			"%s: register 0x%02x write failed (%d)\n",
> +			__func__, reg, ret);
> +
> +	return ret;
> +}
> +
> +/*
> + * max9271_pclk_detect() - Detect valid pixel clock from image sensor
> + *
> + * Wait up to 10ms for a valid pixel clock.
> + *
> + * Returns 0 for success, < 0 for pixel clock not properly detected
> + */
> +static int max9271_pclk_detect(struct max9271_device *dev)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < 100; i++) {
> +		ret = max9271_read(dev, 0x15);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (ret & MAX9271_PCLKDET)
> +			return 0;
> +
> +		usleep_range(50, 100);
> +	}
> +
> +	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
> +
> +	return -EIO;
> +}
> +
> +static void max9271_wake_up(struct max9271_device *dev)
> +{
> +	/*
> +	 * Use the chip default address as this function has to be called
> +	 * before any other one.
> +	 */
> +	dev->client->addr = MAX9271_DEFAULT_ADDR;
> +	i2c_smbus_read_byte(dev->client);
> +	usleep_range(5000, 8000);
> +}
> +
> +static int max9271_set_serial_link(struct max9271_device *dev, bool enable)
> +{
> +	int ret;
> +	u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN;
> +
> +	if (enable) {
> +		ret = max9271_pclk_detect(dev);
> +		if (ret)
> +			return ret;
> +
> +		val |= MAX9271_SEREN;
> +	} else {
> +		val |= MAX9271_CLINKEN;
> +	}
> +
> +	/*
> +	 * 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.
> +	 */
> +	ret = max9271_write(dev, 0x04, val);
> +	if (ret < 0)
> +		return ret;
> +
> +	usleep_range(5000, 8000);
> +
> +	return 0;
> +}
> +
> +static int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config)
> +{
> +	int ret;
> +
> +	ret = max9271_write(dev, 0x0d, i2c_config);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* The delay required after an I2C bus configuration change is not

/*
 * The delay ... ?



> +	 * characterized in the serializer manual. Sleep up to 5msec to
> +	 * stay safe.
> +	 */
> +	usleep_range(3500, 5000);
> +
> +	return 0;
> +}
> +
> +static int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
> +{
> +	int ret;
> +
> +	ret = max9271_read(dev, 0x08);
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * Enable or disable reverse channel high threshold to increase
> +	 * immunity to power supply noise.
> +	 */
> +	ret = max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
> +	if (ret < 0)
> +		return ret;
> +
> +	usleep_range(2000, 2500);
> +
> +	return 0;
> +}
> +
> +static int max9271_configure_gmsl_link(struct max9271_device *dev)
> +{
> +	int ret;
> +
> +	/*
> +	 * 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
> +	 *
> +	 * TODO: Make the GMSL link configuration parametric.
> +	 */
> +	ret = max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
> +			    MAX9271_EDC_1BIT_PARITY);
> +	if (ret < 0)
> +		return ret;
> +
> +	usleep_range(5000, 8000);
> +
> +	/*
> +	 * Adjust spread spectrum to +4% and auto-detect pixel clock
> +	 * and serial link rate.
> +	 */
> +	ret = max9271_write(dev, 0x02,
> +			    MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
> +			    MAX9271_PCLK_AUTODETECT |
> +			    MAX9271_SERIAL_AUTODETECT);
> +	if (ret < 0)
> +		return ret;
> +
> +	usleep_range(5000, 8000);
> +
> +	return 0;
> +}
> +
> +static int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask)
> +{
> +	int ret;
> +
> +	ret = max9271_read(dev, 0x0f);

What is 0x0f?

Ah - the registers are unnamed...

Seems a shame..


> +	if (ret < 0)
> +		return 0;
> +
> +	ret |= gpio_mask;
> +	ret = max9271_write(dev, 0x0f, ret);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	usleep_range(3500, 5000);
> +
> +	return 0;
> +}
> +
> +static int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask)
> +{
> +	int ret;
> +
> +	ret = max9271_read(dev, 0x0f);
> +	if (ret < 0)
> +		return 0;
> +
> +	ret &= ~gpio_mask;
> +	ret = max9271_write(dev, 0x0f, ret);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	usleep_range(3500, 5000);
> +
> +	return 0;
> +}
> +
> +static int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask)
> +{
> +	int ret;
> +
> +	ret = max9271_read(dev, 0x0e);
> +	if (ret < 0)
> +		return 0;
> +
> +	/* BIT(0) reserved: GPO is always enabled. */
> +	ret |= (gpio_mask & ~BIT(0));
> +	ret = max9271_write(dev, 0x0e, ret);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	usleep_range(3500, 5000);
> +
> +	return 0;
> +}
> +
> +static int max9271_verify_id(struct max9271_device *dev)
> +{
> +	int ret;
> +
> +	ret = max9271_read(dev, 0x1e);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	if (ret != MAX9271_ID) {
> +		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
> +			ret);
> +		return -ENXIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max9271_set_address(struct max9271_device *dev, u8 addr)
> +{
> +	int ret;
> +
> +	ret = max9271_write(dev, 0x00, addr << 1);
> +	if (ret < 0) {
> +		dev_err(&dev->client->dev,
> +			"MAX9271 I2C address change failed (%d)\n", ret);
> +		return ret;
> +	}
> +	usleep_range(3500, 5000);
> +
> +	return 0;
> +}
> +
> +/* --- V4L2 Subdev Ops --- */
> +
> +static int max9271_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct max9271_device *max9271 = sd_to_max9271(sd);
> +
> +	return max9271_set_serial_link(max9271, enable);
> +}
> +
> +static int max9271_enum_mbus_code(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_state *sd_state,
> +				  struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct max9271_device *max9271 = sd_to_max9271(sd);
> +
> +	return v4l2_subdev_call(max9271->sensor, pad, enum_mbus_code, NULL,
> +				code);
> +}
> +
> +static int max9271_get_fmt(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_state *sd_state,
> +			   struct v4l2_subdev_format *format)
> +{
> +	struct max9271_device *max9271 = sd_to_max9271(sd);
> +
> +	return v4l2_subdev_call(max9271->sensor, pad, get_fmt, NULL,
> +				format);
> +}
> +
> +static int max9271_set_fmt(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_state *sd_state,
> +			   struct v4l2_subdev_format *format)
> +{
> +	struct max9271_device *max9271 = sd_to_max9271(sd);
> +
> +	return v4l2_subdev_call(max9271->sensor, pad, set_fmt, NULL,
> +				format);
> +}
> +
> +static int max9271_post_register(struct v4l2_subdev *sd)
> +{
> +	struct max9271_device *max9271 = sd_to_max9271(sd);
> +	int ret;
> +
> +	ret = max9271_verify_id(max9271);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = max9271_enable_gpios(max9271, MAX9271_GPIO1OUT);
> +	if (ret)
> +		return ret;
> +
> +	ret = max9271_configure_gmsl_link(max9271);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops max9271_video_ops = {
> +	.s_stream	= max9271_s_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops max9271_subdev_pad_ops = {
> +	.enum_mbus_code = max9271_enum_mbus_code,
> +	.get_fmt	= max9271_get_fmt,
> +	.set_fmt	= max9271_set_fmt,
> +};
> +
> +static const struct v4l2_subdev_core_ops max9271_core_ops = {
> +	.post_register	= max9271_post_register,
> +};
> +
> +static const struct v4l2_subdev_ops max9271_subdev_ops = {
> +	.core		= &max9271_core_ops,
> +	.video		= &max9271_video_ops,
> +	.pad		= &max9271_subdev_pad_ops,
> +};
> +
> +/* --- V4L2 Async Notifier --- */
> +
> +static int max9271_notify_bound(struct v4l2_async_notifier *notifier,
> +				struct v4l2_subdev *subdev,
> +				struct v4l2_async_subdev *asd)
> +{
> +	struct max9271_device *max9271 = notifier_to_max9271(notifier);
> +	int ret, pad;
> +
> +	/*
> +	 * Reserve more space than necessary for controls inherited by the
> +	 * remote subdev.
> +	 */
> +	ret = v4l2_ctrl_handler_init(&max9271->ctrls, 16);
> +	if (ret < 0) {
> +		dev_err(max9271->dev,
> +			"Unable to initialize control handler: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = v4l2_ctrl_add_handler(&max9271->ctrls, subdev->ctrl_handler,
> +				    NULL, true);
> +	if (ret < 0) {
> +		dev_err(max9271->dev,
> +			"Unable to add subdev control handler: %d\n", ret);
> +		goto error_free_handler;
> +	}
> +	max9271->sd.ctrl_handler = &max9271->ctrls;
> +
> +	/* Create media link with the remote sensor source pad. */
> +	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
> +					  MEDIA_PAD_FL_SOURCE);
> +	if (pad < 0) {
> +		dev_err(max9271->dev,
> +			"Failed to find source pad for %s\n", subdev->name);
> +		ret = pad;
> +		goto error_free_handler;
> +	}
> +
> +	ret = media_create_pad_link(&subdev->entity, pad,
> +				    &max9271->sd.entity, MAX9271_SINK_PAD,
> +				    MEDIA_LNK_FL_ENABLED |
> +				    MEDIA_LNK_FL_IMMUTABLE);
> +	if (ret)
> +		goto error_free_handler;
> +
> +	max9271->sensor = subdev;
> +
> +	/*
> +	 * Hold OV10635 in reset during max9271 configuration. The reset signal

Hold the sensor in reset ...

But are all sensors guaranteed to be connected to the GPIO in the same way?

Will this cause us issues? - does the GPIO need to be modelled somehow
to determine what's connected to them?



> +	 * has to be asserted for at least 200 microseconds.
> +	 */
> +	ret = max9271_clear_gpios(max9271, MAX9271_GPIO1OUT);
> +	if (ret)
> +		return ret;
> +	usleep_range(200, 500);
> +
> +	/*
> +	 * Release ov10635 from reset and initialize it. The image sensor
> +	 * requires at least 2048 XVCLK cycles (85 micro-seconds at 24MHz)
> +	 * before being available. Stay safe and wait up to 500 micro-seconds.

More sensor specific ... what needs to be abstracted here?


> +	 */
> +	ret = max9271_set_gpios(max9271, MAX9271_GPIO1OUT);
> +	if (ret)
> +		return ret;
> +	usleep_range(100, 500);
> +
> +	/*
> +	 * Call the sensor post_register operation to complete its
> +	 * initialization.
> +	 */
> +	ret = v4l2_subdev_call(max9271->sensor, core, post_register);
> +	if (ret) {
> +		dev_err(max9271->dev, "Failed to initialize sensor %u\n", ret);
> +		goto error_remove_link;
> +	}
> +
> +	return 0;
> +
> +error_remove_link:
> +	media_entity_remove_links(&max9271->sd.entity);
> +	max9271->sensor = NULL;
> +
> +error_free_handler:
> +	v4l2_ctrl_handler_free(&max9271->ctrls);
> +	max9271->sd.ctrl_handler = NULL;
> +
> +	return ret;
> +}
> +
> +static void max9271_notify_unbind(struct v4l2_async_notifier *notifier,
> +				  struct v4l2_subdev *subdev,
> +				  struct v4l2_async_subdev *asd)
> +{
> +	struct max9271_device *max9271 = notifier_to_max9271(notifier);
> +
> +	media_entity_remove_links(&max9271->sd.entity);
> +	max9271->sensor = NULL;
> +}
> +
> +static const struct v4l2_async_notifier_operations max9271_notifier_ops = {
> +	.bound = max9271_notify_bound,
> +	.unbind = max9271_notify_unbind,
> +};
> +
> +static int max9271_parse_dt(struct max9271_device *max9271)
> +{
> +	struct fwnode_handle *ep, *remote;
> +	struct v4l2_fwnode_endpoint vep = {
> +		.bus_type = V4L2_MBUS_PARALLEL,
> +	};
> +	int ret;
> +
> +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 1, 0, 0);
> +	if (!ep) {
> +		dev_err(max9271->dev, "Unable to get sensor endpoint: %pOF\n",
> +			max9271->dev->of_node);
> +		return -ENOENT;
> +	}
> +
> +	remote = fwnode_graph_get_remote_endpoint(ep);
> +	if (!remote) {
> +		dev_err(max9271->dev, "Unable to get remote endpoint: %pOF\n",
> +			max9271->dev->of_node);
> +		return -ENOENT;
> +	}
> +
> +	ret = v4l2_fwnode_endpoint_parse(ep, &vep);
> +	fwnode_handle_put(ep);
> +	if (ret) {
> +		fwnode_handle_put(remote);
> +		dev_err(max9271->dev, "Unable to parse endpoint: %pOF\n",
> +			to_of_node(ep));
> +		return ret;
> +	}
> +
> +	v4l2_async_notifier_init(&max9271->notifier);
> +	max9271->asd = v4l2_async_notifier_add_fwnode_subdev(&max9271->notifier,
> +					      remote, struct v4l2_async_subdev);
> +	fwnode_handle_put(remote);
> +	if (IS_ERR(max9271->asd))
> +		return PTR_ERR(max9271->asd);
> +
> +	max9271->notifier.ops = &max9271_notifier_ops;
> +	max9271->notifier.flags = V4L2_ASYNC_NOTIFIER_DEFER_POST_REGISTER;

This looks new, I'll have to read up on it ...


> +	ret = v4l2_async_subdev_notifier_register(&max9271->sd,
> +						  &max9271->notifier);
> +	if (ret < 0) {
> +		v4l2_async_notifier_cleanup(&max9271->notifier);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max9271_init(struct max9271_device *max9271)
> +{
> +	int ret;
> +	u8 addr;
> +
> +	max9271_wake_up(max9271);

This call has just set max9271->client->addr = MAX9271_DEFAULT_ADDR, so
the original has now been lost.


> +
> +	/* Re-program the chip address. */
> +	addr = max9271->client->addr;
> +	max9271->client->addr = MAX9271_DEFAULT_ADDR;
> +	ret = max9271_set_address(max9271, addr);

So this is currently broken :S

> +	if (ret < 0)
> +		return ret;
> +	max9271->client->addr = addr;
> +
> +	/* Serial link disabled during conf as it needs a valid pixel clock. */
> +	ret = max9271_set_serial_link(max9271, false);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 *  Ensure that we have a good link configuration before attempting to
> +	 *  identify the device.
> +	 */
> +	ret = max9271_configure_i2c(max9271, MAX9271_I2CSLVSH_469NS_234NS |
> +					     MAX9271_I2CSLVTO_1024US |
> +					     MAX9271_I2CMSTBT_105KBPS);

Are these parameters tied to the max9286 ?


> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Set reverse channel high threshold to increase noise immunity.
> +	 *
> +	 * This should be compensated by increasing the reverse channel
> +	 * amplitude on the remote deserializer side.
> +	 */
> +	return max9271_set_high_threshold(max9271, true);
> +}
> +
> +static int max9271_probe(struct i2c_client *client)
> +{
> +	struct max9271_device *max9271;
> +	struct fwnode_handle *ep;
> +	int ret;
> +
> +	max9271 = devm_kzalloc(&client->dev, sizeof(*max9271), GFP_KERNEL);
> +	if (!max9271)
> +		return -ENOMEM;
> +	max9271->dev = &client->dev;
> +	max9271->client = client;
> +
> +	/* Initialize and register the subdevice. */
> +	v4l2_i2c_subdev_init(&max9271->sd, client, &max9271_subdev_ops);
> +	max9271->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	max9271->pads[MAX9271_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
> +	max9271->pads[MAX9271_SINK_PAD].flags = MEDIA_PAD_FL_SINK;
> +	max9271->sd.entity.flags |= MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;

Is it a formatter - do we need a new/different entity type?


> +	ret = media_entity_pads_init(&max9271->sd.entity, 2, max9271->pads);
> +	if (ret < 0)
> +		return ret;
> +
> +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 0, 0, 0);
> +	if (!ep) {
> +		dev_err(max9271->dev, "Unable to get endpoint 0: %pOF\n",
> +			max9271->dev->of_node);
> +		ret = -ENODEV;
> +		goto error_media_entity;
> +	}
> +
> +	max9271->sd.fwnode = ep;
> +	ret = v4l2_async_register_subdev(&max9271->sd);
> +	if (ret)
> +		goto error_put_node;
> +
> +	ret = max9271_parse_dt(max9271);
> +	if (ret)
> +		goto error_unregister_subdev;
> +
> +	ret = max9271_init(max9271);
> +	if (ret)
> +		goto error_unregister_subdev;
> +
> +	return 0;
> +
> +error_unregister_subdev:
> +	v4l2_async_unregister_subdev(&max9271->sd);
> +error_put_node:
> +	fwnode_handle_put(max9271->sd.fwnode);
> +error_media_entity:
> +	media_entity_cleanup(&max9271->sd.entity);
> +
> +	return ret;
> +}
> +
> +static int max9271_remove(struct i2c_client *client)
> +{
> +	struct max9271_device *max9271 = i2c_to_max9271(client);
> +
> +	v4l2_ctrl_handler_free(&max9271->ctrls);
> +	v4l2_async_notifier_cleanup(&max9271->notifier);
> +	v4l2_async_unregister_subdev(&max9271->sd);
> +	fwnode_handle_put(max9271->sd.fwnode);
> +	media_entity_cleanup(&max9271->sd.entity);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id max9271_of_ids[] = {
> +	{ .compatible = "imi,max9271", },

This should be a 'maxim' prefix now, not IMI.


> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, max9271_of_ids);
> +
> +static struct i2c_driver max9271_i2c_driver = {
> +	.driver	= {
> +		.name	= "max9271",
> +		.of_match_table = max9271_of_ids,
> +	},
> +	.probe_new	= max9271_probe,
> +	.remove		= max9271_remove,
> +};
> +
> +module_i2c_driver(max9271_i2c_driver);
> +
> +MODULE_DESCRIPTION("MAX9271 GMSL serializer subdevice driver");
> +MODULE_AUTHOR("Jacopo Mondi");
> +MODULE_LICENSE("GPL");
> 

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

* Re: [RFC 2/5] media: i2c: Add MAX9271 I2C driver
  2021-08-17 15:49   ` Kieran Bingham
@ 2021-08-18  8:27     ` Jacopo Mondi
  2021-08-18 12:38       ` Kieran Bingham
                         ` (2 more replies)
  0 siblings, 3 replies; 21+ messages in thread
From: Jacopo Mondi @ 2021-08-18  8:27 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: Jacopo Mondi, Mauro Carvalho Chehab, Kieran Bingham,
	Laurent Pinchart, Niklas Söderlund, Hans Verkuil,
	Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	linux-renesas-soc, linux-media

Hi Kieran, thanks for review!

On Tue, Aug 17, 2021 at 04:49:17PM +0100, Kieran Bingham wrote:
> Hi Jacopo,
>
> On 17/08/2021 08:27, Jacopo Mondi wrote:
> > The MAX9271 is a GMSL serializer that serializes a video stream
> > received from an image sensor through the parallel video bus.
>
>
> https://datasheets.maximintegrated.com/en/ds/MAX9271.pdf calls it a
> "16-Bit GMSL Serializer with Coas or STP Cable Drive"
>

Nice we have a public datasheet now!
Nice, well, it might mean GMSL is just old ?

>
> > The serializer it's usually found embedded with an image sensor and
>
> s/it's/is/
>
> > other ancillary chips in camera modules like RDACM20 and RDACM21.
> >
> > Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> > ---
> >  MAINTAINERS                 |   9 +
> >  drivers/media/i2c/Kconfig   |  12 +
> >  drivers/media/i2c/Makefile  |   1 +
> >  drivers/media/i2c/max9271.c | 756 ++++++++++++++++++++++++++++++++++++
> >  4 files changed, 778 insertions(+)
> >  create mode 100644 drivers/media/i2c/max9271.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 7ad89cac19b7..2dab25a08c9c 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -11244,6 +11244,15 @@ F:	Documentation/hwmon/max6697.rst
> >  F:	drivers/hwmon/max6697.c
> >  F:	include/linux/platform_data/max6697.h
> >
> > +MAX9271 GMSL SERIALIZER 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:	drivers/media/i2c/max9271.c
> > +
> >  MAX9286 QUAD GMSL DESERIALIZER DRIVER
> >  M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
> >  M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> > diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> > index 08feb3e8c1bf..b793d1f322d9 100644
> > --- a/drivers/media/i2c/Kconfig
> > +++ b/drivers/media/i2c/Kconfig
> > @@ -1300,6 +1300,18 @@ source "drivers/media/i2c/m5mols/Kconfig"
> >  config VIDEO_MAX9271_LIB
> >  	tristate
> >
> > +config VIDEO_MAX9271
> > +	tristate "MAX9271 GMSL serializer support"
> > +	depends on I2C
> > +	select V4L2_FWNODE
> > +	select VIDEO_V4L2_SUBDEV_API
> > +	select MEDIA_CONTROLLER
> > +	help
> > +	  This driver supports the Maxim MAX9271 GMSL serializer.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > +	  module will be called max9271.
> > +
> >  config VIDEO_RDACM20
> >  	tristate "IMI RDACM20 camera support"
> >  	depends on I2C
> > diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> > index 4d879373bd48..37bb51065574 100644
> > --- a/drivers/media/i2c/Makefile
> > +++ b/drivers/media/i2c/Makefile
> > @@ -130,6 +130,7 @@ obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
> >  obj-$(CONFIG_VIDEO_IMX412)	+= imx412.o
> >  obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
> >  obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271-lib.o
> > +obj-$(CONFIG_VIDEO_MAX9271)	+= max9271.o
> >  obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
> >  obj-$(CONFIG_VIDEO_RDACM21)	+= rdacm21.o
> >  obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
> > diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c
> > new file mode 100644
> > index 000000000000..64987cba3d3e
> > --- /dev/null
> > +++ b/drivers/media/i2c/max9271.c
> > @@ -0,0 +1,756 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (C) 2017-2021 Jacopo Mondi
> > + * Copyright (C) 2017-2020 Kieran Bingham
> > + * Copyright (C) 2017-2019 Laurent Pinchart
> > + * Copyright (C) 2017-2019 Niklas Söderlund
> > + * Copyright (C) 2016 Renesas Electronics Corporation
> > + * Copyright (C) 2015 Cogent Embedded, Inc.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/fwnode.h>
> > +#include <linux/init.h>
> > +#include <linux/i2c.h>
> > +#include <linux/module.h>
> > +#include <linux/property.h>
> > +
> > +#include <media/v4l2-async.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-subdev.h>
> > +
> > +#define MAX9271_DEFAULT_ADDR	0x40
> > +
> > +/* Register 0x02 */
> > +#define MAX9271_SPREAD_SPECT_0		(0 << 5)
> > +#define MAX9271_SPREAD_SPECT_05		(1 << 5)
> > +#define MAX9271_SPREAD_SPECT_15		(2 << 5)
> > +#define MAX9271_SPREAD_SPECT_1		(5 << 5)
> > +#define MAX9271_SPREAD_SPECT_2		(3 << 5)
> > +#define MAX9271_SPREAD_SPECT_3		(6 << 5)
> > +#define MAX9271_SPREAD_SPECT_4		(7 << 5)
> > +#define MAX9271_R02_RES			BIT(4)
> > +#define MAX9271_PCLK_AUTODETECT		(3 << 2)
> > +#define MAX9271_SERIAL_AUTODETECT	(0x03)
> > +/* Register 0x04 */
> > +#define MAX9271_SEREN			BIT(7)
> > +#define MAX9271_CLINKEN			BIT(6)
> > +#define MAX9271_PRBSEN			BIT(5)
> > +#define MAX9271_SLEEP			BIT(4)
> > +#define MAX9271_INTTYPE_I2C		(0 << 2)
> > +#define MAX9271_INTTYPE_UART		(1 << 2)
> > +#define MAX9271_INTTYPE_NONE		(2 << 2)
> > +#define MAX9271_REVCCEN			BIT(1)
> > +#define MAX9271_FWDCCEN			BIT(0)
> > +/* Register 0x07 */
> > +#define MAX9271_DBL			BIT(7)
> > +#define MAX9271_DRS			BIT(6)
> > +#define MAX9271_BWS			BIT(5)
> > +#define MAX9271_ES			BIT(4)
> > +#define MAX9271_HVEN			BIT(2)
> > +#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
> > +#define MAX9271_EDC_6BIT_CRC		(1 << 0)
> > +#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
> > +/* Register 0x08 */
> > +#define MAX9271_INVVS			BIT(7)
> > +#define MAX9271_INVHS			BIT(6)
> > +#define MAX9271_REV_LOGAIN		BIT(3)
> > +#define MAX9271_REV_HIVTH		BIT(0)
> > +/* Register 0x09 */
> > +#define MAX9271_ID			0x09
> > +/* Register 0x0d */
> > +#define MAX9271_I2CLOCACK		BIT(7)
> > +#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
> > +#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
> > +#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
> > +#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
> > +#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
> > +#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
> > +#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
> > +#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
> > +#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
> > +#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
> > +#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
> > +#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
> > +#define MAX9271_I2CSLVTO_NONE		(3 << 0)
> > +#define MAX9271_I2CSLVTO_1024US		(2 << 0)
> > +#define MAX9271_I2CSLVTO_256US		(1 << 0)
> > +#define MAX9271_I2CSLVTO_64US		(0 << 0)
> > +/* Register 0x0f */
> > +#define MAX9271_GPIO5OUT		BIT(5)
> > +#define MAX9271_GPIO4OUT		BIT(4)
> > +#define MAX9271_GPIO3OUT		BIT(3)
> > +#define MAX9271_GPIO2OUT		BIT(2)
> > +#define MAX9271_GPIO1OUT		BIT(1)
> > +#define MAX9271_GPO			BIT(0)
> > +/* Register 0x15 */
> > +#define MAX9271_PCLKDET			BIT(0)
> > +
> > +struct max9271_device {
> > +	struct device			*dev;
> > +	struct i2c_client		*client;
> > +	struct v4l2_subdev		sd;
> > +#define MAX9271_SOURCE_PAD	0
> > +#define MAX9271_SINK_PAD	1
> > +	struct media_pad		pads[2];
> > +	struct v4l2_async_notifier	notifier;
> > +	struct v4l2_async_subdev	*asd;
> > +	struct v4l2_ctrl_handler	ctrls;
> > +	struct v4l2_subdev		*sensor;
> > +};
> > +
> > +static inline struct max9271_device *sd_to_max9271(struct v4l2_subdev *sd)
> > +{
> > +	return container_of(sd, struct max9271_device, sd);
> > +}
> > +
> > +static inline struct max9271_device *i2c_to_max9271(struct i2c_client *client)
> > +{
> > +	return sd_to_max9271(i2c_get_clientdata(client));
> > +}
> > +
> > +static inline struct max9271_device *notifier_to_max9271(
> > +						struct v4l2_async_notifier *nf)
> > +{
> > +	return container_of(nf, struct max9271_device, notifier);
> > +}
> > +
> > +/* --- MAX9271 hardware operations --- */
> > +
> > +static int max9271_read(struct max9271_device *dev, u8 reg)
> > +{
> > +	int ret;
> > +
> > +	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
> > +
> > +	ret = i2c_smbus_read_byte_data(dev->client, reg);
> > +	if (ret < 0)
> > +		dev_dbg(&dev->client->dev,
> > +			"%s: register 0x%02x read failed (%d)\n",
> > +			__func__, reg, ret);
> > +
> > +	return ret;
> > +}
> > +
> > +static int max9271_write(struct max9271_device *dev, u8 reg, u8 val)
> > +{
> > +	int ret;
> > +
> > +	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
> > +
> > +	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
> > +	if (ret < 0)
> > +		dev_err(&dev->client->dev,
> > +			"%s: register 0x%02x write failed (%d)\n",
> > +			__func__, reg, ret);
> > +
> > +	return ret;
> > +}
> > +
> > +/*
> > + * max9271_pclk_detect() - Detect valid pixel clock from image sensor
> > + *
> > + * Wait up to 10ms for a valid pixel clock.
> > + *
> > + * Returns 0 for success, < 0 for pixel clock not properly detected
> > + */
> > +static int max9271_pclk_detect(struct max9271_device *dev)
> > +{
> > +	unsigned int i;
> > +	int ret;
> > +
> > +	for (i = 0; i < 100; i++) {
> > +		ret = max9271_read(dev, 0x15);
> > +		if (ret < 0)
> > +			return ret;
> > +
> > +		if (ret & MAX9271_PCLKDET)
> > +			return 0;
> > +
> > +		usleep_range(50, 100);
> > +	}
> > +
> > +	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
> > +
> > +	return -EIO;
> > +}
> > +
> > +static void max9271_wake_up(struct max9271_device *dev)
> > +{
> > +	/*
> > +	 * Use the chip default address as this function has to be called
> > +	 * before any other one.
> > +	 */
> > +	dev->client->addr = MAX9271_DEFAULT_ADDR;
> > +	i2c_smbus_read_byte(dev->client);
> > +	usleep_range(5000, 8000);
> > +}
> > +
> > +static int max9271_set_serial_link(struct max9271_device *dev, bool enable)
> > +{
> > +	int ret;
> > +	u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN;
> > +
> > +	if (enable) {
> > +		ret = max9271_pclk_detect(dev);
> > +		if (ret)
> > +			return ret;
> > +
> > +		val |= MAX9271_SEREN;
> > +	} else {
> > +		val |= MAX9271_CLINKEN;
> > +	}
> > +
> > +	/*
> > +	 * 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.
> > +	 */
> > +	ret = max9271_write(dev, 0x04, val);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	usleep_range(5000, 8000);
> > +
> > +	return 0;
> > +}
> > +
> > +static int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config)
> > +{
> > +	int ret;
> > +
> > +	ret = max9271_write(dev, 0x0d, i2c_config);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	/* The delay required after an I2C bus configuration change is not
>
> /*
>  * The delay ... ?
>

Ah, ups

>
>
> > +	 * characterized in the serializer manual. Sleep up to 5msec to
> > +	 * stay safe.
> > +	 */
> > +	usleep_range(3500, 5000);
> > +
> > +	return 0;
> > +}
> > +
> > +static int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
> > +{
> > +	int ret;
> > +
> > +	ret = max9271_read(dev, 0x08);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	/*
> > +	 * Enable or disable reverse channel high threshold to increase
> > +	 * immunity to power supply noise.
> > +	 */
> > +	ret = max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	usleep_range(2000, 2500);
> > +
> > +	return 0;
> > +}
> > +
> > +static int max9271_configure_gmsl_link(struct max9271_device *dev)
> > +{
> > +	int ret;
> > +
> > +	/*
> > +	 * 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
> > +	 *
> > +	 * TODO: Make the GMSL link configuration parametric.
> > +	 */
> > +	ret = max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
> > +			    MAX9271_EDC_1BIT_PARITY);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	usleep_range(5000, 8000);
> > +
> > +	/*
> > +	 * Adjust spread spectrum to +4% and auto-detect pixel clock
> > +	 * and serial link rate.
> > +	 */
> > +	ret = max9271_write(dev, 0x02,
> > +			    MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
> > +			    MAX9271_PCLK_AUTODETECT |
> > +			    MAX9271_SERIAL_AUTODETECT);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	usleep_range(5000, 8000);
> > +
> > +	return 0;
> > +}
> > +
> > +static int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask)
> > +{
> > +	int ret;
> > +
> > +	ret = max9271_read(dev, 0x0f);
>
> What is 0x0f?
>
> Ah - the registers are unnamed...
>
> Seems a shame..

It is a bit. If the overall approach is validated, I'll go and
beautify the driver.

>
>
> > +	if (ret < 0)
> > +		return 0;
> > +
> > +	ret |= gpio_mask;
> > +	ret = max9271_write(dev, 0x0f, ret);
> > +	if (ret < 0) {
> > +		dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	usleep_range(3500, 5000);
> > +
> > +	return 0;
> > +}
> > +
> > +static int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask)
> > +{
> > +	int ret;
> > +
> > +	ret = max9271_read(dev, 0x0f);
> > +	if (ret < 0)
> > +		return 0;
> > +
> > +	ret &= ~gpio_mask;
> > +	ret = max9271_write(dev, 0x0f, ret);
> > +	if (ret < 0) {
> > +		dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	usleep_range(3500, 5000);
> > +
> > +	return 0;
> > +}
> > +
> > +static int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask)
> > +{
> > +	int ret;
> > +
> > +	ret = max9271_read(dev, 0x0e);
> > +	if (ret < 0)
> > +		return 0;
> > +
> > +	/* BIT(0) reserved: GPO is always enabled. */
> > +	ret |= (gpio_mask & ~BIT(0));
> > +	ret = max9271_write(dev, 0x0e, ret);
> > +	if (ret < 0) {
> > +		dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	usleep_range(3500, 5000);
> > +
> > +	return 0;
> > +}
> > +
> > +static int max9271_verify_id(struct max9271_device *dev)
> > +{
> > +	int ret;
> > +
> > +	ret = max9271_read(dev, 0x1e);
> > +	if (ret < 0) {
> > +		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
> > +			ret);
> > +		return ret;
> > +	}
> > +
> > +	if (ret != MAX9271_ID) {
> > +		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
> > +			ret);
> > +		return -ENXIO;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int max9271_set_address(struct max9271_device *dev, u8 addr)
> > +{
> > +	int ret;
> > +
> > +	ret = max9271_write(dev, 0x00, addr << 1);
> > +	if (ret < 0) {
> > +		dev_err(&dev->client->dev,
> > +			"MAX9271 I2C address change failed (%d)\n", ret);
> > +		return ret;
> > +	}
> > +	usleep_range(3500, 5000);
> > +
> > +	return 0;
> > +}
> > +
> > +/* --- V4L2 Subdev Ops --- */
> > +
> > +static int max9271_s_stream(struct v4l2_subdev *sd, int enable)
> > +{
> > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > +
> > +	return max9271_set_serial_link(max9271, enable);
> > +}
> > +
> > +static int max9271_enum_mbus_code(struct v4l2_subdev *sd,
> > +				  struct v4l2_subdev_state *sd_state,
> > +				  struct v4l2_subdev_mbus_code_enum *code)
> > +{
> > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > +
> > +	return v4l2_subdev_call(max9271->sensor, pad, enum_mbus_code, NULL,
> > +				code);
> > +}
> > +
> > +static int max9271_get_fmt(struct v4l2_subdev *sd,
> > +			   struct v4l2_subdev_state *sd_state,
> > +			   struct v4l2_subdev_format *format)
> > +{
> > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > +
> > +	return v4l2_subdev_call(max9271->sensor, pad, get_fmt, NULL,
> > +				format);
> > +}
> > +
> > +static int max9271_set_fmt(struct v4l2_subdev *sd,
> > +			   struct v4l2_subdev_state *sd_state,
> > +			   struct v4l2_subdev_format *format)
> > +{
> > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > +
> > +	return v4l2_subdev_call(max9271->sensor, pad, set_fmt, NULL,
> > +				format);
> > +}
> > +
> > +static int max9271_post_register(struct v4l2_subdev *sd)
> > +{
> > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > +	int ret;
> > +
> > +	ret = max9271_verify_id(max9271);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ret = max9271_enable_gpios(max9271, MAX9271_GPIO1OUT);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = max9271_configure_gmsl_link(max9271);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct v4l2_subdev_video_ops max9271_video_ops = {
> > +	.s_stream	= max9271_s_stream,
> > +};
> > +
> > +static const struct v4l2_subdev_pad_ops max9271_subdev_pad_ops = {
> > +	.enum_mbus_code = max9271_enum_mbus_code,
> > +	.get_fmt	= max9271_get_fmt,
> > +	.set_fmt	= max9271_set_fmt,
> > +};
> > +
> > +static const struct v4l2_subdev_core_ops max9271_core_ops = {
> > +	.post_register	= max9271_post_register,
> > +};
> > +
> > +static const struct v4l2_subdev_ops max9271_subdev_ops = {
> > +	.core		= &max9271_core_ops,
> > +	.video		= &max9271_video_ops,
> > +	.pad		= &max9271_subdev_pad_ops,
> > +};
> > +
> > +/* --- V4L2 Async Notifier --- */
> > +
> > +static int max9271_notify_bound(struct v4l2_async_notifier *notifier,
> > +				struct v4l2_subdev *subdev,
> > +				struct v4l2_async_subdev *asd)
> > +{
> > +	struct max9271_device *max9271 = notifier_to_max9271(notifier);
> > +	int ret, pad;
> > +
> > +	/*
> > +	 * Reserve more space than necessary for controls inherited by the
> > +	 * remote subdev.
> > +	 */
> > +	ret = v4l2_ctrl_handler_init(&max9271->ctrls, 16);
> > +	if (ret < 0) {
> > +		dev_err(max9271->dev,
> > +			"Unable to initialize control handler: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = v4l2_ctrl_add_handler(&max9271->ctrls, subdev->ctrl_handler,
> > +				    NULL, true);
> > +	if (ret < 0) {
> > +		dev_err(max9271->dev,
> > +			"Unable to add subdev control handler: %d\n", ret);
> > +		goto error_free_handler;
> > +	}
> > +	max9271->sd.ctrl_handler = &max9271->ctrls;
> > +
> > +	/* Create media link with the remote sensor source pad. */
> > +	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
> > +					  MEDIA_PAD_FL_SOURCE);
> > +	if (pad < 0) {
> > +		dev_err(max9271->dev,
> > +			"Failed to find source pad for %s\n", subdev->name);
> > +		ret = pad;
> > +		goto error_free_handler;
> > +	}
> > +
> > +	ret = media_create_pad_link(&subdev->entity, pad,
> > +				    &max9271->sd.entity, MAX9271_SINK_PAD,
> > +				    MEDIA_LNK_FL_ENABLED |
> > +				    MEDIA_LNK_FL_IMMUTABLE);
> > +	if (ret)
> > +		goto error_free_handler;
> > +
> > +	max9271->sensor = subdev;
> > +
> > +	/*
> > +	 * Hold OV10635 in reset during max9271 configuration. The reset signal
>
> Hold the sensor in reset ...
>
> But are all sensors guaranteed to be connected to the GPIO in the same way?
>
> Will this cause us issues? - does the GPIO need to be modelled somehow
> to determine what's connected to them?
>
>
>
> > +	 * has to be asserted for at least 200 microseconds.
> > +	 */
> > +	ret = max9271_clear_gpios(max9271, MAX9271_GPIO1OUT);
> > +	if (ret)
> > +		return ret;
> > +	usleep_range(200, 500);
> > +
> > +	/*
> > +	 * Release ov10635 from reset and initialize it. The image sensor
> > +	 * requires at least 2048 XVCLK cycles (85 micro-seconds at 24MHz)
> > +	 * before being available. Stay safe and wait up to 500 micro-seconds.
>
> More sensor specific ... what needs to be abstracted here?

Yes indeed. As pointed out in the cover letter the handling of the
sensor's reset line should go through the gpiod API, and will probably
require installing a gpiochip in the max9271 driver.

>
>
> > +	 */
> > +	ret = max9271_set_gpios(max9271, MAX9271_GPIO1OUT);
> > +	if (ret)
> > +		return ret;
> > +	usleep_range(100, 500);
> > +
> > +	/*
> > +	 * Call the sensor post_register operation to complete its
> > +	 * initialization.
> > +	 */
> > +	ret = v4l2_subdev_call(max9271->sensor, core, post_register);
> > +	if (ret) {
> > +		dev_err(max9271->dev, "Failed to initialize sensor %u\n", ret);
> > +		goto error_remove_link;
> > +	}
> > +
> > +	return 0;
> > +
> > +error_remove_link:
> > +	media_entity_remove_links(&max9271->sd.entity);
> > +	max9271->sensor = NULL;
> > +
> > +error_free_handler:
> > +	v4l2_ctrl_handler_free(&max9271->ctrls);
> > +	max9271->sd.ctrl_handler = NULL;
> > +
> > +	return ret;
> > +}
> > +
> > +static void max9271_notify_unbind(struct v4l2_async_notifier *notifier,
> > +				  struct v4l2_subdev *subdev,
> > +				  struct v4l2_async_subdev *asd)
> > +{
> > +	struct max9271_device *max9271 = notifier_to_max9271(notifier);
> > +
> > +	media_entity_remove_links(&max9271->sd.entity);
> > +	max9271->sensor = NULL;
> > +}
> > +
> > +static const struct v4l2_async_notifier_operations max9271_notifier_ops = {
> > +	.bound = max9271_notify_bound,
> > +	.unbind = max9271_notify_unbind,
> > +};
> > +
> > +static int max9271_parse_dt(struct max9271_device *max9271)
> > +{
> > +	struct fwnode_handle *ep, *remote;
> > +	struct v4l2_fwnode_endpoint vep = {
> > +		.bus_type = V4L2_MBUS_PARALLEL,
> > +	};
> > +	int ret;
> > +
> > +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 1, 0, 0);
> > +	if (!ep) {
> > +		dev_err(max9271->dev, "Unable to get sensor endpoint: %pOF\n",
> > +			max9271->dev->of_node);
> > +		return -ENOENT;
> > +	}
> > +
> > +	remote = fwnode_graph_get_remote_endpoint(ep);
> > +	if (!remote) {
> > +		dev_err(max9271->dev, "Unable to get remote endpoint: %pOF\n",
> > +			max9271->dev->of_node);
> > +		return -ENOENT;
> > +	}
> > +
> > +	ret = v4l2_fwnode_endpoint_parse(ep, &vep);
> > +	fwnode_handle_put(ep);
> > +	if (ret) {
> > +		fwnode_handle_put(remote);
> > +		dev_err(max9271->dev, "Unable to parse endpoint: %pOF\n",
> > +			to_of_node(ep));
> > +		return ret;
> > +	}
> > +
> > +	v4l2_async_notifier_init(&max9271->notifier);
> > +	max9271->asd = v4l2_async_notifier_add_fwnode_subdev(&max9271->notifier,
> > +					      remote, struct v4l2_async_subdev);
> > +	fwnode_handle_put(remote);
> > +	if (IS_ERR(max9271->asd))
> > +		return PTR_ERR(max9271->asd);
> > +
> > +	max9271->notifier.ops = &max9271_notifier_ops;
> > +	max9271->notifier.flags = V4L2_ASYNC_NOTIFIER_DEFER_POST_REGISTER;
>
> This looks new, I'll have to read up on it ...

It is. I am pushing to get feedback on this since a few months now.
If you wish to help:
https://patchwork.linuxtv.org/project/linux-media/list/?series=5847

>
>
> > +	ret = v4l2_async_subdev_notifier_register(&max9271->sd,
> > +						  &max9271->notifier);
> > +	if (ret < 0) {
> > +		v4l2_async_notifier_cleanup(&max9271->notifier);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int max9271_init(struct max9271_device *max9271)
> > +{
> > +	int ret;
> > +	u8 addr;
> > +
> > +	max9271_wake_up(max9271);
>
> This call has just set max9271->client->addr = MAX9271_DEFAULT_ADDR, so
> the original has now been lost.

Doh! I overlooked the fact max9271_wake_up() silently overwrites the
i2c client address. For the reasons explained in the cover letter I've
been able to test this patch with a single camera only, so the issue
went unnoticed here. Thanks for spotting!

>
>
> > +
> > +	/* Re-program the chip address. */
> > +	addr = max9271->client->addr;
> > +	max9271->client->addr = MAX9271_DEFAULT_ADDR;
> > +	ret = max9271_set_address(max9271, addr);
>
> So this is currently broken :S
>
> > +	if (ret < 0)
> > +		return ret;
> > +	max9271->client->addr = addr;
> > +
> > +	/* Serial link disabled during conf as it needs a valid pixel clock. */
> > +	ret = max9271_set_serial_link(max9271, false);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/*
> > +	 *  Ensure that we have a good link configuration before attempting to
> > +	 *  identify the device.
> > +	 */
> > +	ret = max9271_configure_i2c(max9271, MAX9271_I2CSLVSH_469NS_234NS |
> > +					     MAX9271_I2CSLVTO_1024US |
> > +					     MAX9271_I2CMSTBT_105KBPS);
>
> Are these parameters tied to the max9286 ?
>

More to the I2c bus configuration and should probably be made
configurable through the canonical i2c DTS properties.

>
> > +	if (ret)
> > +		return ret;
> > +
> > +	/*
> > +	 * Set reverse channel high threshold to increase noise immunity.
> > +	 *
> > +	 * This should be compensated by increasing the reverse channel
> > +	 * amplitude on the remote deserializer side.
> > +	 */
> > +	return max9271_set_high_threshold(max9271, true);
> > +}
> > +
> > +static int max9271_probe(struct i2c_client *client)
> > +{
> > +	struct max9271_device *max9271;
> > +	struct fwnode_handle *ep;
> > +	int ret;
> > +
> > +	max9271 = devm_kzalloc(&client->dev, sizeof(*max9271), GFP_KERNEL);
> > +	if (!max9271)
> > +		return -ENOMEM;
> > +	max9271->dev = &client->dev;
> > +	max9271->client = client;
> > +
> > +	/* Initialize and register the subdevice. */
> > +	v4l2_i2c_subdev_init(&max9271->sd, client, &max9271_subdev_ops);
> > +	max9271->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +	max9271->pads[MAX9271_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
> > +	max9271->pads[MAX9271_SINK_PAD].flags = MEDIA_PAD_FL_SINK;
> > +	max9271->sd.entity.flags |= MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
>
> Is it a formatter - do we need a new/different entity type?

I admit I had an hard time choosing the correct entity type :)

>
>
> > +	ret = media_entity_pads_init(&max9271->sd.entity, 2, max9271->pads);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 0, 0, 0);
> > +	if (!ep) {
> > +		dev_err(max9271->dev, "Unable to get endpoint 0: %pOF\n",
> > +			max9271->dev->of_node);
> > +		ret = -ENODEV;
> > +		goto error_media_entity;
> > +	}
> > +
> > +	max9271->sd.fwnode = ep;
> > +	ret = v4l2_async_register_subdev(&max9271->sd);
> > +	if (ret)
> > +		goto error_put_node;
> > +
> > +	ret = max9271_parse_dt(max9271);
> > +	if (ret)
> > +		goto error_unregister_subdev;
> > +
> > +	ret = max9271_init(max9271);
> > +	if (ret)
> > +		goto error_unregister_subdev;
> > +
> > +	return 0;
> > +
> > +error_unregister_subdev:
> > +	v4l2_async_unregister_subdev(&max9271->sd);
> > +error_put_node:
> > +	fwnode_handle_put(max9271->sd.fwnode);
> > +error_media_entity:
> > +	media_entity_cleanup(&max9271->sd.entity);
> > +
> > +	return ret;
> > +}
> > +
> > +static int max9271_remove(struct i2c_client *client)
> > +{
> > +	struct max9271_device *max9271 = i2c_to_max9271(client);
> > +
> > +	v4l2_ctrl_handler_free(&max9271->ctrls);
> > +	v4l2_async_notifier_cleanup(&max9271->notifier);
> > +	v4l2_async_unregister_subdev(&max9271->sd);
> > +	fwnode_handle_put(max9271->sd.fwnode);
> > +	media_entity_cleanup(&max9271->sd.entity);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id max9271_of_ids[] = {
> > +	{ .compatible = "imi,max9271", },
>
> This should be a 'maxim' prefix now, not IMI.
>

Indeed!

Thanks
  j

>
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, max9271_of_ids);
> > +
> > +static struct i2c_driver max9271_i2c_driver = {
> > +	.driver	= {
> > +		.name	= "max9271",
> > +		.of_match_table = max9271_of_ids,
> > +	},
> > +	.probe_new	= max9271_probe,
> > +	.remove		= max9271_remove,
> > +};
> > +
> > +module_i2c_driver(max9271_i2c_driver);
> > +
> > +MODULE_DESCRIPTION("MAX9271 GMSL serializer subdevice driver");
> > +MODULE_AUTHOR("Jacopo Mondi");
> > +MODULE_LICENSE("GPL");
> >

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

* Re: [RFC 2/5] media: i2c: Add MAX9271 I2C driver
  2021-08-18  8:27     ` Jacopo Mondi
@ 2021-08-18 12:38       ` Kieran Bingham
  2021-08-23  2:22       ` Laurent Pinchart
  2021-08-23 12:05       ` Geert Uytterhoeven
  2 siblings, 0 replies; 21+ messages in thread
From: Kieran Bingham @ 2021-08-18 12:38 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Jacopo Mondi, Mauro Carvalho Chehab, Kieran Bingham,
	Laurent Pinchart, Niklas Söderlund, Hans Verkuil,
	Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	linux-renesas-soc, linux-media

On 18/08/2021 09:27, Jacopo Mondi wrote:
> Hi Kieran, thanks for review!
> 
> On Tue, Aug 17, 2021 at 04:49:17PM +0100, Kieran Bingham wrote:
>> Hi Jacopo,
>>
>> On 17/08/2021 08:27, Jacopo Mondi wrote:
>>> The MAX9271 is a GMSL serializer that serializes a video stream
>>> received from an image sensor through the parallel video bus.
>>
>>
>> https://datasheets.maximintegrated.com/en/ds/MAX9271.pdf calls it a
>> "16-Bit GMSL Serializer with Coas or STP Cable Drive"
>>
> 
> Nice we have a public datasheet now!
> Nice, well, it might mean GMSL is just old ?

Hahah maybe.

>>> The serializer it's usually found embedded with an image sensor and
>>
>> s/it's/is/
>>
>>> other ancillary chips in camera modules like RDACM20 and RDACM21.
>>>
>>> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
>>> ---
>>>  MAINTAINERS                 |   9 +
>>>  drivers/media/i2c/Kconfig   |  12 +
>>>  drivers/media/i2c/Makefile  |   1 +
>>>  drivers/media/i2c/max9271.c | 756 ++++++++++++++++++++++++++++++++++++
>>>  4 files changed, 778 insertions(+)
>>>  create mode 100644 drivers/media/i2c/max9271.c
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index 7ad89cac19b7..2dab25a08c9c 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -11244,6 +11244,15 @@ F:	Documentation/hwmon/max6697.rst
>>>  F:	drivers/hwmon/max6697.c
>>>  F:	include/linux/platform_data/max6697.h
>>>
>>> +MAX9271 GMSL SERIALIZER 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:	drivers/media/i2c/max9271.c
>>> +
>>>  MAX9286 QUAD GMSL DESERIALIZER DRIVER
>>>  M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
>>>  M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>>> index 08feb3e8c1bf..b793d1f322d9 100644
>>> --- a/drivers/media/i2c/Kconfig
>>> +++ b/drivers/media/i2c/Kconfig
>>> @@ -1300,6 +1300,18 @@ source "drivers/media/i2c/m5mols/Kconfig"
>>>  config VIDEO_MAX9271_LIB
>>>  	tristate
>>>
>>> +config VIDEO_MAX9271
>>> +	tristate "MAX9271 GMSL serializer support"
>>> +	depends on I2C
>>> +	select V4L2_FWNODE
>>> +	select VIDEO_V4L2_SUBDEV_API
>>> +	select MEDIA_CONTROLLER
>>> +	help
>>> +	  This driver supports the Maxim MAX9271 GMSL serializer.
>>> +
>>> +	  To compile this driver as a module, choose M here: the
>>> +	  module will be called max9271.
>>> +
>>>  config VIDEO_RDACM20
>>>  	tristate "IMI RDACM20 camera support"
>>>  	depends on I2C
>>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>>> index 4d879373bd48..37bb51065574 100644
>>> --- a/drivers/media/i2c/Makefile
>>> +++ b/drivers/media/i2c/Makefile
>>> @@ -130,6 +130,7 @@ obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
>>>  obj-$(CONFIG_VIDEO_IMX412)	+= imx412.o
>>>  obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
>>>  obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271-lib.o
>>> +obj-$(CONFIG_VIDEO_MAX9271)	+= max9271.o
>>>  obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
>>>  obj-$(CONFIG_VIDEO_RDACM21)	+= rdacm21.o
>>>  obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
>>> diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c
>>> new file mode 100644
>>> index 000000000000..64987cba3d3e
>>> --- /dev/null
>>> +++ b/drivers/media/i2c/max9271.c
>>> @@ -0,0 +1,756 @@
>>> +// SPDX-License-Identifier: GPL-2.0+
>>> +/*
>>> + * Copyright (C) 2017-2021 Jacopo Mondi
>>> + * Copyright (C) 2017-2020 Kieran Bingham
>>> + * Copyright (C) 2017-2019 Laurent Pinchart
>>> + * Copyright (C) 2017-2019 Niklas Söderlund
>>> + * Copyright (C) 2016 Renesas Electronics Corporation
>>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>>> + */
>>> +
>>> +#include <linux/delay.h>
>>> +#include <linux/fwnode.h>
>>> +#include <linux/init.h>
>>> +#include <linux/i2c.h>
>>> +#include <linux/module.h>
>>> +#include <linux/property.h>
>>> +
>>> +#include <media/v4l2-async.h>
>>> +#include <media/v4l2-ctrls.h>
>>> +#include <media/v4l2-fwnode.h>
>>> +#include <media/v4l2-subdev.h>
>>> +
>>> +#define MAX9271_DEFAULT_ADDR	0x40
>>> +
>>> +/* Register 0x02 */
>>> +#define MAX9271_SPREAD_SPECT_0		(0 << 5)
>>> +#define MAX9271_SPREAD_SPECT_05		(1 << 5)
>>> +#define MAX9271_SPREAD_SPECT_15		(2 << 5)
>>> +#define MAX9271_SPREAD_SPECT_1		(5 << 5)
>>> +#define MAX9271_SPREAD_SPECT_2		(3 << 5)
>>> +#define MAX9271_SPREAD_SPECT_3		(6 << 5)
>>> +#define MAX9271_SPREAD_SPECT_4		(7 << 5)
>>> +#define MAX9271_R02_RES			BIT(4)
>>> +#define MAX9271_PCLK_AUTODETECT		(3 << 2)
>>> +#define MAX9271_SERIAL_AUTODETECT	(0x03)
>>> +/* Register 0x04 */
>>> +#define MAX9271_SEREN			BIT(7)
>>> +#define MAX9271_CLINKEN			BIT(6)
>>> +#define MAX9271_PRBSEN			BIT(5)
>>> +#define MAX9271_SLEEP			BIT(4)
>>> +#define MAX9271_INTTYPE_I2C		(0 << 2)
>>> +#define MAX9271_INTTYPE_UART		(1 << 2)
>>> +#define MAX9271_INTTYPE_NONE		(2 << 2)
>>> +#define MAX9271_REVCCEN			BIT(1)
>>> +#define MAX9271_FWDCCEN			BIT(0)
>>> +/* Register 0x07 */
>>> +#define MAX9271_DBL			BIT(7)
>>> +#define MAX9271_DRS			BIT(6)
>>> +#define MAX9271_BWS			BIT(5)
>>> +#define MAX9271_ES			BIT(4)
>>> +#define MAX9271_HVEN			BIT(2)
>>> +#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
>>> +#define MAX9271_EDC_6BIT_CRC		(1 << 0)
>>> +#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
>>> +/* Register 0x08 */
>>> +#define MAX9271_INVVS			BIT(7)
>>> +#define MAX9271_INVHS			BIT(6)
>>> +#define MAX9271_REV_LOGAIN		BIT(3)
>>> +#define MAX9271_REV_HIVTH		BIT(0)
>>> +/* Register 0x09 */
>>> +#define MAX9271_ID			0x09
>>> +/* Register 0x0d */
>>> +#define MAX9271_I2CLOCACK		BIT(7)
>>> +#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
>>> +#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
>>> +#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
>>> +#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
>>> +#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
>>> +#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
>>> +#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
>>> +#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
>>> +#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
>>> +#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
>>> +#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
>>> +#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
>>> +#define MAX9271_I2CSLVTO_NONE		(3 << 0)
>>> +#define MAX9271_I2CSLVTO_1024US		(2 << 0)
>>> +#define MAX9271_I2CSLVTO_256US		(1 << 0)
>>> +#define MAX9271_I2CSLVTO_64US		(0 << 0)
>>> +/* Register 0x0f */
>>> +#define MAX9271_GPIO5OUT		BIT(5)
>>> +#define MAX9271_GPIO4OUT		BIT(4)
>>> +#define MAX9271_GPIO3OUT		BIT(3)
>>> +#define MAX9271_GPIO2OUT		BIT(2)
>>> +#define MAX9271_GPIO1OUT		BIT(1)
>>> +#define MAX9271_GPO			BIT(0)
>>> +/* Register 0x15 */
>>> +#define MAX9271_PCLKDET			BIT(0)
>>> +
>>> +struct max9271_device {
>>> +	struct device			*dev;
>>> +	struct i2c_client		*client;
>>> +	struct v4l2_subdev		sd;
>>> +#define MAX9271_SOURCE_PAD	0
>>> +#define MAX9271_SINK_PAD	1
>>> +	struct media_pad		pads[2];
>>> +	struct v4l2_async_notifier	notifier;
>>> +	struct v4l2_async_subdev	*asd;
>>> +	struct v4l2_ctrl_handler	ctrls;
>>> +	struct v4l2_subdev		*sensor;
>>> +};
>>> +
>>> +static inline struct max9271_device *sd_to_max9271(struct v4l2_subdev *sd)
>>> +{
>>> +	return container_of(sd, struct max9271_device, sd);
>>> +}
>>> +
>>> +static inline struct max9271_device *i2c_to_max9271(struct i2c_client *client)
>>> +{
>>> +	return sd_to_max9271(i2c_get_clientdata(client));
>>> +}
>>> +
>>> +static inline struct max9271_device *notifier_to_max9271(
>>> +						struct v4l2_async_notifier *nf)
>>> +{
>>> +	return container_of(nf, struct max9271_device, notifier);
>>> +}
>>> +
>>> +/* --- MAX9271 hardware operations --- */
>>> +
>>> +static int max9271_read(struct max9271_device *dev, u8 reg)
>>> +{
>>> +	int ret;
>>> +
>>> +	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
>>> +
>>> +	ret = i2c_smbus_read_byte_data(dev->client, reg);
>>> +	if (ret < 0)
>>> +		dev_dbg(&dev->client->dev,
>>> +			"%s: register 0x%02x read failed (%d)\n",
>>> +			__func__, reg, ret);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int max9271_write(struct max9271_device *dev, u8 reg, u8 val)
>>> +{
>>> +	int ret;
>>> +
>>> +	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
>>> +
>>> +	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
>>> +	if (ret < 0)
>>> +		dev_err(&dev->client->dev,
>>> +			"%s: register 0x%02x write failed (%d)\n",
>>> +			__func__, reg, ret);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/*
>>> + * max9271_pclk_detect() - Detect valid pixel clock from image sensor
>>> + *
>>> + * Wait up to 10ms for a valid pixel clock.
>>> + *
>>> + * Returns 0 for success, < 0 for pixel clock not properly detected
>>> + */
>>> +static int max9271_pclk_detect(struct max9271_device *dev)
>>> +{
>>> +	unsigned int i;
>>> +	int ret;
>>> +
>>> +	for (i = 0; i < 100; i++) {
>>> +		ret = max9271_read(dev, 0x15);
>>> +		if (ret < 0)
>>> +			return ret;
>>> +
>>> +		if (ret & MAX9271_PCLKDET)
>>> +			return 0;
>>> +
>>> +		usleep_range(50, 100);
>>> +	}
>>> +
>>> +	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
>>> +
>>> +	return -EIO;
>>> +}
>>> +
>>> +static void max9271_wake_up(struct max9271_device *dev)
>>> +{
>>> +	/*
>>> +	 * Use the chip default address as this function has to be called
>>> +	 * before any other one.
>>> +	 */
>>> +	dev->client->addr = MAX9271_DEFAULT_ADDR;
>>> +	i2c_smbus_read_byte(dev->client);
>>> +	usleep_range(5000, 8000);
>>> +}
>>> +
>>> +static int max9271_set_serial_link(struct max9271_device *dev, bool enable)
>>> +{
>>> +	int ret;
>>> +	u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN;
>>> +
>>> +	if (enable) {
>>> +		ret = max9271_pclk_detect(dev);
>>> +		if (ret)
>>> +			return ret;
>>> +
>>> +		val |= MAX9271_SEREN;
>>> +	} else {
>>> +		val |= MAX9271_CLINKEN;
>>> +	}
>>> +
>>> +	/*
>>> +	 * 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.
>>> +	 */
>>> +	ret = max9271_write(dev, 0x04, val);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	usleep_range(5000, 8000);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = max9271_write(dev, 0x0d, i2c_config);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	/* The delay required after an I2C bus configuration change is not
>>
>> /*
>>  * The delay ... ?
>>
> 
> Ah, ups
> 
>>
>>
>>> +	 * characterized in the serializer manual. Sleep up to 5msec to
>>> +	 * stay safe.
>>> +	 */
>>> +	usleep_range(3500, 5000);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = max9271_read(dev, 0x08);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	/*
>>> +	 * Enable or disable reverse channel high threshold to increase
>>> +	 * immunity to power supply noise.
>>> +	 */
>>> +	ret = max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	usleep_range(2000, 2500);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int max9271_configure_gmsl_link(struct max9271_device *dev)
>>> +{
>>> +	int ret;
>>> +
>>> +	/*
>>> +	 * 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
>>> +	 *
>>> +	 * TODO: Make the GMSL link configuration parametric.
>>> +	 */
>>> +	ret = max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
>>> +			    MAX9271_EDC_1BIT_PARITY);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	usleep_range(5000, 8000);
>>> +
>>> +	/*
>>> +	 * Adjust spread spectrum to +4% and auto-detect pixel clock
>>> +	 * and serial link rate.
>>> +	 */
>>> +	ret = max9271_write(dev, 0x02,
>>> +			    MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
>>> +			    MAX9271_PCLK_AUTODETECT |
>>> +			    MAX9271_SERIAL_AUTODETECT);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	usleep_range(5000, 8000);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = max9271_read(dev, 0x0f);
>>
>> What is 0x0f?
>>
>> Ah - the registers are unnamed...
>>
>> Seems a shame..
> 
> It is a bit. If the overall approach is validated, I'll go and
> beautify the driver.

Indeed, well they don't have names in the datasheet was my point, but I
guess we could give them names of their expected usage ...




>>> +	if (ret < 0)
>>> +		return 0;
>>> +
>>> +	ret |= gpio_mask;
>>> +	ret = max9271_write(dev, 0x0f, ret);
>>> +	if (ret < 0) {
>>> +		dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	usleep_range(3500, 5000);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = max9271_read(dev, 0x0f);
>>> +	if (ret < 0)
>>> +		return 0;
>>> +
>>> +	ret &= ~gpio_mask;
>>> +	ret = max9271_write(dev, 0x0f, ret);
>>> +	if (ret < 0) {
>>> +		dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	usleep_range(3500, 5000);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = max9271_read(dev, 0x0e);
>>> +	if (ret < 0)
>>> +		return 0;
>>> +
>>> +	/* BIT(0) reserved: GPO is always enabled. */
>>> +	ret |= (gpio_mask & ~BIT(0));
>>> +	ret = max9271_write(dev, 0x0e, ret);
>>> +	if (ret < 0) {
>>> +		dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	usleep_range(3500, 5000);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int max9271_verify_id(struct max9271_device *dev)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = max9271_read(dev, 0x1e);
>>> +	if (ret < 0) {
>>> +		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
>>> +			ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	if (ret != MAX9271_ID) {
>>> +		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
>>> +			ret);
>>> +		return -ENXIO;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int max9271_set_address(struct max9271_device *dev, u8 addr)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = max9271_write(dev, 0x00, addr << 1);
>>> +	if (ret < 0) {
>>> +		dev_err(&dev->client->dev,
>>> +			"MAX9271 I2C address change failed (%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +	usleep_range(3500, 5000);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/* --- V4L2 Subdev Ops --- */
>>> +
>>> +static int max9271_s_stream(struct v4l2_subdev *sd, int enable)
>>> +{
>>> +	struct max9271_device *max9271 = sd_to_max9271(sd);
>>> +
>>> +	return max9271_set_serial_link(max9271, enable);
>>> +}
>>> +
>>> +static int max9271_enum_mbus_code(struct v4l2_subdev *sd,
>>> +				  struct v4l2_subdev_state *sd_state,
>>> +				  struct v4l2_subdev_mbus_code_enum *code)
>>> +{
>>> +	struct max9271_device *max9271 = sd_to_max9271(sd);
>>> +
>>> +	return v4l2_subdev_call(max9271->sensor, pad, enum_mbus_code, NULL,
>>> +				code);
>>> +}
>>> +
>>> +static int max9271_get_fmt(struct v4l2_subdev *sd,
>>> +			   struct v4l2_subdev_state *sd_state,
>>> +			   struct v4l2_subdev_format *format)
>>> +{
>>> +	struct max9271_device *max9271 = sd_to_max9271(sd);
>>> +
>>> +	return v4l2_subdev_call(max9271->sensor, pad, get_fmt, NULL,
>>> +				format);
>>> +}
>>> +
>>> +static int max9271_set_fmt(struct v4l2_subdev *sd,
>>> +			   struct v4l2_subdev_state *sd_state,
>>> +			   struct v4l2_subdev_format *format)
>>> +{
>>> +	struct max9271_device *max9271 = sd_to_max9271(sd);
>>> +
>>> +	return v4l2_subdev_call(max9271->sensor, pad, set_fmt, NULL,
>>> +				format);
>>> +}
>>> +
>>> +static int max9271_post_register(struct v4l2_subdev *sd)
>>> +{
>>> +	struct max9271_device *max9271 = sd_to_max9271(sd);
>>> +	int ret;
>>> +
>>> +	ret = max9271_verify_id(max9271);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = max9271_enable_gpios(max9271, MAX9271_GPIO1OUT);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = max9271_configure_gmsl_link(max9271);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct v4l2_subdev_video_ops max9271_video_ops = {
>>> +	.s_stream	= max9271_s_stream,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_pad_ops max9271_subdev_pad_ops = {
>>> +	.enum_mbus_code = max9271_enum_mbus_code,
>>> +	.get_fmt	= max9271_get_fmt,
>>> +	.set_fmt	= max9271_set_fmt,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_core_ops max9271_core_ops = {
>>> +	.post_register	= max9271_post_register,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_ops max9271_subdev_ops = {
>>> +	.core		= &max9271_core_ops,
>>> +	.video		= &max9271_video_ops,
>>> +	.pad		= &max9271_subdev_pad_ops,
>>> +};
>>> +
>>> +/* --- V4L2 Async Notifier --- */
>>> +
>>> +static int max9271_notify_bound(struct v4l2_async_notifier *notifier,
>>> +				struct v4l2_subdev *subdev,
>>> +				struct v4l2_async_subdev *asd)
>>> +{
>>> +	struct max9271_device *max9271 = notifier_to_max9271(notifier);
>>> +	int ret, pad;
>>> +
>>> +	/*
>>> +	 * Reserve more space than necessary for controls inherited by the
>>> +	 * remote subdev.
>>> +	 */
>>> +	ret = v4l2_ctrl_handler_init(&max9271->ctrls, 16);
>>> +	if (ret < 0) {
>>> +		dev_err(max9271->dev,
>>> +			"Unable to initialize control handler: %d\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = v4l2_ctrl_add_handler(&max9271->ctrls, subdev->ctrl_handler,
>>> +				    NULL, true);
>>> +	if (ret < 0) {
>>> +		dev_err(max9271->dev,
>>> +			"Unable to add subdev control handler: %d\n", ret);
>>> +		goto error_free_handler;
>>> +	}
>>> +	max9271->sd.ctrl_handler = &max9271->ctrls;
>>> +
>>> +	/* Create media link with the remote sensor source pad. */
>>> +	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
>>> +					  MEDIA_PAD_FL_SOURCE);
>>> +	if (pad < 0) {
>>> +		dev_err(max9271->dev,
>>> +			"Failed to find source pad for %s\n", subdev->name);
>>> +		ret = pad;
>>> +		goto error_free_handler;
>>> +	}
>>> +
>>> +	ret = media_create_pad_link(&subdev->entity, pad,
>>> +				    &max9271->sd.entity, MAX9271_SINK_PAD,
>>> +				    MEDIA_LNK_FL_ENABLED |
>>> +				    MEDIA_LNK_FL_IMMUTABLE);
>>> +	if (ret)
>>> +		goto error_free_handler;
>>> +
>>> +	max9271->sensor = subdev;
>>> +
>>> +	/*
>>> +	 * Hold OV10635 in reset during max9271 configuration. The reset signal
>>
>> Hold the sensor in reset ...
>>
>> But are all sensors guaranteed to be connected to the GPIO in the same way?
>>
>> Will this cause us issues? - does the GPIO need to be modelled somehow
>> to determine what's connected to them?
>>
>>
>>
>>> +	 * has to be asserted for at least 200 microseconds.
>>> +	 */
>>> +	ret = max9271_clear_gpios(max9271, MAX9271_GPIO1OUT);
>>> +	if (ret)
>>> +		return ret;
>>> +	usleep_range(200, 500);
>>> +
>>> +	/*
>>> +	 * Release ov10635 from reset and initialize it. The image sensor
>>> +	 * requires at least 2048 XVCLK cycles (85 micro-seconds at 24MHz)
>>> +	 * before being available. Stay safe and wait up to 500 micro-seconds.
>>
>> More sensor specific ... what needs to be abstracted here?
> 
> Yes indeed. As pointed out in the cover letter the handling of the
> sensor's reset line should go through the gpiod API, and will probably
> require installing a gpiochip in the max9271 driver.

Ok - I missed that, sorry.



>>> +	 */
>>> +	ret = max9271_set_gpios(max9271, MAX9271_GPIO1OUT);
>>> +	if (ret)
>>> +		return ret;
>>> +	usleep_range(100, 500);
>>> +
>>> +	/*
>>> +	 * Call the sensor post_register operation to complete its
>>> +	 * initialization.
>>> +	 */
>>> +	ret = v4l2_subdev_call(max9271->sensor, core, post_register);
>>> +	if (ret) {
>>> +		dev_err(max9271->dev, "Failed to initialize sensor %u\n", ret);
>>> +		goto error_remove_link;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +error_remove_link:
>>> +	media_entity_remove_links(&max9271->sd.entity);
>>> +	max9271->sensor = NULL;
>>> +
>>> +error_free_handler:
>>> +	v4l2_ctrl_handler_free(&max9271->ctrls);
>>> +	max9271->sd.ctrl_handler = NULL;
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static void max9271_notify_unbind(struct v4l2_async_notifier *notifier,
>>> +				  struct v4l2_subdev *subdev,
>>> +				  struct v4l2_async_subdev *asd)
>>> +{
>>> +	struct max9271_device *max9271 = notifier_to_max9271(notifier);
>>> +
>>> +	media_entity_remove_links(&max9271->sd.entity);
>>> +	max9271->sensor = NULL;
>>> +}
>>> +
>>> +static const struct v4l2_async_notifier_operations max9271_notifier_ops = {
>>> +	.bound = max9271_notify_bound,
>>> +	.unbind = max9271_notify_unbind,
>>> +};
>>> +
>>> +static int max9271_parse_dt(struct max9271_device *max9271)
>>> +{
>>> +	struct fwnode_handle *ep, *remote;
>>> +	struct v4l2_fwnode_endpoint vep = {
>>> +		.bus_type = V4L2_MBUS_PARALLEL,
>>> +	};
>>> +	int ret;
>>> +
>>> +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 1, 0, 0);
>>> +	if (!ep) {
>>> +		dev_err(max9271->dev, "Unable to get sensor endpoint: %pOF\n",
>>> +			max9271->dev->of_node);
>>> +		return -ENOENT;
>>> +	}
>>> +
>>> +	remote = fwnode_graph_get_remote_endpoint(ep);
>>> +	if (!remote) {
>>> +		dev_err(max9271->dev, "Unable to get remote endpoint: %pOF\n",
>>> +			max9271->dev->of_node);
>>> +		return -ENOENT;
>>> +	}
>>> +
>>> +	ret = v4l2_fwnode_endpoint_parse(ep, &vep);
>>> +	fwnode_handle_put(ep);
>>> +	if (ret) {
>>> +		fwnode_handle_put(remote);
>>> +		dev_err(max9271->dev, "Unable to parse endpoint: %pOF\n",
>>> +			to_of_node(ep));
>>> +		return ret;
>>> +	}
>>> +
>>> +	v4l2_async_notifier_init(&max9271->notifier);
>>> +	max9271->asd = v4l2_async_notifier_add_fwnode_subdev(&max9271->notifier,
>>> +					      remote, struct v4l2_async_subdev);
>>> +	fwnode_handle_put(remote);
>>> +	if (IS_ERR(max9271->asd))
>>> +		return PTR_ERR(max9271->asd);
>>> +
>>> +	max9271->notifier.ops = &max9271_notifier_ops;
>>> +	max9271->notifier.flags = V4L2_ASYNC_NOTIFIER_DEFER_POST_REGISTER;
>>
>> This looks new, I'll have to read up on it ...
> 
> It is. I am pushing to get feedback on this since a few months now.
> If you wish to help:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=5847
> 
>>
>>
>>> +	ret = v4l2_async_subdev_notifier_register(&max9271->sd,
>>> +						  &max9271->notifier);
>>> +	if (ret < 0) {
>>> +		v4l2_async_notifier_cleanup(&max9271->notifier);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int max9271_init(struct max9271_device *max9271)
>>> +{
>>> +	int ret;
>>> +	u8 addr;
>>> +
>>> +	max9271_wake_up(max9271);
>>
>> This call has just set max9271->client->addr = MAX9271_DEFAULT_ADDR, so
>> the original has now been lost.
> 
> Doh! I overlooked the fact max9271_wake_up() silently overwrites the
> i2c client address. For the reasons explained in the cover letter I've
> been able to test this patch with a single camera only, so the issue
> went unnoticed here. Thanks for spotting!

That's what extra eyes are for ;-)


>>> +
>>> +	/* Re-program the chip address. */
>>> +	addr = max9271->client->addr;
>>> +	max9271->client->addr = MAX9271_DEFAULT_ADDR;
>>> +	ret = max9271_set_address(max9271, addr);
>>
>> So this is currently broken :S
>>
>>> +	if (ret < 0)
>>> +		return ret;
>>> +	max9271->client->addr = addr;
>>> +
>>> +	/* Serial link disabled during conf as it needs a valid pixel clock. */
>>> +	ret = max9271_set_serial_link(max9271, false);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	/*
>>> +	 *  Ensure that we have a good link configuration before attempting to
>>> +	 *  identify the device.
>>> +	 */
>>> +	ret = max9271_configure_i2c(max9271, MAX9271_I2CSLVSH_469NS_234NS |
>>> +					     MAX9271_I2CSLVTO_1024US |
>>> +					     MAX9271_I2CMSTBT_105KBPS);
>>
>> Are these parameters tied to the max9286 ?
>>
> 
> More to the I2c bus configuration and should probably be made
> configurable through the canonical i2c DTS properties.

Ok


>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	/*
>>> +	 * Set reverse channel high threshold to increase noise immunity.
>>> +	 *
>>> +	 * This should be compensated by increasing the reverse channel
>>> +	 * amplitude on the remote deserializer side.
>>> +	 */
>>> +	return max9271_set_high_threshold(max9271, true);
>>> +}
>>> +
>>> +static int max9271_probe(struct i2c_client *client)
>>> +{
>>> +	struct max9271_device *max9271;
>>> +	struct fwnode_handle *ep;
>>> +	int ret;
>>> +
>>> +	max9271 = devm_kzalloc(&client->dev, sizeof(*max9271), GFP_KERNEL);
>>> +	if (!max9271)
>>> +		return -ENOMEM;
>>> +	max9271->dev = &client->dev;
>>> +	max9271->client = client;
>>> +
>>> +	/* Initialize and register the subdevice. */
>>> +	v4l2_i2c_subdev_init(&max9271->sd, client, &max9271_subdev_ops);
>>> +	max9271->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>>> +	max9271->pads[MAX9271_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
>>> +	max9271->pads[MAX9271_SINK_PAD].flags = MEDIA_PAD_FL_SINK;
>>> +	max9271->sd.entity.flags |= MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
>>
>> Is it a formatter - do we need a new/different entity type?
> 
> I admit I had an hard time choosing the correct entity type :)

Yes, I suspect it might just be that it's a distinct new thing? ...


>>> +	ret = media_entity_pads_init(&max9271->sd.entity, 2, max9271->pads);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 0, 0, 0);
>>> +	if (!ep) {
>>> +		dev_err(max9271->dev, "Unable to get endpoint 0: %pOF\n",
>>> +			max9271->dev->of_node);
>>> +		ret = -ENODEV;
>>> +		goto error_media_entity;
>>> +	}
>>> +
>>> +	max9271->sd.fwnode = ep;
>>> +	ret = v4l2_async_register_subdev(&max9271->sd);
>>> +	if (ret)
>>> +		goto error_put_node;
>>> +
>>> +	ret = max9271_parse_dt(max9271);
>>> +	if (ret)
>>> +		goto error_unregister_subdev;
>>> +
>>> +	ret = max9271_init(max9271);
>>> +	if (ret)
>>> +		goto error_unregister_subdev;
>>> +
>>> +	return 0;
>>> +
>>> +error_unregister_subdev:
>>> +	v4l2_async_unregister_subdev(&max9271->sd);
>>> +error_put_node:
>>> +	fwnode_handle_put(max9271->sd.fwnode);
>>> +error_media_entity:
>>> +	media_entity_cleanup(&max9271->sd.entity);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int max9271_remove(struct i2c_client *client)
>>> +{
>>> +	struct max9271_device *max9271 = i2c_to_max9271(client);
>>> +
>>> +	v4l2_ctrl_handler_free(&max9271->ctrls);
>>> +	v4l2_async_notifier_cleanup(&max9271->notifier);
>>> +	v4l2_async_unregister_subdev(&max9271->sd);
>>> +	fwnode_handle_put(max9271->sd.fwnode);
>>> +	media_entity_cleanup(&max9271->sd.entity);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct of_device_id max9271_of_ids[] = {
>>> +	{ .compatible = "imi,max9271", },
>>
>> This should be a 'maxim' prefix now, not IMI.
>>
> 
> Indeed!
> 
> Thanks
>   j
> 
>>
>>> +	{ }
>>> +};
>>> +MODULE_DEVICE_TABLE(of, max9271_of_ids);
>>> +
>>> +static struct i2c_driver max9271_i2c_driver = {
>>> +	.driver	= {
>>> +		.name	= "max9271",
>>> +		.of_match_table = max9271_of_ids,
>>> +	},
>>> +	.probe_new	= max9271_probe,
>>> +	.remove		= max9271_remove,
>>> +};
>>> +
>>> +module_i2c_driver(max9271_i2c_driver);
>>> +
>>> +MODULE_DESCRIPTION("MAX9271 GMSL serializer subdevice driver");
>>> +MODULE_AUTHOR("Jacopo Mondi");
>>> +MODULE_LICENSE("GPL");
>>>

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

* Re: [RFC 0/5] media: i2c: Add MAX9271 subdevice driver
  2021-08-17  7:26 [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Jacopo Mondi
                   ` (4 preceding siblings ...)
  2021-08-17  7:27 ` [RFC 5/5] arm64: dts: GMSL: Adapt to the use max9271 driver Jacopo Mondi
@ 2021-08-18 12:47 ` Kieran Bingham
  2021-08-23  2:12   ` Laurent Pinchart
  2021-08-23  2:06 ` Laurent Pinchart
  6 siblings, 1 reply; 21+ messages in thread
From: Kieran Bingham @ 2021-08-18 12:47 UTC (permalink / raw)
  To: Jacopo Mondi, Mauro Carvalho Chehab, Kieran Bingham,
	Laurent Pinchart, Niklas Söderlund
  Cc: Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	linux-renesas-soc, linux-media

Hi Jacopo,

On 17/08/2021 08:26, Jacopo Mondi wrote:
> Hello,
>    as noticed during the inclusion of the RDACM20/21 cameras, their driver make
> use of a library driver that exports functions to control the MAX9271 GMSL
> serializer embedded in the camera module.
> 
> This series attempts to create an i2c subdevice driver for the MAX9271
> serializer, to support the camera module operations using the v4l2 subdev
> operations.
> 
> The series is based on the currently in-review:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=5847
> https://patchwork.linuxtv.org/project/linux-media/list/?series=5949
> 
> The series:
> 1) Introduced an i2c subdev driver for the MAX9271 GMSL serializer
> 2) Adapt the RDACM20 driver by removing the MAX9271 handling from there
> 3) Modify the DTS layout to describe the MAX9271 chip and the camera module
>    separately
> 
> To be done:
> - bindings
> - handling of reset lines between max9271 and image sensor
> - the camera module drivers could be made sensor drivers
> 
> However I'm not fully convinced this really brings any benefit as the serializer
> and the image sensor are actually packed together in the same camera module
> and are tightly coupled.
> 
> The biggest issue I'm facing, and for which I would be happy to receive pointers
> to is the following one.
> 
> The new DTS layout now looks like
> 
> 	max9286 {
> 
> 		i2c-mux {
> 			i2c@0 {
> 				max9271 {
> 				}
> 
> 				rdacm20{
> 				}
> 			}
> 		}
> 	}

Is there any feasibility, or benefit to us modelling like:

> 	max9286 {
>
> 		i2c-mux {
> 			i2c@0 {
> 				rdacm20 {
>	 				max9271{
>					}
>					ov13858{
					}
> 				}
> 			}
> 		}
> 	}


Perhaps that doesn't actually give much benefit, but I feel like the
max9271 is a part of the rdacm20, so it should be contained within it,
not besides it ..


> If I do rely on the probe sequence implemented by the instantiation of the
> i2c-mux child nodes:
> 
> 	- max9286
> 		-max9271
> 		-sensor
> 
> 		-max9271
> 		-sensor
> 
> 		...
> 
> As per each i2c-mux subnode the max9271 and the connected sensor are probed once
> after the other.
> 
> This unfortunately doesn't play well with the requirements of GMSL bus, for
> which the post_register operation is being introduced. With the current
> RDACM20/21 drivers and post_register in place with two cameras connected to the
> system, the desired initialization sequence looks like:
> 
>             MAX9286                  RDACM20/21
> 
>             probe()
>                |
>                ---------------------> |
>                                       camera 1 probe() {
>                                          enable_threshold()
>                                       }
>                |<--------------------|
>             v4l2 async bound {
> 		completed = no
>                |
>                ---------------------> |
>                                       camera 2 probe() {
>                                          enable_threshold()
>                                       }
>                |<--------------------|
> 		completed = yes
> 
>                 compensate_amplitude()
> 
>                 call post_register()
>                |-------------------->|
>                                      camera 1 post_register()
>                                          access camera registers()
>                                     }
>                |<-------------------
>                |-------------------->|
>                                      camera 2 post_register()
>                                          access camera registers()
>                                     }
>                |<-------------------
>             }
> 
> Which guarantees that the bulk access to the camera registers happens after the
> deserializer has compensated the channel amplitude.
> 
> With the new model I do have a race between the sensor probe and the
> post_register() of the serializer in case a single camera is connected.
> 
> What happes is that as soon as the max9271 registers its async subdev the
> max9286 notifier completes an call max9271->post_register(). But at that time
> the sensor subdev has not probed yet, so there is no subdev on which to call
> post_register in the max9271
> 
> following:
> 
>     MAX9286                  MAX9271			SENSOR
> 
>     probe()
>        |
>        ---------------------> |
> 			      probe() {
> 				 enable_threshold()
> 			      }
>       }
>        |<--------------------|
>     v4l2 async bound {
> 	completed = yes
>  	subdev->post_register()
>        |-------------------->|
> 			     post_register()
> 				gmsl_bus_config()
> 				subdev->post_register(NULL)
> 				segfault
> 			    }
> 							probe()
>     }
> > If I instead do not use post_register() between the max9271 and the
sensor,
> then the model works for a single camera only (as it is implemented in this
> version)
> 
>     MAX9286                  MAX9271			SENSOR
> 
>     probe()
>        |
>        ---------------------> |
> 			      probe() {
> 				 enable_threshold()
> 			      }
>       }
>        |<--------------------|
>     v4l2 async bound {
> 	completed = no
>        |-------------------->|
> 							probe() {
> 							   i2c writes to
> 							   the sensor without
> 							   GMSL configuration
> 							}
>     }
> 
> So, my question is: are there examples on how to have the max9271 driver
> control the probe time the connected sensor without relying on the probe
> sequence of the I2C-mux device nodes ? If I could do so, what I would like to
> realize looks like
> 
>     MAX9286                  MAX9271			SENSOR
> 
>     probe()
>        |
>        ---------------------> |
> 			      camera 1 probe() {
> 				--------------------->|
> 							 sensor probe()
> 				 enable_threshold()
> 			      }
>       }
>        |<--------------------|
>     v4l2 async bound {
> 	completed = no
>        |-------------------->|
> 			     camera 2 probe() {
> 				--------------------->|
> 							sensor probe()
> 				 enable_threshold()
> 			      }
>        |<--------------------|
> 	completed = yes
> 
> 	compensate_amplitude()
> 	for (subdev)
> 	   subdev->post_register()
>           |----------------->|
> 			     camera 1 post_register()
> 				subdev->post_register()
> 				--------------------->|
> 							post_register()
> 								i2c writes
> 	   subdev->post_register()
>           |----------------->|
> 			     camera 2 post_register()
> 				subdev->post_register()
> 				--------------------->|
> 							post_register()
> 								i2c writes
>     }
> 


If we're still likely to have an RDACM20 container 'device' - I wonder
if it's possible that it could be responsible for making sure both of
it's subdevices (the max9271, and the sensor) are handled in the correct
sequence...



> 
> I recall Mauro pointed me to an example when he first suggested to make the
> MAX9271 library a proper i2c subdevice driver. Do you happen to recall which one
> was it ?
> 
> Thanks
>    j
> 
> Jacopo Mondi (5):
>   media: i2c: max9271: Rename max9271 library driver
>   media: i2c: Add MAX9271 I2C driver
>   media: i2c: rdacm20: Adapt to work with MAX9271
>   media: i2c: max9286: Fetch PIXEL_RATE in s_stream
>   arm64: dts: GMSL: Adapt to the use max9271 driver
> 
>  MAINTAINERS                                   |  17 +-
>  arch/arm64/boot/dts/renesas/gmsl-cameras.dtsi |  34 +-
>  .../arm64/boot/dts/renesas/r8a77970-eagle.dts |   6 +-
>  drivers/media/i2c/Kconfig                     |  12 +
>  drivers/media/i2c/Makefile                    |   3 +-
>  drivers/media/i2c/max9271-lib.c               | 374 +++++++++++++
>  .../media/i2c/{max9271.h => max9271-lib.h}    |   0
>  drivers/media/i2c/max9271.c                   | 528 +++++++++++++++---
>  drivers/media/i2c/max9286.c                   |   6 +-
>  drivers/media/i2c/rdacm20.c                   | 139 +----
>  drivers/media/i2c/rdacm21.c                   |   2 +-
>  11 files changed, 917 insertions(+), 204 deletions(-)
>  create mode 100644 drivers/media/i2c/max9271-lib.c
>  rename drivers/media/i2c/{max9271.h => max9271-lib.h} (100%)
> 
> --
> 2.32.0
> 

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

* Re: [RFC 1/5] media: i2c: max9271: Rename max9271 library driver
  2021-08-17  7:26 ` [RFC 1/5] media: i2c: max9271: Rename max9271 library driver Jacopo Mondi
@ 2021-08-18 12:48   ` Kieran Bingham
  2021-08-23  2:09   ` Laurent Pinchart
  1 sibling, 0 replies; 21+ messages in thread
From: Kieran Bingham @ 2021-08-18 12:48 UTC (permalink / raw)
  To: Jacopo Mondi, Mauro Carvalho Chehab, Kieran Bingham,
	Laurent Pinchart, Niklas Söderlund
  Cc: Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	linux-renesas-soc, linux-media

Hi Jacopo,

On 17/08/2021 08:26, Jacopo Mondi wrote:
> Support for the MAX9271 GMSL serializer was provided in the form of a
> library driver, with the RDACM20 and RDACM21 camera module drivers using
> the functions exported by the library.
> 
> In preparation to introduce an i2c subdevice driver to support the
> MAX9271 serializer, rename the library driver from max9271 to
> max9271-lib.

I may as well add this here now to save me later if this continues ;-)

Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>

> 
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> ---
>  MAINTAINERS                                    | 8 ++++----
>  drivers/media/i2c/Makefile                     | 2 +-
>  drivers/media/i2c/{max9271.c => max9271-lib.c} | 2 +-
>  drivers/media/i2c/{max9271.h => max9271-lib.h} | 0
>  drivers/media/i2c/rdacm20.c                    | 2 +-
>  drivers/media/i2c/rdacm21.c                    | 2 +-
>  6 files changed, 8 insertions(+), 8 deletions(-)
>  rename drivers/media/i2c/{max9271.c => max9271-lib.c} (99%)
>  rename drivers/media/i2c/{max9271.h => max9271-lib.h} (100%)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 524eabe50d79..7ad89cac19b7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15625,8 +15625,8 @@ M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
>  L:	linux-media@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
> -F:	drivers/media/i2c/max9271.c
> -F:	drivers/media/i2c/max9271.h
> +F:	drivers/media/i2c/max9271-lib.c
> +F:	drivers/media/i2c/max9271-lib.h
>  F:	drivers/media/i2c/rdacm20.c
>  
>  RDACM21 Camera Sensor
> @@ -15637,8 +15637,8 @@ M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
>  L:	linux-media@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
> -F:	drivers/media/i2c/max9271.c
> -F:	drivers/media/i2c/max9271.h
> +F:	drivers/media/i2c/max9271-lib.c
> +F:	drivers/media/i2c/max9271-lib.h
>  F:	drivers/media/i2c/rdacm21.c
>  
>  RDC R-321X SoC
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 83268f20aa3a..4d879373bd48 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -129,7 +129,7 @@ obj-$(CONFIG_VIDEO_IMX335)	+= imx335.o
>  obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
>  obj-$(CONFIG_VIDEO_IMX412)	+= imx412.o
>  obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
> -obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271.o
> +obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271-lib.o
>  obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
>  obj-$(CONFIG_VIDEO_RDACM21)	+= rdacm21.o
>  obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
> diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271-lib.c
> similarity index 99%
> rename from drivers/media/i2c/max9271.c
> rename to drivers/media/i2c/max9271-lib.c
> index ff86c8c4ea61..c554bb0f42f4 100644
> --- a/drivers/media/i2c/max9271.c
> +++ b/drivers/media/i2c/max9271-lib.c
> @@ -20,7 +20,7 @@
>  #include <linux/i2c.h>
>  #include <linux/module.h>
>  
> -#include "max9271.h"
> +#include "max9271-lib.h"
>  
>  static int max9271_read(struct max9271_device *dev, u8 reg)
>  {
> diff --git a/drivers/media/i2c/max9271.h b/drivers/media/i2c/max9271-lib.h
> similarity index 100%
> rename from drivers/media/i2c/max9271.h
> rename to drivers/media/i2c/max9271-lib.h
> diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
> index eb0e3dc22cc3..bf06a1c50306 100644
> --- a/drivers/media/i2c/rdacm20.c
> +++ b/drivers/media/i2c/rdacm20.c
> @@ -27,7 +27,7 @@
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-subdev.h>
>  
> -#include "max9271.h"
> +#include "max9271-lib.h"
>  
>  #define OV10635_I2C_ADDRESS		0x30
>  
> diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c
> index 35217782f693..3a05abe4e96c 100644
> --- a/drivers/media/i2c/rdacm21.c
> +++ b/drivers/media/i2c/rdacm21.c
> @@ -21,7 +21,7 @@
>  #include <media/v4l2-async.h>
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-subdev.h>
> -#include "max9271.h"
> +#include "max9271-lib.h"
>  
>  #define MAX9271_RESET_CYCLES		10
>  
> 

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

* Re: [RFC 0/5] media: i2c: Add MAX9271 subdevice driver
  2021-08-17  7:26 [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Jacopo Mondi
                   ` (5 preceding siblings ...)
  2021-08-18 12:47 ` [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Kieran Bingham
@ 2021-08-23  2:06 ` Laurent Pinchart
  2021-09-13  7:59   ` Jacopo Mondi
  6 siblings, 1 reply; 21+ messages in thread
From: Laurent Pinchart @ 2021-08-23  2:06 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Mauro Carvalho Chehab, Kieran Bingham, Niklas Söderlund,
	Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	linux-renesas-soc, linux-media

Hi Jacopo,

On Tue, Aug 17, 2021 at 09:26:58AM +0200, Jacopo Mondi wrote:
> Hello,
>    as noticed during the inclusion of the RDACM20/21 cameras, their driver make
> use of a library driver that exports functions to control the MAX9271 GMSL
> serializer embedded in the camera module.
> 
> This series attempts to create an i2c subdevice driver for the MAX9271
> serializer, to support the camera module operations using the v4l2 subdev
> operations.
> 
> The series is based on the currently in-review:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=5847
> https://patchwork.linuxtv.org/project/linux-media/list/?series=5949
> 
> The series:
> 1) Introduced an i2c subdev driver for the MAX9271 GMSL serializer
> 2) Adapt the RDACM20 driver by removing the MAX9271 handling from there
> 3) Modify the DTS layout to describe the MAX9271 chip and the camera module
>    separately
> 
> To be done:
> - bindings
> - handling of reset lines between max9271 and image sensor
> - the camera module drivers could be made sensor drivers
> 
> However I'm not fully convinced this really brings any benefit as the serializer
> and the image sensor are actually packed together in the same camera module
> and are tightly coupled.

I'm not convinced either. More than that, I think it will make it
impossible to handle more complex camera topologies.

> The biggest issue I'm facing, and for which I would be happy to receive pointers
> to is the following one.
> 
> The new DTS layout now looks like
> 
> 	max9286 {
> 
> 		i2c-mux {
> 			i2c@0 {
> 				max9271 {
> 				}
> 
> 				rdacm20{
> 				}
> 			}
> 		}
> 	}
> 
> If I do rely on the probe sequence implemented by the instantiation of the
> i2c-mux child nodes:
> 
> 	- max9286
> 		-max9271
> 		-sensor
> 
> 		-max9271
> 		-sensor
> 
> 		...
> 
> As per each i2c-mux subnode the max9271 and the connected sensor are probed once
> after the other.
> 
> This unfortunately doesn't play well with the requirements of GMSL bus, for
> which the post_register operation is being introduced. With the current
> RDACM20/21 drivers and post_register in place with two cameras connected to the
> system, the desired initialization sequence looks like:
> 
>             MAX9286                  RDACM20/21
> 
>             probe()
>                |
>                ---------------------> |
>                                       camera 1 probe() {
>                                          enable_threshold()
>                                       }
>                |<--------------------|
>             v4l2 async bound {
> 		completed = no
>                |
>                ---------------------> |
>                                       camera 2 probe() {
>                                          enable_threshold()
>                                       }
>                |<--------------------|
> 		completed = yes
> 
>                 compensate_amplitude()
> 
>                 call post_register()
>                |-------------------->|
>                                      camera 1 post_register()
>                                          access camera registers()
>                                     }
>                |<-------------------
>                |-------------------->|
>                                      camera 2 post_register()
>                                          access camera registers()
>                                     }
>                |<-------------------
>             }
> 
> Which guarantees that the bulk access to the camera registers happens after the
> deserializer has compensated the channel amplitude.
> 
> With the new model I do have a race between the sensor probe and the
> post_register() of the serializer in case a single camera is connected.
> 
> What happes is that as soon as the max9271 registers its async subdev the
> max9286 notifier completes an call max9271->post_register(). But at that time
> the sensor subdev has not probed yet, so there is no subdev on which to call
> post_register in the max9271
> 
> following:
> 
>     MAX9286                  MAX9271			SENSOR
> 
>     probe()
>        |
>        ---------------------> |
> 			      probe() {
> 				 enable_threshold()
> 			      }
>       }
>        |<--------------------|
>     v4l2 async bound {
> 	completed = yes
>  	subdev->post_register()
>        |-------------------->|
> 			     post_register()
> 				gmsl_bus_config()
> 				subdev->post_register(NULL)
> 				segfault
> 			    }
> 							probe()
>     }
> 
> If I instead do not use post_register() between the max9271 and the sensor,
> then the model works for a single camera only (as it is implemented in this
> version)
> 
>     MAX9286                  MAX9271			SENSOR
> 
>     probe()
>        |
>        ---------------------> |
> 			      probe() {
> 				 enable_threshold()
> 			      }
>       }
>        |<--------------------|
>     v4l2 async bound {
> 	completed = no
>        |-------------------->|
> 							probe() {
> 							   i2c writes to
> 							   the sensor without
> 							   GMSL configuration
> 							}
>     }
> 
> So, my question is: are there examples on how to have the max9271 driver
> control the probe time the connected sensor without relying on the probe
> sequence of the I2C-mux device nodes ? If I could do so, what I would like to
> realize looks like

How about making the sensor a child of the max9271 in DT ?

> 
>     MAX9286                  MAX9271			SENSOR
> 
>     probe()
>        |
>        ---------------------> |
> 			      camera 1 probe() {
> 				--------------------->|
> 							 sensor probe()
> 				 enable_threshold()
> 			      }
>       }
>        |<--------------------|
>     v4l2 async bound {
> 	completed = no
>        |-------------------->|
> 			     camera 2 probe() {
> 				--------------------->|
> 							sensor probe()
> 				 enable_threshold()
> 			      }
>        |<--------------------|
> 	completed = yes
> 
> 	compensate_amplitude()
> 	for (subdev)
> 	   subdev->post_register()
>           |----------------->|
> 			     camera 1 post_register()
> 				subdev->post_register()
> 				--------------------->|
> 							post_register()
> 								i2c writes
> 	   subdev->post_register()
>           |----------------->|
> 			     camera 2 post_register()
> 				subdev->post_register()
> 				--------------------->|
> 							post_register()
> 								i2c writes
>     }
> 
> 
> I recall Mauro pointed me to an example when he first suggested to make the
> MAX9271 library a proper i2c subdevice driver. Do you happen to recall which one
> was it ?
> 
> Thanks
>    j
> 
> Jacopo Mondi (5):
>   media: i2c: max9271: Rename max9271 library driver
>   media: i2c: Add MAX9271 I2C driver
>   media: i2c: rdacm20: Adapt to work with MAX9271
>   media: i2c: max9286: Fetch PIXEL_RATE in s_stream
>   arm64: dts: GMSL: Adapt to the use max9271 driver
> 
>  MAINTAINERS                                   |  17 +-
>  arch/arm64/boot/dts/renesas/gmsl-cameras.dtsi |  34 +-
>  .../arm64/boot/dts/renesas/r8a77970-eagle.dts |   6 +-
>  drivers/media/i2c/Kconfig                     |  12 +
>  drivers/media/i2c/Makefile                    |   3 +-
>  drivers/media/i2c/max9271-lib.c               | 374 +++++++++++++
>  .../media/i2c/{max9271.h => max9271-lib.h}    |   0
>  drivers/media/i2c/max9271.c                   | 528 +++++++++++++++---
>  drivers/media/i2c/max9286.c                   |   6 +-
>  drivers/media/i2c/rdacm20.c                   | 139 +----
>  drivers/media/i2c/rdacm21.c                   |   2 +-
>  11 files changed, 917 insertions(+), 204 deletions(-)
>  create mode 100644 drivers/media/i2c/max9271-lib.c
>  rename drivers/media/i2c/{max9271.h => max9271-lib.h} (100%)

-- 
Regards,

Laurent Pinchart

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

* Re: [RFC 1/5] media: i2c: max9271: Rename max9271 library driver
  2021-08-17  7:26 ` [RFC 1/5] media: i2c: max9271: Rename max9271 library driver Jacopo Mondi
  2021-08-18 12:48   ` Kieran Bingham
@ 2021-08-23  2:09   ` Laurent Pinchart
  1 sibling, 0 replies; 21+ messages in thread
From: Laurent Pinchart @ 2021-08-23  2:09 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Mauro Carvalho Chehab, Kieran Bingham, Niklas Söderlund,
	Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	linux-renesas-soc, linux-media

Hi Jacopo,

Thank you for the patch.

On Tue, Aug 17, 2021 at 09:26:59AM +0200, Jacopo Mondi wrote:
> Support for the MAX9271 GMSL serializer was provided in the form of a
> library driver, with the RDACM20 and RDACM21 camera module drivers using
> the functions exported by the library.
> 
> In preparation to introduce an i2c subdevice driver to support the
> MAX9271 serializer, rename the library driver from max9271 to
> max9271-lib.
> 
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Even if the series ended up being rejected (due, for instance, to
realizing this would be a bad approach), this may make sense on its own.

> ---
>  MAINTAINERS                                    | 8 ++++----
>  drivers/media/i2c/Makefile                     | 2 +-
>  drivers/media/i2c/{max9271.c => max9271-lib.c} | 2 +-
>  drivers/media/i2c/{max9271.h => max9271-lib.h} | 0
>  drivers/media/i2c/rdacm20.c                    | 2 +-
>  drivers/media/i2c/rdacm21.c                    | 2 +-
>  6 files changed, 8 insertions(+), 8 deletions(-)
>  rename drivers/media/i2c/{max9271.c => max9271-lib.c} (99%)
>  rename drivers/media/i2c/{max9271.h => max9271-lib.h} (100%)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 524eabe50d79..7ad89cac19b7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15625,8 +15625,8 @@ M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
>  L:	linux-media@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
> -F:	drivers/media/i2c/max9271.c
> -F:	drivers/media/i2c/max9271.h
> +F:	drivers/media/i2c/max9271-lib.c
> +F:	drivers/media/i2c/max9271-lib.h
>  F:	drivers/media/i2c/rdacm20.c
>  
>  RDACM21 Camera Sensor
> @@ -15637,8 +15637,8 @@ M:	Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
>  L:	linux-media@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
> -F:	drivers/media/i2c/max9271.c
> -F:	drivers/media/i2c/max9271.h
> +F:	drivers/media/i2c/max9271-lib.c
> +F:	drivers/media/i2c/max9271-lib.h
>  F:	drivers/media/i2c/rdacm21.c
>  
>  RDC R-321X SoC
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 83268f20aa3a..4d879373bd48 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -129,7 +129,7 @@ obj-$(CONFIG_VIDEO_IMX335)	+= imx335.o
>  obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
>  obj-$(CONFIG_VIDEO_IMX412)	+= imx412.o
>  obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
> -obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271.o
> +obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271-lib.o
>  obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
>  obj-$(CONFIG_VIDEO_RDACM21)	+= rdacm21.o
>  obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
> diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271-lib.c
> similarity index 99%
> rename from drivers/media/i2c/max9271.c
> rename to drivers/media/i2c/max9271-lib.c
> index ff86c8c4ea61..c554bb0f42f4 100644
> --- a/drivers/media/i2c/max9271.c
> +++ b/drivers/media/i2c/max9271-lib.c
> @@ -20,7 +20,7 @@
>  #include <linux/i2c.h>
>  #include <linux/module.h>
>  
> -#include "max9271.h"
> +#include "max9271-lib.h"
>  
>  static int max9271_read(struct max9271_device *dev, u8 reg)
>  {
> diff --git a/drivers/media/i2c/max9271.h b/drivers/media/i2c/max9271-lib.h
> similarity index 100%
> rename from drivers/media/i2c/max9271.h
> rename to drivers/media/i2c/max9271-lib.h
> diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
> index eb0e3dc22cc3..bf06a1c50306 100644
> --- a/drivers/media/i2c/rdacm20.c
> +++ b/drivers/media/i2c/rdacm20.c
> @@ -27,7 +27,7 @@
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-subdev.h>
>  
> -#include "max9271.h"
> +#include "max9271-lib.h"
>  
>  #define OV10635_I2C_ADDRESS		0x30
>  
> diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c
> index 35217782f693..3a05abe4e96c 100644
> --- a/drivers/media/i2c/rdacm21.c
> +++ b/drivers/media/i2c/rdacm21.c
> @@ -21,7 +21,7 @@
>  #include <media/v4l2-async.h>
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-subdev.h>
> -#include "max9271.h"
> +#include "max9271-lib.h"
>  
>  #define MAX9271_RESET_CYCLES		10
>  

-- 
Regards,

Laurent Pinchart

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

* Re: [RFC 0/5] media: i2c: Add MAX9271 subdevice driver
  2021-08-18 12:47 ` [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Kieran Bingham
@ 2021-08-23  2:12   ` Laurent Pinchart
  0 siblings, 0 replies; 21+ messages in thread
From: Laurent Pinchart @ 2021-08-23  2:12 UTC (permalink / raw)
  To: Kieran Bingham
  Cc: Jacopo Mondi, Mauro Carvalho Chehab, Kieran Bingham,
	Niklas Söderlund, Hans Verkuil, Sakari Ailus,
	Manivannan Sadhasivam, Thomas NIZAN, linux-renesas-soc,
	linux-media

Hello,

On Wed, Aug 18, 2021 at 01:47:02PM +0100, Kieran Bingham wrote:
> On 17/08/2021 08:26, Jacopo Mondi wrote:
> > Hello,
> >    as noticed during the inclusion of the RDACM20/21 cameras, their driver make
> > use of a library driver that exports functions to control the MAX9271 GMSL
> > serializer embedded in the camera module.
> > 
> > This series attempts to create an i2c subdevice driver for the MAX9271
> > serializer, to support the camera module operations using the v4l2 subdev
> > operations.
> > 
> > The series is based on the currently in-review:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=5847
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=5949
> > 
> > The series:
> > 1) Introduced an i2c subdev driver for the MAX9271 GMSL serializer
> > 2) Adapt the RDACM20 driver by removing the MAX9271 handling from there
> > 3) Modify the DTS layout to describe the MAX9271 chip and the camera module
> >    separately
> > 
> > To be done:
> > - bindings
> > - handling of reset lines between max9271 and image sensor
> > - the camera module drivers could be made sensor drivers
> > 
> > However I'm not fully convinced this really brings any benefit as the serializer
> > and the image sensor are actually packed together in the same camera module
> > and are tightly coupled.
> > 
> > The biggest issue I'm facing, and for which I would be happy to receive pointers
> > to is the following one.
> > 
> > The new DTS layout now looks like
> > 
> > 	max9286 {
> > 
> > 		i2c-mux {
> > 			i2c@0 {
> > 				max9271 {
> > 				}
> > 
> > 				rdacm20{
> > 				}
> > 			}
> > 		}
> > 	}
> 
> Is there any feasibility, or benefit to us modelling like:
> 
> > 	max9286 {
> >
> > 		i2c-mux {
> > 			i2c@0 {
> > 				rdacm20 {
> >	 				max9271{
> >					}
> >					ov13858{
> 					}
> > 				}

I don't see the point to be honest. If we have an rdacm20 device and a
corresponding driver, it's pointless to have the max9271 and ov13858
devices in DT. All the information needed would already be known to the
rdacm20 driver. A "detailed" DT description would only make sense if we
modelled the max9271 and ov13858, but not the rdacm20. In that case, I'd
make the ov13858 a child of the max9271.

I had misread this when replying to the cover letter btw, and thought
the rdacm20 node beside the max9271 was the ov13858.

> > 			}
> > 		}
> > 	}
> 
> Perhaps that doesn't actually give much benefit, but I feel like the
> max9271 is a part of the rdacm20, so it should be contained within it,
> not besides it ..
> 
> > If I do rely on the probe sequence implemented by the instantiation of the
> > i2c-mux child nodes:
> > 
> > 	- max9286
> > 		-max9271
> > 		-sensor
> > 
> > 		-max9271
> > 		-sensor
> > 
> > 		...
> > 
> > As per each i2c-mux subnode the max9271 and the connected sensor are probed once
> > after the other.
> > 
> > This unfortunately doesn't play well with the requirements of GMSL bus, for
> > which the post_register operation is being introduced. With the current
> > RDACM20/21 drivers and post_register in place with two cameras connected to the
> > system, the desired initialization sequence looks like:
> > 
> >             MAX9286                  RDACM20/21
> > 
> >             probe()
> >                |
> >                ---------------------> |
> >                                       camera 1 probe() {
> >                                          enable_threshold()
> >                                       }
> >                |<--------------------|
> >             v4l2 async bound {
> > 		completed = no
> >                |
> >                ---------------------> |
> >                                       camera 2 probe() {
> >                                          enable_threshold()
> >                                       }
> >                |<--------------------|
> > 		completed = yes
> > 
> >                 compensate_amplitude()
> > 
> >                 call post_register()
> >                |-------------------->|
> >                                      camera 1 post_register()
> >                                          access camera registers()
> >                                     }
> >                |<-------------------
> >                |-------------------->|
> >                                      camera 2 post_register()
> >                                          access camera registers()
> >                                     }
> >                |<-------------------
> >             }
> > 
> > Which guarantees that the bulk access to the camera registers happens after the
> > deserializer has compensated the channel amplitude.
> > 
> > With the new model I do have a race between the sensor probe and the
> > post_register() of the serializer in case a single camera is connected.
> > 
> > What happes is that as soon as the max9271 registers its async subdev the
> > max9286 notifier completes an call max9271->post_register(). But at that time
> > the sensor subdev has not probed yet, so there is no subdev on which to call
> > post_register in the max9271
> > 
> > following:
> > 
> >     MAX9286                  MAX9271			SENSOR
> > 
> >     probe()
> >        |
> >        ---------------------> |
> > 			      probe() {
> > 				 enable_threshold()
> > 			      }
> >       }
> >        |<--------------------|
> >     v4l2 async bound {
> > 	completed = yes
> >  	subdev->post_register()
> >        |-------------------->|
> > 			     post_register()
> > 				gmsl_bus_config()
> > 				subdev->post_register(NULL)
> > 				segfault
> > 			    }
> > 							probe()
> >     }
> > > If I instead do not use post_register() between the max9271 and the
> sensor,
> > then the model works for a single camera only (as it is implemented in this
> > version)
> > 
> >     MAX9286                  MAX9271			SENSOR
> > 
> >     probe()
> >        |
> >        ---------------------> |
> > 			      probe() {
> > 				 enable_threshold()
> > 			      }
> >       }
> >        |<--------------------|
> >     v4l2 async bound {
> > 	completed = no
> >        |-------------------->|
> > 							probe() {
> > 							   i2c writes to
> > 							   the sensor without
> > 							   GMSL configuration
> > 							}
> >     }
> > 
> > So, my question is: are there examples on how to have the max9271 driver
> > control the probe time the connected sensor without relying on the probe
> > sequence of the I2C-mux device nodes ? If I could do so, what I would like to
> > realize looks like
> > 
> >     MAX9286                  MAX9271			SENSOR
> > 
> >     probe()
> >        |
> >        ---------------------> |
> > 			      camera 1 probe() {
> > 				--------------------->|
> > 							 sensor probe()
> > 				 enable_threshold()
> > 			      }
> >       }
> >        |<--------------------|
> >     v4l2 async bound {
> > 	completed = no
> >        |-------------------->|
> > 			     camera 2 probe() {
> > 				--------------------->|
> > 							sensor probe()
> > 				 enable_threshold()
> > 			      }
> >        |<--------------------|
> > 	completed = yes
> > 
> > 	compensate_amplitude()
> > 	for (subdev)
> > 	   subdev->post_register()
> >           |----------------->|
> > 			     camera 1 post_register()
> > 				subdev->post_register()
> > 				--------------------->|
> > 							post_register()
> > 								i2c writes
> > 	   subdev->post_register()
> >           |----------------->|
> > 			     camera 2 post_register()
> > 				subdev->post_register()
> > 				--------------------->|
> > 							post_register()
> > 								i2c writes
> >     }
> > 
> 
> If we're still likely to have an RDACM20 container 'device' - I wonder
> if it's possible that it could be responsible for making sure both of
> it's subdevices (the max9271, and the sensor) are handled in the correct
> sequence...
> 
> > I recall Mauro pointed me to an example when he first suggested to make the
> > MAX9271 library a proper i2c subdevice driver. Do you happen to recall which one
> > was it ?
> > 
> > Thanks
> >    j
> > 
> > Jacopo Mondi (5):
> >   media: i2c: max9271: Rename max9271 library driver
> >   media: i2c: Add MAX9271 I2C driver
> >   media: i2c: rdacm20: Adapt to work with MAX9271
> >   media: i2c: max9286: Fetch PIXEL_RATE in s_stream
> >   arm64: dts: GMSL: Adapt to the use max9271 driver
> > 
> >  MAINTAINERS                                   |  17 +-
> >  arch/arm64/boot/dts/renesas/gmsl-cameras.dtsi |  34 +-
> >  .../arm64/boot/dts/renesas/r8a77970-eagle.dts |   6 +-
> >  drivers/media/i2c/Kconfig                     |  12 +
> >  drivers/media/i2c/Makefile                    |   3 +-
> >  drivers/media/i2c/max9271-lib.c               | 374 +++++++++++++
> >  .../media/i2c/{max9271.h => max9271-lib.h}    |   0
> >  drivers/media/i2c/max9271.c                   | 528 +++++++++++++++---
> >  drivers/media/i2c/max9286.c                   |   6 +-
> >  drivers/media/i2c/rdacm20.c                   | 139 +----
> >  drivers/media/i2c/rdacm21.c                   |   2 +-
> >  11 files changed, 917 insertions(+), 204 deletions(-)
> >  create mode 100644 drivers/media/i2c/max9271-lib.c
> >  rename drivers/media/i2c/{max9271.h => max9271-lib.h} (100%)
> > 

-- 
Regards,

Laurent Pinchart

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

* Re: [RFC 4/5] media: i2c: max9286: Fetch PIXEL_RATE in s_stream
  2021-08-17  7:27 ` [RFC 4/5] media: i2c: max9286: Fetch PIXEL_RATE in s_stream Jacopo Mondi
@ 2021-08-23  2:17   ` Laurent Pinchart
  2021-08-23  7:20     ` Jacopo Mondi
  0 siblings, 1 reply; 21+ messages in thread
From: Laurent Pinchart @ 2021-08-23  2:17 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Mauro Carvalho Chehab, Kieran Bingham, Niklas Söderlund,
	Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	linux-renesas-soc, linux-media

Hi Jacopo,

Thank you for the patch.

On Tue, Aug 17, 2021 at 09:27:02AM +0200, Jacopo Mondi wrote:
> The max9286 driver needs to fetch the remote serializer PIXEL_RATE
> control value in order to compute its own one, as the sum of the

s/its own one/its own/ (or its own pixel rate)

> values reported by the connected subdevices.
> 
> Currently the control is verified to be present at notifier's bound
> time, which requires the serializer driver to register the control at
> probe time. As the serializer driver might need to register the control
> later, by adding the control handler of its connected sensor, post-pone
> the max9286 check for the control availability at start stream time.
> 
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> ---
>  drivers/media/i2c/max9286.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> index 1b92d18a1f94..98fc90339a9e 100644
> --- a/drivers/media/i2c/max9286.c
> +++ b/drivers/media/i2c/max9286.c
> @@ -595,7 +595,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
>  	max9286_check_config_link(priv, priv->source_mask);
>  	max9286_configure_i2c(priv, false);
>  
> -	return max9286_set_pixelrate(priv);
> +	return 0;
>  }
>  
>  static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
> @@ -674,6 +674,10 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
>  	int ret;
>  
>  	if (enable) {
> +		ret = max9286_set_pixelrate(priv);
> +		if (ret)
> +			return ret;

That's very likely not going to work. The CSI-2 receiver connected to
the max9286 will need to know the pixel rate as part of its
initialization sequence, before calling .s_stream(1) on the max9286.

> +
>  		/*
>  		 * The frame sync between cameras is transmitted across the
>  		 * reverse channel as GPIO. We must open all channels while

-- 
Regards,

Laurent Pinchart

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

* Re: [RFC 2/5] media: i2c: Add MAX9271 I2C driver
  2021-08-18  8:27     ` Jacopo Mondi
  2021-08-18 12:38       ` Kieran Bingham
@ 2021-08-23  2:22       ` Laurent Pinchart
  2021-08-23  7:21         ` Jacopo Mondi
  2021-08-23 12:05       ` Geert Uytterhoeven
  2 siblings, 1 reply; 21+ messages in thread
From: Laurent Pinchart @ 2021-08-23  2:22 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Kieran Bingham, Jacopo Mondi, Mauro Carvalho Chehab,
	Kieran Bingham, Niklas Söderlund, Hans Verkuil,
	Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	linux-renesas-soc, linux-media

On Wed, Aug 18, 2021 at 10:27:00AM +0200, Jacopo Mondi wrote:
> Hi Kieran, thanks for review!
> 
> On Tue, Aug 17, 2021 at 04:49:17PM +0100, Kieran Bingham wrote:
> > Hi Jacopo,
> >
> > On 17/08/2021 08:27, Jacopo Mondi wrote:
> > > The MAX9271 is a GMSL serializer that serializes a video stream
> > > received from an image sensor through the parallel video bus.
> >
> >
> > https://datasheets.maximintegrated.com/en/ds/MAX9271.pdf calls it a
> > "16-Bit GMSL Serializer with Coas or STP Cable Drive"
> >
> 
> Nice we have a public datasheet now!
> Nice, well, it might mean GMSL is just old ?
> 
> >
> > > The serializer it's usually found embedded with an image sensor and
> >
> > s/it's/is/
> >
> > > other ancillary chips in camera modules like RDACM20 and RDACM21.
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> > > ---
> > >  MAINTAINERS                 |   9 +
> > >  drivers/media/i2c/Kconfig   |  12 +
> > >  drivers/media/i2c/Makefile  |   1 +
> > >  drivers/media/i2c/max9271.c | 756 ++++++++++++++++++++++++++++++++++++
> > >  4 files changed, 778 insertions(+)
> > >  create mode 100644 drivers/media/i2c/max9271.c
> > >
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 7ad89cac19b7..2dab25a08c9c 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -11244,6 +11244,15 @@ F:	Documentation/hwmon/max6697.rst
> > >  F:	drivers/hwmon/max6697.c
> > >  F:	include/linux/platform_data/max6697.h
> > >
> > > +MAX9271 GMSL SERIALIZER 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:	drivers/media/i2c/max9271.c
> > > +
> > >  MAX9286 QUAD GMSL DESERIALIZER DRIVER
> > >  M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
> > >  M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> > > diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> > > index 08feb3e8c1bf..b793d1f322d9 100644
> > > --- a/drivers/media/i2c/Kconfig
> > > +++ b/drivers/media/i2c/Kconfig
> > > @@ -1300,6 +1300,18 @@ source "drivers/media/i2c/m5mols/Kconfig"
> > >  config VIDEO_MAX9271_LIB
> > >  	tristate
> > >
> > > +config VIDEO_MAX9271
> > > +	tristate "MAX9271 GMSL serializer support"
> > > +	depends on I2C
> > > +	select V4L2_FWNODE
> > > +	select VIDEO_V4L2_SUBDEV_API
> > > +	select MEDIA_CONTROLLER
> > > +	help
> > > +	  This driver supports the Maxim MAX9271 GMSL serializer.
> > > +
> > > +	  To compile this driver as a module, choose M here: the
> > > +	  module will be called max9271.
> > > +
> > >  config VIDEO_RDACM20
> > >  	tristate "IMI RDACM20 camera support"
> > >  	depends on I2C
> > > diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> > > index 4d879373bd48..37bb51065574 100644
> > > --- a/drivers/media/i2c/Makefile
> > > +++ b/drivers/media/i2c/Makefile
> > > @@ -130,6 +130,7 @@ obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
> > >  obj-$(CONFIG_VIDEO_IMX412)	+= imx412.o
> > >  obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
> > >  obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271-lib.o
> > > +obj-$(CONFIG_VIDEO_MAX9271)	+= max9271.o
> > >  obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
> > >  obj-$(CONFIG_VIDEO_RDACM21)	+= rdacm21.o
> > >  obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
> > > diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c
> > > new file mode 100644
> > > index 000000000000..64987cba3d3e
> > > --- /dev/null
> > > +++ b/drivers/media/i2c/max9271.c
> > > @@ -0,0 +1,756 @@
> > > +// SPDX-License-Identifier: GPL-2.0+
> > > +/*
> > > + * Copyright (C) 2017-2021 Jacopo Mondi
> > > + * Copyright (C) 2017-2020 Kieran Bingham
> > > + * Copyright (C) 2017-2019 Laurent Pinchart
> > > + * Copyright (C) 2017-2019 Niklas Söderlund
> > > + * Copyright (C) 2016 Renesas Electronics Corporation
> > > + * Copyright (C) 2015 Cogent Embedded, Inc.
> > > + */
> > > +
> > > +#include <linux/delay.h>
> > > +#include <linux/fwnode.h>
> > > +#include <linux/init.h>
> > > +#include <linux/i2c.h>
> > > +#include <linux/module.h>
> > > +#include <linux/property.h>
> > > +
> > > +#include <media/v4l2-async.h>
> > > +#include <media/v4l2-ctrls.h>
> > > +#include <media/v4l2-fwnode.h>
> > > +#include <media/v4l2-subdev.h>
> > > +
> > > +#define MAX9271_DEFAULT_ADDR	0x40
> > > +
> > > +/* Register 0x02 */
> > > +#define MAX9271_SPREAD_SPECT_0		(0 << 5)
> > > +#define MAX9271_SPREAD_SPECT_05		(1 << 5)
> > > +#define MAX9271_SPREAD_SPECT_15		(2 << 5)
> > > +#define MAX9271_SPREAD_SPECT_1		(5 << 5)
> > > +#define MAX9271_SPREAD_SPECT_2		(3 << 5)
> > > +#define MAX9271_SPREAD_SPECT_3		(6 << 5)
> > > +#define MAX9271_SPREAD_SPECT_4		(7 << 5)
> > > +#define MAX9271_R02_RES			BIT(4)
> > > +#define MAX9271_PCLK_AUTODETECT		(3 << 2)
> > > +#define MAX9271_SERIAL_AUTODETECT	(0x03)
> > > +/* Register 0x04 */
> > > +#define MAX9271_SEREN			BIT(7)
> > > +#define MAX9271_CLINKEN			BIT(6)
> > > +#define MAX9271_PRBSEN			BIT(5)
> > > +#define MAX9271_SLEEP			BIT(4)
> > > +#define MAX9271_INTTYPE_I2C		(0 << 2)
> > > +#define MAX9271_INTTYPE_UART		(1 << 2)
> > > +#define MAX9271_INTTYPE_NONE		(2 << 2)
> > > +#define MAX9271_REVCCEN			BIT(1)
> > > +#define MAX9271_FWDCCEN			BIT(0)
> > > +/* Register 0x07 */
> > > +#define MAX9271_DBL			BIT(7)
> > > +#define MAX9271_DRS			BIT(6)
> > > +#define MAX9271_BWS			BIT(5)
> > > +#define MAX9271_ES			BIT(4)
> > > +#define MAX9271_HVEN			BIT(2)
> > > +#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
> > > +#define MAX9271_EDC_6BIT_CRC		(1 << 0)
> > > +#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
> > > +/* Register 0x08 */
> > > +#define MAX9271_INVVS			BIT(7)
> > > +#define MAX9271_INVHS			BIT(6)
> > > +#define MAX9271_REV_LOGAIN		BIT(3)
> > > +#define MAX9271_REV_HIVTH		BIT(0)
> > > +/* Register 0x09 */
> > > +#define MAX9271_ID			0x09
> > > +/* Register 0x0d */
> > > +#define MAX9271_I2CLOCACK		BIT(7)
> > > +#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
> > > +#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
> > > +#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
> > > +#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
> > > +#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
> > > +#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
> > > +#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
> > > +#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
> > > +#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
> > > +#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
> > > +#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
> > > +#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
> > > +#define MAX9271_I2CSLVTO_NONE		(3 << 0)
> > > +#define MAX9271_I2CSLVTO_1024US		(2 << 0)
> > > +#define MAX9271_I2CSLVTO_256US		(1 << 0)
> > > +#define MAX9271_I2CSLVTO_64US		(0 << 0)
> > > +/* Register 0x0f */
> > > +#define MAX9271_GPIO5OUT		BIT(5)
> > > +#define MAX9271_GPIO4OUT		BIT(4)
> > > +#define MAX9271_GPIO3OUT		BIT(3)
> > > +#define MAX9271_GPIO2OUT		BIT(2)
> > > +#define MAX9271_GPIO1OUT		BIT(1)
> > > +#define MAX9271_GPO			BIT(0)
> > > +/* Register 0x15 */
> > > +#define MAX9271_PCLKDET			BIT(0)
> > > +
> > > +struct max9271_device {
> > > +	struct device			*dev;
> > > +	struct i2c_client		*client;
> > > +	struct v4l2_subdev		sd;
> > > +#define MAX9271_SOURCE_PAD	0
> > > +#define MAX9271_SINK_PAD	1
> > > +	struct media_pad		pads[2];
> > > +	struct v4l2_async_notifier	notifier;
> > > +	struct v4l2_async_subdev	*asd;
> > > +	struct v4l2_ctrl_handler	ctrls;
> > > +	struct v4l2_subdev		*sensor;
> > > +};
> > > +
> > > +static inline struct max9271_device *sd_to_max9271(struct v4l2_subdev *sd)
> > > +{
> > > +	return container_of(sd, struct max9271_device, sd);
> > > +}
> > > +
> > > +static inline struct max9271_device *i2c_to_max9271(struct i2c_client *client)
> > > +{
> > > +	return sd_to_max9271(i2c_get_clientdata(client));
> > > +}
> > > +
> > > +static inline struct max9271_device *notifier_to_max9271(
> > > +						struct v4l2_async_notifier *nf)
> > > +{
> > > +	return container_of(nf, struct max9271_device, notifier);
> > > +}
> > > +
> > > +/* --- MAX9271 hardware operations --- */
> > > +
> > > +static int max9271_read(struct max9271_device *dev, u8 reg)
> > > +{
> > > +	int ret;
> > > +
> > > +	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
> > > +
> > > +	ret = i2c_smbus_read_byte_data(dev->client, reg);
> > > +	if (ret < 0)
> > > +		dev_dbg(&dev->client->dev,
> > > +			"%s: register 0x%02x read failed (%d)\n",
> > > +			__func__, reg, ret);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int max9271_write(struct max9271_device *dev, u8 reg, u8 val)
> > > +{
> > > +	int ret;
> > > +
> > > +	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
> > > +
> > > +	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
> > > +	if (ret < 0)
> > > +		dev_err(&dev->client->dev,
> > > +			"%s: register 0x%02x write failed (%d)\n",
> > > +			__func__, reg, ret);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +/*
> > > + * max9271_pclk_detect() - Detect valid pixel clock from image sensor
> > > + *
> > > + * Wait up to 10ms for a valid pixel clock.
> > > + *
> > > + * Returns 0 for success, < 0 for pixel clock not properly detected
> > > + */
> > > +static int max9271_pclk_detect(struct max9271_device *dev)
> > > +{
> > > +	unsigned int i;
> > > +	int ret;
> > > +
> > > +	for (i = 0; i < 100; i++) {
> > > +		ret = max9271_read(dev, 0x15);
> > > +		if (ret < 0)
> > > +			return ret;
> > > +
> > > +		if (ret & MAX9271_PCLKDET)
> > > +			return 0;
> > > +
> > > +		usleep_range(50, 100);
> > > +	}
> > > +
> > > +	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
> > > +
> > > +	return -EIO;
> > > +}
> > > +
> > > +static void max9271_wake_up(struct max9271_device *dev)
> > > +{
> > > +	/*
> > > +	 * Use the chip default address as this function has to be called
> > > +	 * before any other one.
> > > +	 */
> > > +	dev->client->addr = MAX9271_DEFAULT_ADDR;
> > > +	i2c_smbus_read_byte(dev->client);
> > > +	usleep_range(5000, 8000);
> > > +}
> > > +
> > > +static int max9271_set_serial_link(struct max9271_device *dev, bool enable)
> > > +{
> > > +	int ret;
> > > +	u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN;
> > > +
> > > +	if (enable) {
> > > +		ret = max9271_pclk_detect(dev);
> > > +		if (ret)
> > > +			return ret;
> > > +
> > > +		val |= MAX9271_SEREN;
> > > +	} else {
> > > +		val |= MAX9271_CLINKEN;
> > > +	}
> > > +
> > > +	/*
> > > +	 * 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.
> > > +	 */
> > > +	ret = max9271_write(dev, 0x04, val);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	usleep_range(5000, 8000);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config)
> > > +{
> > > +	int ret;
> > > +
> > > +	ret = max9271_write(dev, 0x0d, i2c_config);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	/* The delay required after an I2C bus configuration change is not
> >
> > /*
> >  * The delay ... ?
> >
> 
> Ah, ups
> 
> >
> >
> > > +	 * characterized in the serializer manual. Sleep up to 5msec to
> > > +	 * stay safe.
> > > +	 */
> > > +	usleep_range(3500, 5000);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
> > > +{
> > > +	int ret;
> > > +
> > > +	ret = max9271_read(dev, 0x08);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	/*
> > > +	 * Enable or disable reverse channel high threshold to increase
> > > +	 * immunity to power supply noise.
> > > +	 */
> > > +	ret = max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	usleep_range(2000, 2500);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int max9271_configure_gmsl_link(struct max9271_device *dev)
> > > +{
> > > +	int ret;
> > > +
> > > +	/*
> > > +	 * 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
> > > +	 *
> > > +	 * TODO: Make the GMSL link configuration parametric.
> > > +	 */
> > > +	ret = max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
> > > +			    MAX9271_EDC_1BIT_PARITY);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	usleep_range(5000, 8000);
> > > +
> > > +	/*
> > > +	 * Adjust spread spectrum to +4% and auto-detect pixel clock
> > > +	 * and serial link rate.
> > > +	 */
> > > +	ret = max9271_write(dev, 0x02,
> > > +			    MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
> > > +			    MAX9271_PCLK_AUTODETECT |
> > > +			    MAX9271_SERIAL_AUTODETECT);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	usleep_range(5000, 8000);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask)
> > > +{
> > > +	int ret;
> > > +
> > > +	ret = max9271_read(dev, 0x0f);
> >
> > What is 0x0f?
> >
> > Ah - the registers are unnamed...
> >
> > Seems a shame..
> 
> It is a bit. If the overall approach is validated, I'll go and
> beautify the driver.
> 
> >
> >
> > > +	if (ret < 0)
> > > +		return 0;
> > > +
> > > +	ret |= gpio_mask;
> > > +	ret = max9271_write(dev, 0x0f, ret);
> > > +	if (ret < 0) {
> > > +		dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	usleep_range(3500, 5000);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask)
> > > +{
> > > +	int ret;
> > > +
> > > +	ret = max9271_read(dev, 0x0f);
> > > +	if (ret < 0)
> > > +		return 0;
> > > +
> > > +	ret &= ~gpio_mask;
> > > +	ret = max9271_write(dev, 0x0f, ret);
> > > +	if (ret < 0) {
> > > +		dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	usleep_range(3500, 5000);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask)
> > > +{
> > > +	int ret;
> > > +
> > > +	ret = max9271_read(dev, 0x0e);
> > > +	if (ret < 0)
> > > +		return 0;
> > > +
> > > +	/* BIT(0) reserved: GPO is always enabled. */
> > > +	ret |= (gpio_mask & ~BIT(0));
> > > +	ret = max9271_write(dev, 0x0e, ret);
> > > +	if (ret < 0) {
> > > +		dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	usleep_range(3500, 5000);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int max9271_verify_id(struct max9271_device *dev)
> > > +{
> > > +	int ret;
> > > +
> > > +	ret = max9271_read(dev, 0x1e);
> > > +	if (ret < 0) {
> > > +		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
> > > +			ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	if (ret != MAX9271_ID) {
> > > +		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
> > > +			ret);
> > > +		return -ENXIO;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int max9271_set_address(struct max9271_device *dev, u8 addr)
> > > +{
> > > +	int ret;
> > > +
> > > +	ret = max9271_write(dev, 0x00, addr << 1);
> > > +	if (ret < 0) {
> > > +		dev_err(&dev->client->dev,
> > > +			"MAX9271 I2C address change failed (%d)\n", ret);
> > > +		return ret;
> > > +	}
> > > +	usleep_range(3500, 5000);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +/* --- V4L2 Subdev Ops --- */
> > > +
> > > +static int max9271_s_stream(struct v4l2_subdev *sd, int enable)
> > > +{
> > > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > > +
> > > +	return max9271_set_serial_link(max9271, enable);
> > > +}
> > > +
> > > +static int max9271_enum_mbus_code(struct v4l2_subdev *sd,
> > > +				  struct v4l2_subdev_state *sd_state,
> > > +				  struct v4l2_subdev_mbus_code_enum *code)
> > > +{
> > > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > > +
> > > +	return v4l2_subdev_call(max9271->sensor, pad, enum_mbus_code, NULL,
> > > +				code);
> > > +}
> > > +
> > > +static int max9271_get_fmt(struct v4l2_subdev *sd,
> > > +			   struct v4l2_subdev_state *sd_state,
> > > +			   struct v4l2_subdev_format *format)
> > > +{
> > > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > > +
> > > +	return v4l2_subdev_call(max9271->sensor, pad, get_fmt, NULL,
> > > +				format);
> > > +}
> > > +
> > > +static int max9271_set_fmt(struct v4l2_subdev *sd,
> > > +			   struct v4l2_subdev_state *sd_state,
> > > +			   struct v4l2_subdev_format *format)
> > > +{
> > > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > > +
> > > +	return v4l2_subdev_call(max9271->sensor, pad, set_fmt, NULL,
> > > +				format);
> > > +}
> > > +
> > > +static int max9271_post_register(struct v4l2_subdev *sd)
> > > +{
> > > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > > +	int ret;
> > > +
> > > +	ret = max9271_verify_id(max9271);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	ret = max9271_enable_gpios(max9271, MAX9271_GPIO1OUT);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	ret = max9271_configure_gmsl_link(max9271);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct v4l2_subdev_video_ops max9271_video_ops = {
> > > +	.s_stream	= max9271_s_stream,
> > > +};
> > > +
> > > +static const struct v4l2_subdev_pad_ops max9271_subdev_pad_ops = {
> > > +	.enum_mbus_code = max9271_enum_mbus_code,
> > > +	.get_fmt	= max9271_get_fmt,
> > > +	.set_fmt	= max9271_set_fmt,
> > > +};
> > > +
> > > +static const struct v4l2_subdev_core_ops max9271_core_ops = {
> > > +	.post_register	= max9271_post_register,
> > > +};
> > > +
> > > +static const struct v4l2_subdev_ops max9271_subdev_ops = {
> > > +	.core		= &max9271_core_ops,
> > > +	.video		= &max9271_video_ops,
> > > +	.pad		= &max9271_subdev_pad_ops,
> > > +};
> > > +
> > > +/* --- V4L2 Async Notifier --- */
> > > +
> > > +static int max9271_notify_bound(struct v4l2_async_notifier *notifier,
> > > +				struct v4l2_subdev *subdev,
> > > +				struct v4l2_async_subdev *asd)
> > > +{
> > > +	struct max9271_device *max9271 = notifier_to_max9271(notifier);
> > > +	int ret, pad;
> > > +
> > > +	/*
> > > +	 * Reserve more space than necessary for controls inherited by the
> > > +	 * remote subdev.
> > > +	 */
> > > +	ret = v4l2_ctrl_handler_init(&max9271->ctrls, 16);
> > > +	if (ret < 0) {
> > > +		dev_err(max9271->dev,
> > > +			"Unable to initialize control handler: %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = v4l2_ctrl_add_handler(&max9271->ctrls, subdev->ctrl_handler,
> > > +				    NULL, true);
> > > +	if (ret < 0) {
> > > +		dev_err(max9271->dev,
> > > +			"Unable to add subdev control handler: %d\n", ret);
> > > +		goto error_free_handler;
> > > +	}
> > > +	max9271->sd.ctrl_handler = &max9271->ctrls;
> > > +
> > > +	/* Create media link with the remote sensor source pad. */
> > > +	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
> > > +					  MEDIA_PAD_FL_SOURCE);
> > > +	if (pad < 0) {
> > > +		dev_err(max9271->dev,
> > > +			"Failed to find source pad for %s\n", subdev->name);
> > > +		ret = pad;
> > > +		goto error_free_handler;
> > > +	}
> > > +
> > > +	ret = media_create_pad_link(&subdev->entity, pad,
> > > +				    &max9271->sd.entity, MAX9271_SINK_PAD,
> > > +				    MEDIA_LNK_FL_ENABLED |
> > > +				    MEDIA_LNK_FL_IMMUTABLE);
> > > +	if (ret)
> > > +		goto error_free_handler;
> > > +
> > > +	max9271->sensor = subdev;
> > > +
> > > +	/*
> > > +	 * Hold OV10635 in reset during max9271 configuration. The reset signal
> >
> > Hold the sensor in reset ...
> >
> > But are all sensors guaranteed to be connected to the GPIO in the same way?
> >
> > Will this cause us issues? - does the GPIO need to be modelled somehow
> > to determine what's connected to them?
> >
> > > +	 * has to be asserted for at least 200 microseconds.
> > > +	 */
> > > +	ret = max9271_clear_gpios(max9271, MAX9271_GPIO1OUT);
> > > +	if (ret)
> > > +		return ret;
> > > +	usleep_range(200, 500);
> > > +
> > > +	/*
> > > +	 * Release ov10635 from reset and initialize it. The image sensor
> > > +	 * requires at least 2048 XVCLK cycles (85 micro-seconds at 24MHz)
> > > +	 * before being available. Stay safe and wait up to 500 micro-seconds.
> >
> > More sensor specific ... what needs to be abstracted here?
> 
> Yes indeed. As pointed out in the cover letter the handling of the
> sensor's reset line should go through the gpiod API, and will probably
> require installing a gpiochip in the max9271 driver.

What would be the point though ? I really think this isn't worth it.
This could would btw need to move to the sensor driver, we don't want to
encode the reset duration in the max9271 driver, and we don't want to
specify it in DT either.

> > > +	 */
> > > +	ret = max9271_set_gpios(max9271, MAX9271_GPIO1OUT);
> > > +	if (ret)
> > > +		return ret;
> > > +	usleep_range(100, 500);
> > > +
> > > +	/*
> > > +	 * Call the sensor post_register operation to complete its
> > > +	 * initialization.
> > > +	 */
> > > +	ret = v4l2_subdev_call(max9271->sensor, core, post_register);
> > > +	if (ret) {
> > > +		dev_err(max9271->dev, "Failed to initialize sensor %u\n", ret);
> > > +		goto error_remove_link;
> > > +	}
> > > +
> > > +	return 0;
> > > +
> > > +error_remove_link:
> > > +	media_entity_remove_links(&max9271->sd.entity);
> > > +	max9271->sensor = NULL;
> > > +
> > > +error_free_handler:
> > > +	v4l2_ctrl_handler_free(&max9271->ctrls);
> > > +	max9271->sd.ctrl_handler = NULL;
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static void max9271_notify_unbind(struct v4l2_async_notifier *notifier,
> > > +				  struct v4l2_subdev *subdev,
> > > +				  struct v4l2_async_subdev *asd)
> > > +{
> > > +	struct max9271_device *max9271 = notifier_to_max9271(notifier);
> > > +
> > > +	media_entity_remove_links(&max9271->sd.entity);
> > > +	max9271->sensor = NULL;
> > > +}
> > > +
> > > +static const struct v4l2_async_notifier_operations max9271_notifier_ops = {
> > > +	.bound = max9271_notify_bound,
> > > +	.unbind = max9271_notify_unbind,
> > > +};
> > > +
> > > +static int max9271_parse_dt(struct max9271_device *max9271)
> > > +{
> > > +	struct fwnode_handle *ep, *remote;
> > > +	struct v4l2_fwnode_endpoint vep = {
> > > +		.bus_type = V4L2_MBUS_PARALLEL,
> > > +	};
> > > +	int ret;
> > > +
> > > +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 1, 0, 0);
> > > +	if (!ep) {
> > > +		dev_err(max9271->dev, "Unable to get sensor endpoint: %pOF\n",
> > > +			max9271->dev->of_node);
> > > +		return -ENOENT;
> > > +	}
> > > +
> > > +	remote = fwnode_graph_get_remote_endpoint(ep);
> > > +	if (!remote) {
> > > +		dev_err(max9271->dev, "Unable to get remote endpoint: %pOF\n",
> > > +			max9271->dev->of_node);
> > > +		return -ENOENT;
> > > +	}
> > > +
> > > +	ret = v4l2_fwnode_endpoint_parse(ep, &vep);
> > > +	fwnode_handle_put(ep);
> > > +	if (ret) {
> > > +		fwnode_handle_put(remote);
> > > +		dev_err(max9271->dev, "Unable to parse endpoint: %pOF\n",
> > > +			to_of_node(ep));
> > > +		return ret;
> > > +	}
> > > +
> > > +	v4l2_async_notifier_init(&max9271->notifier);
> > > +	max9271->asd = v4l2_async_notifier_add_fwnode_subdev(&max9271->notifier,
> > > +					      remote, struct v4l2_async_subdev);
> > > +	fwnode_handle_put(remote);
> > > +	if (IS_ERR(max9271->asd))
> > > +		return PTR_ERR(max9271->asd);
> > > +
> > > +	max9271->notifier.ops = &max9271_notifier_ops;
> > > +	max9271->notifier.flags = V4L2_ASYNC_NOTIFIER_DEFER_POST_REGISTER;
> >
> > This looks new, I'll have to read up on it ...
> 
> It is. I am pushing to get feedback on this since a few months now.
> If you wish to help:
> https://patchwork.linuxtv.org/project/linux-media/list/?series=5847
> 
> > > +	ret = v4l2_async_subdev_notifier_register(&max9271->sd,
> > > +						  &max9271->notifier);
> > > +	if (ret < 0) {
> > > +		v4l2_async_notifier_cleanup(&max9271->notifier);
> > > +		return ret;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int max9271_init(struct max9271_device *max9271)
> > > +{
> > > +	int ret;
> > > +	u8 addr;
> > > +
> > > +	max9271_wake_up(max9271);
> >
> > This call has just set max9271->client->addr = MAX9271_DEFAULT_ADDR, so
> > the original has now been lost.
> 
> Doh! I overlooked the fact max9271_wake_up() silently overwrites the
> i2c client address. For the reasons explained in the cover letter I've
> been able to test this patch with a single camera only, so the issue
> went unnoticed here. Thanks for spotting!
> 
> > > +
> > > +	/* Re-program the chip address. */
> > > +	addr = max9271->client->addr;
> > > +	max9271->client->addr = MAX9271_DEFAULT_ADDR;
> > > +	ret = max9271_set_address(max9271, addr);
> >
> > So this is currently broken :S
> >
> > > +	if (ret < 0)
> > > +		return ret;
> > > +	max9271->client->addr = addr;
> > > +
> > > +	/* Serial link disabled during conf as it needs a valid pixel clock. */
> > > +	ret = max9271_set_serial_link(max9271, false);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	/*
> > > +	 *  Ensure that we have a good link configuration before attempting to
> > > +	 *  identify the device.
> > > +	 */
> > > +	ret = max9271_configure_i2c(max9271, MAX9271_I2CSLVSH_469NS_234NS |
> > > +					     MAX9271_I2CSLVTO_1024US |
> > > +					     MAX9271_I2CMSTBT_105KBPS);
> >
> > Are these parameters tied to the max9286 ?
> 
> More to the I2c bus configuration and should probably be made
> configurable through the canonical i2c DTS properties.
> 
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	/*
> > > +	 * Set reverse channel high threshold to increase noise immunity.
> > > +	 *
> > > +	 * This should be compensated by increasing the reverse channel
> > > +	 * amplitude on the remote deserializer side.
> > > +	 */
> > > +	return max9271_set_high_threshold(max9271, true);
> > > +}
> > > +
> > > +static int max9271_probe(struct i2c_client *client)
> > > +{
> > > +	struct max9271_device *max9271;
> > > +	struct fwnode_handle *ep;
> > > +	int ret;
> > > +
> > > +	max9271 = devm_kzalloc(&client->dev, sizeof(*max9271), GFP_KERNEL);
> > > +	if (!max9271)
> > > +		return -ENOMEM;
> > > +	max9271->dev = &client->dev;
> > > +	max9271->client = client;
> > > +
> > > +	/* Initialize and register the subdevice. */
> > > +	v4l2_i2c_subdev_init(&max9271->sd, client, &max9271_subdev_ops);
> > > +	max9271->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > > +	max9271->pads[MAX9271_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
> > > +	max9271->pads[MAX9271_SINK_PAD].flags = MEDIA_PAD_FL_SINK;
> > > +	max9271->sd.entity.flags |= MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
> >
> > Is it a formatter - do we need a new/different entity type?
> 
> I admit I had an hard time choosing the correct entity type :)
> 
> > > +	ret = media_entity_pads_init(&max9271->sd.entity, 2, max9271->pads);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 0, 0, 0);
> > > +	if (!ep) {
> > > +		dev_err(max9271->dev, "Unable to get endpoint 0: %pOF\n",
> > > +			max9271->dev->of_node);
> > > +		ret = -ENODEV;
> > > +		goto error_media_entity;
> > > +	}
> > > +
> > > +	max9271->sd.fwnode = ep;
> > > +	ret = v4l2_async_register_subdev(&max9271->sd);
> > > +	if (ret)
> > > +		goto error_put_node;
> > > +
> > > +	ret = max9271_parse_dt(max9271);
> > > +	if (ret)
> > > +		goto error_unregister_subdev;
> > > +
> > > +	ret = max9271_init(max9271);
> > > +	if (ret)
> > > +		goto error_unregister_subdev;
> > > +
> > > +	return 0;
> > > +
> > > +error_unregister_subdev:
> > > +	v4l2_async_unregister_subdev(&max9271->sd);
> > > +error_put_node:
> > > +	fwnode_handle_put(max9271->sd.fwnode);
> > > +error_media_entity:
> > > +	media_entity_cleanup(&max9271->sd.entity);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int max9271_remove(struct i2c_client *client)
> > > +{
> > > +	struct max9271_device *max9271 = i2c_to_max9271(client);
> > > +
> > > +	v4l2_ctrl_handler_free(&max9271->ctrls);
> > > +	v4l2_async_notifier_cleanup(&max9271->notifier);
> > > +	v4l2_async_unregister_subdev(&max9271->sd);
> > > +	fwnode_handle_put(max9271->sd.fwnode);
> > > +	media_entity_cleanup(&max9271->sd.entity);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct of_device_id max9271_of_ids[] = {
> > > +	{ .compatible = "imi,max9271", },
> >
> > This should be a 'maxim' prefix now, not IMI.
> >
> > > +	{ }
> > > +};
> > > +MODULE_DEVICE_TABLE(of, max9271_of_ids);
> > > +
> > > +static struct i2c_driver max9271_i2c_driver = {
> > > +	.driver	= {
> > > +		.name	= "max9271",
> > > +		.of_match_table = max9271_of_ids,
> > > +	},
> > > +	.probe_new	= max9271_probe,
> > > +	.remove		= max9271_remove,
> > > +};
> > > +
> > > +module_i2c_driver(max9271_i2c_driver);
> > > +
> > > +MODULE_DESCRIPTION("MAX9271 GMSL serializer subdevice driver");
> > > +MODULE_AUTHOR("Jacopo Mondi");
> > > +MODULE_LICENSE("GPL");

-- 
Regards,

Laurent Pinchart

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

* Re: [RFC 4/5] media: i2c: max9286: Fetch PIXEL_RATE in s_stream
  2021-08-23  2:17   ` Laurent Pinchart
@ 2021-08-23  7:20     ` Jacopo Mondi
  2021-08-23  9:34       ` Laurent Pinchart
  0 siblings, 1 reply; 21+ messages in thread
From: Jacopo Mondi @ 2021-08-23  7:20 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Jacopo Mondi, Mauro Carvalho Chehab, Kieran Bingham,
	Niklas Söderlund, Hans Verkuil, Sakari Ailus,
	Manivannan Sadhasivam, Thomas NIZAN, linux-renesas-soc,
	linux-media

Hi Laurent,

On Mon, Aug 23, 2021 at 05:17:05AM +0300, Laurent Pinchart wrote:
> Hi Jacopo,
>
> Thank you for the patch.
>
> On Tue, Aug 17, 2021 at 09:27:02AM +0200, Jacopo Mondi wrote:
> > The max9286 driver needs to fetch the remote serializer PIXEL_RATE
> > control value in order to compute its own one, as the sum of the
>
> s/its own one/its own/ (or its own pixel rate)
>
> > values reported by the connected subdevices.
> >
> > Currently the control is verified to be present at notifier's bound
> > time, which requires the serializer driver to register the control at
> > probe time. As the serializer driver might need to register the control
> > later, by adding the control handler of its connected sensor, post-pone
> > the max9286 check for the control availability at start stream time.
> >
> > Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> > ---
> >  drivers/media/i2c/max9286.c | 6 +++++-
> >  1 file changed, 5 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> > index 1b92d18a1f94..98fc90339a9e 100644
> > --- a/drivers/media/i2c/max9286.c
> > +++ b/drivers/media/i2c/max9286.c
> > @@ -595,7 +595,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> >  	max9286_check_config_link(priv, priv->source_mask);
> >  	max9286_configure_i2c(priv, false);
> >
> > -	return max9286_set_pixelrate(priv);
> > +	return 0;
> >  }
> >
> >  static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
> > @@ -674,6 +674,10 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
> >  	int ret;
> >
> >  	if (enable) {
> > +		ret = max9286_set_pixelrate(priv);
> > +		if (ret)
> > +			return ret;
>
> That's very likely not going to work. The CSI-2 receiver connected to
> the max9286 will need to know the pixel rate as part of its
> initialization sequence, before calling .s_stream(1) on the max9286.
>

How so ? The R-Car CSI-2 receiver feteches the pixel rate at s_stream
time, I thought it was customary to do so. What is it needed for before
streamon time ?

> > +
> >  		/*
> >  		 * The frame sync between cameras is transmitted across the
> >  		 * reverse channel as GPIO. We must open all channels while
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [RFC 2/5] media: i2c: Add MAX9271 I2C driver
  2021-08-23  2:22       ` Laurent Pinchart
@ 2021-08-23  7:21         ` Jacopo Mondi
  0 siblings, 0 replies; 21+ messages in thread
From: Jacopo Mondi @ 2021-08-23  7:21 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Kieran Bingham, Jacopo Mondi, Mauro Carvalho Chehab,
	Kieran Bingham, Niklas Söderlund, Hans Verkuil,
	Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	linux-renesas-soc, linux-media

Hi Laurent,

On Mon, Aug 23, 2021 at 05:22:45AM +0300, Laurent Pinchart wrote:
> On Wed, Aug 18, 2021 at 10:27:00AM +0200, Jacopo Mondi wrote:
> > Hi Kieran, thanks for review!
> >
> > On Tue, Aug 17, 2021 at 04:49:17PM +0100, Kieran Bingham wrote:
> > > Hi Jacopo,
> > >
> > > On 17/08/2021 08:27, Jacopo Mondi wrote:
> > > > The MAX9271 is a GMSL serializer that serializes a video stream
> > > > received from an image sensor through the parallel video bus.
> > >
> > >
> > > https://datasheets.maximintegrated.com/en/ds/MAX9271.pdf calls it a
> > > "16-Bit GMSL Serializer with Coas or STP Cable Drive"
> > >
> >
> > Nice we have a public datasheet now!
> > Nice, well, it might mean GMSL is just old ?
> >
> > >
> > > > The serializer it's usually found embedded with an image sensor and
> > >
> > > s/it's/is/
> > >
> > > > other ancillary chips in camera modules like RDACM20 and RDACM21.
> > > >
> > > > Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> > > > ---
> > > >  MAINTAINERS                 |   9 +
> > > >  drivers/media/i2c/Kconfig   |  12 +
> > > >  drivers/media/i2c/Makefile  |   1 +
> > > >  drivers/media/i2c/max9271.c | 756 ++++++++++++++++++++++++++++++++++++
> > > >  4 files changed, 778 insertions(+)
> > > >  create mode 100644 drivers/media/i2c/max9271.c
> > > >
> > > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > > index 7ad89cac19b7..2dab25a08c9c 100644
> > > > --- a/MAINTAINERS
> > > > +++ b/MAINTAINERS
> > > > @@ -11244,6 +11244,15 @@ F:	Documentation/hwmon/max6697.rst
> > > >  F:	drivers/hwmon/max6697.c
> > > >  F:	include/linux/platform_data/max6697.h
> > > >
> > > > +MAX9271 GMSL SERIALIZER 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:	drivers/media/i2c/max9271.c
> > > > +
> > > >  MAX9286 QUAD GMSL DESERIALIZER DRIVER
> > > >  M:	Jacopo Mondi <jacopo+renesas@jmondi.org>
> > > >  M:	Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> > > > diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> > > > index 08feb3e8c1bf..b793d1f322d9 100644
> > > > --- a/drivers/media/i2c/Kconfig
> > > > +++ b/drivers/media/i2c/Kconfig
> > > > @@ -1300,6 +1300,18 @@ source "drivers/media/i2c/m5mols/Kconfig"
> > > >  config VIDEO_MAX9271_LIB
> > > >  	tristate
> > > >
> > > > +config VIDEO_MAX9271
> > > > +	tristate "MAX9271 GMSL serializer support"
> > > > +	depends on I2C
> > > > +	select V4L2_FWNODE
> > > > +	select VIDEO_V4L2_SUBDEV_API
> > > > +	select MEDIA_CONTROLLER
> > > > +	help
> > > > +	  This driver supports the Maxim MAX9271 GMSL serializer.
> > > > +
> > > > +	  To compile this driver as a module, choose M here: the
> > > > +	  module will be called max9271.
> > > > +
> > > >  config VIDEO_RDACM20
> > > >  	tristate "IMI RDACM20 camera support"
> > > >  	depends on I2C
> > > > diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> > > > index 4d879373bd48..37bb51065574 100644
> > > > --- a/drivers/media/i2c/Makefile
> > > > +++ b/drivers/media/i2c/Makefile
> > > > @@ -130,6 +130,7 @@ obj-$(CONFIG_VIDEO_IMX355)	+= imx355.o
> > > >  obj-$(CONFIG_VIDEO_IMX412)	+= imx412.o
> > > >  obj-$(CONFIG_VIDEO_MAX9286)	+= max9286.o
> > > >  obj-$(CONFIG_VIDEO_MAX9271_LIB)	+= max9271-lib.o
> > > > +obj-$(CONFIG_VIDEO_MAX9271)	+= max9271.o
> > > >  obj-$(CONFIG_VIDEO_RDACM20)	+= rdacm20.o
> > > >  obj-$(CONFIG_VIDEO_RDACM21)	+= rdacm21.o
> > > >  obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
> > > > diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c
> > > > new file mode 100644
> > > > index 000000000000..64987cba3d3e
> > > > --- /dev/null
> > > > +++ b/drivers/media/i2c/max9271.c
> > > > @@ -0,0 +1,756 @@
> > > > +// SPDX-License-Identifier: GPL-2.0+
> > > > +/*
> > > > + * Copyright (C) 2017-2021 Jacopo Mondi
> > > > + * Copyright (C) 2017-2020 Kieran Bingham
> > > > + * Copyright (C) 2017-2019 Laurent Pinchart
> > > > + * Copyright (C) 2017-2019 Niklas Söderlund
> > > > + * Copyright (C) 2016 Renesas Electronics Corporation
> > > > + * Copyright (C) 2015 Cogent Embedded, Inc.
> > > > + */
> > > > +
> > > > +#include <linux/delay.h>
> > > > +#include <linux/fwnode.h>
> > > > +#include <linux/init.h>
> > > > +#include <linux/i2c.h>
> > > > +#include <linux/module.h>
> > > > +#include <linux/property.h>
> > > > +
> > > > +#include <media/v4l2-async.h>
> > > > +#include <media/v4l2-ctrls.h>
> > > > +#include <media/v4l2-fwnode.h>
> > > > +#include <media/v4l2-subdev.h>
> > > > +
> > > > +#define MAX9271_DEFAULT_ADDR	0x40
> > > > +
> > > > +/* Register 0x02 */
> > > > +#define MAX9271_SPREAD_SPECT_0		(0 << 5)
> > > > +#define MAX9271_SPREAD_SPECT_05		(1 << 5)
> > > > +#define MAX9271_SPREAD_SPECT_15		(2 << 5)
> > > > +#define MAX9271_SPREAD_SPECT_1		(5 << 5)
> > > > +#define MAX9271_SPREAD_SPECT_2		(3 << 5)
> > > > +#define MAX9271_SPREAD_SPECT_3		(6 << 5)
> > > > +#define MAX9271_SPREAD_SPECT_4		(7 << 5)
> > > > +#define MAX9271_R02_RES			BIT(4)
> > > > +#define MAX9271_PCLK_AUTODETECT		(3 << 2)
> > > > +#define MAX9271_SERIAL_AUTODETECT	(0x03)
> > > > +/* Register 0x04 */
> > > > +#define MAX9271_SEREN			BIT(7)
> > > > +#define MAX9271_CLINKEN			BIT(6)
> > > > +#define MAX9271_PRBSEN			BIT(5)
> > > > +#define MAX9271_SLEEP			BIT(4)
> > > > +#define MAX9271_INTTYPE_I2C		(0 << 2)
> > > > +#define MAX9271_INTTYPE_UART		(1 << 2)
> > > > +#define MAX9271_INTTYPE_NONE		(2 << 2)
> > > > +#define MAX9271_REVCCEN			BIT(1)
> > > > +#define MAX9271_FWDCCEN			BIT(0)
> > > > +/* Register 0x07 */
> > > > +#define MAX9271_DBL			BIT(7)
> > > > +#define MAX9271_DRS			BIT(6)
> > > > +#define MAX9271_BWS			BIT(5)
> > > > +#define MAX9271_ES			BIT(4)
> > > > +#define MAX9271_HVEN			BIT(2)
> > > > +#define MAX9271_EDC_1BIT_PARITY		(0 << 0)
> > > > +#define MAX9271_EDC_6BIT_CRC		(1 << 0)
> > > > +#define MAX9271_EDC_6BIT_HAMMING	(2 << 0)
> > > > +/* Register 0x08 */
> > > > +#define MAX9271_INVVS			BIT(7)
> > > > +#define MAX9271_INVHS			BIT(6)
> > > > +#define MAX9271_REV_LOGAIN		BIT(3)
> > > > +#define MAX9271_REV_HIVTH		BIT(0)
> > > > +/* Register 0x09 */
> > > > +#define MAX9271_ID			0x09
> > > > +/* Register 0x0d */
> > > > +#define MAX9271_I2CLOCACK		BIT(7)
> > > > +#define MAX9271_I2CSLVSH_1046NS_469NS	(3 << 5)
> > > > +#define MAX9271_I2CSLVSH_938NS_352NS	(2 << 5)
> > > > +#define MAX9271_I2CSLVSH_469NS_234NS	(1 << 5)
> > > > +#define MAX9271_I2CSLVSH_352NS_117NS	(0 << 5)
> > > > +#define MAX9271_I2CMSTBT_837KBPS	(7 << 2)
> > > > +#define MAX9271_I2CMSTBT_533KBPS	(6 << 2)
> > > > +#define MAX9271_I2CMSTBT_339KBPS	(5 << 2)
> > > > +#define MAX9271_I2CMSTBT_173KBPS	(4 << 2)
> > > > +#define MAX9271_I2CMSTBT_105KBPS	(3 << 2)
> > > > +#define MAX9271_I2CMSTBT_84KBPS		(2 << 2)
> > > > +#define MAX9271_I2CMSTBT_28KBPS		(1 << 2)
> > > > +#define MAX9271_I2CMSTBT_8KBPS		(0 << 2)
> > > > +#define MAX9271_I2CSLVTO_NONE		(3 << 0)
> > > > +#define MAX9271_I2CSLVTO_1024US		(2 << 0)
> > > > +#define MAX9271_I2CSLVTO_256US		(1 << 0)
> > > > +#define MAX9271_I2CSLVTO_64US		(0 << 0)
> > > > +/* Register 0x0f */
> > > > +#define MAX9271_GPIO5OUT		BIT(5)
> > > > +#define MAX9271_GPIO4OUT		BIT(4)
> > > > +#define MAX9271_GPIO3OUT		BIT(3)
> > > > +#define MAX9271_GPIO2OUT		BIT(2)
> > > > +#define MAX9271_GPIO1OUT		BIT(1)
> > > > +#define MAX9271_GPO			BIT(0)
> > > > +/* Register 0x15 */
> > > > +#define MAX9271_PCLKDET			BIT(0)
> > > > +
> > > > +struct max9271_device {
> > > > +	struct device			*dev;
> > > > +	struct i2c_client		*client;
> > > > +	struct v4l2_subdev		sd;
> > > > +#define MAX9271_SOURCE_PAD	0
> > > > +#define MAX9271_SINK_PAD	1
> > > > +	struct media_pad		pads[2];
> > > > +	struct v4l2_async_notifier	notifier;
> > > > +	struct v4l2_async_subdev	*asd;
> > > > +	struct v4l2_ctrl_handler	ctrls;
> > > > +	struct v4l2_subdev		*sensor;
> > > > +};
> > > > +
> > > > +static inline struct max9271_device *sd_to_max9271(struct v4l2_subdev *sd)
> > > > +{
> > > > +	return container_of(sd, struct max9271_device, sd);
> > > > +}
> > > > +
> > > > +static inline struct max9271_device *i2c_to_max9271(struct i2c_client *client)
> > > > +{
> > > > +	return sd_to_max9271(i2c_get_clientdata(client));
> > > > +}
> > > > +
> > > > +static inline struct max9271_device *notifier_to_max9271(
> > > > +						struct v4l2_async_notifier *nf)
> > > > +{
> > > > +	return container_of(nf, struct max9271_device, notifier);
> > > > +}
> > > > +
> > > > +/* --- MAX9271 hardware operations --- */
> > > > +
> > > > +static int max9271_read(struct max9271_device *dev, u8 reg)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
> > > > +
> > > > +	ret = i2c_smbus_read_byte_data(dev->client, reg);
> > > > +	if (ret < 0)
> > > > +		dev_dbg(&dev->client->dev,
> > > > +			"%s: register 0x%02x read failed (%d)\n",
> > > > +			__func__, reg, ret);
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +static int max9271_write(struct max9271_device *dev, u8 reg, u8 val)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
> > > > +
> > > > +	ret = i2c_smbus_write_byte_data(dev->client, reg, val);
> > > > +	if (ret < 0)
> > > > +		dev_err(&dev->client->dev,
> > > > +			"%s: register 0x%02x write failed (%d)\n",
> > > > +			__func__, reg, ret);
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +/*
> > > > + * max9271_pclk_detect() - Detect valid pixel clock from image sensor
> > > > + *
> > > > + * Wait up to 10ms for a valid pixel clock.
> > > > + *
> > > > + * Returns 0 for success, < 0 for pixel clock not properly detected
> > > > + */
> > > > +static int max9271_pclk_detect(struct max9271_device *dev)
> > > > +{
> > > > +	unsigned int i;
> > > > +	int ret;
> > > > +
> > > > +	for (i = 0; i < 100; i++) {
> > > > +		ret = max9271_read(dev, 0x15);
> > > > +		if (ret < 0)
> > > > +			return ret;
> > > > +
> > > > +		if (ret & MAX9271_PCLKDET)
> > > > +			return 0;
> > > > +
> > > > +		usleep_range(50, 100);
> > > > +	}
> > > > +
> > > > +	dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
> > > > +
> > > > +	return -EIO;
> > > > +}
> > > > +
> > > > +static void max9271_wake_up(struct max9271_device *dev)
> > > > +{
> > > > +	/*
> > > > +	 * Use the chip default address as this function has to be called
> > > > +	 * before any other one.
> > > > +	 */
> > > > +	dev->client->addr = MAX9271_DEFAULT_ADDR;
> > > > +	i2c_smbus_read_byte(dev->client);
> > > > +	usleep_range(5000, 8000);
> > > > +}
> > > > +
> > > > +static int max9271_set_serial_link(struct max9271_device *dev, bool enable)
> > > > +{
> > > > +	int ret;
> > > > +	u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN;
> > > > +
> > > > +	if (enable) {
> > > > +		ret = max9271_pclk_detect(dev);
> > > > +		if (ret)
> > > > +			return ret;
> > > > +
> > > > +		val |= MAX9271_SEREN;
> > > > +	} else {
> > > > +		val |= MAX9271_CLINKEN;
> > > > +	}
> > > > +
> > > > +	/*
> > > > +	 * 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.
> > > > +	 */
> > > > +	ret = max9271_write(dev, 0x04, val);
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +
> > > > +	usleep_range(5000, 8000);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	ret = max9271_write(dev, 0x0d, i2c_config);
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +
> > > > +	/* The delay required after an I2C bus configuration change is not
> > >
> > > /*
> > >  * The delay ... ?
> > >
> >
> > Ah, ups
> >
> > >
> > >
> > > > +	 * characterized in the serializer manual. Sleep up to 5msec to
> > > > +	 * stay safe.
> > > > +	 */
> > > > +	usleep_range(3500, 5000);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	ret = max9271_read(dev, 0x08);
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +
> > > > +	/*
> > > > +	 * Enable or disable reverse channel high threshold to increase
> > > > +	 * immunity to power supply noise.
> > > > +	 */
> > > > +	ret = max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +
> > > > +	usleep_range(2000, 2500);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int max9271_configure_gmsl_link(struct max9271_device *dev)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	/*
> > > > +	 * 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
> > > > +	 *
> > > > +	 * TODO: Make the GMSL link configuration parametric.
> > > > +	 */
> > > > +	ret = max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
> > > > +			    MAX9271_EDC_1BIT_PARITY);
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +
> > > > +	usleep_range(5000, 8000);
> > > > +
> > > > +	/*
> > > > +	 * Adjust spread spectrum to +4% and auto-detect pixel clock
> > > > +	 * and serial link rate.
> > > > +	 */
> > > > +	ret = max9271_write(dev, 0x02,
> > > > +			    MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
> > > > +			    MAX9271_PCLK_AUTODETECT |
> > > > +			    MAX9271_SERIAL_AUTODETECT);
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +
> > > > +	usleep_range(5000, 8000);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	ret = max9271_read(dev, 0x0f);
> > >
> > > What is 0x0f?
> > >
> > > Ah - the registers are unnamed...
> > >
> > > Seems a shame..
> >
> > It is a bit. If the overall approach is validated, I'll go and
> > beautify the driver.
> >
> > >
> > >
> > > > +	if (ret < 0)
> > > > +		return 0;
> > > > +
> > > > +	ret |= gpio_mask;
> > > > +	ret = max9271_write(dev, 0x0f, ret);
> > > > +	if (ret < 0) {
> > > > +		dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret);
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	usleep_range(3500, 5000);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	ret = max9271_read(dev, 0x0f);
> > > > +	if (ret < 0)
> > > > +		return 0;
> > > > +
> > > > +	ret &= ~gpio_mask;
> > > > +	ret = max9271_write(dev, 0x0f, ret);
> > > > +	if (ret < 0) {
> > > > +		dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret);
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	usleep_range(3500, 5000);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	ret = max9271_read(dev, 0x0e);
> > > > +	if (ret < 0)
> > > > +		return 0;
> > > > +
> > > > +	/* BIT(0) reserved: GPO is always enabled. */
> > > > +	ret |= (gpio_mask & ~BIT(0));
> > > > +	ret = max9271_write(dev, 0x0e, ret);
> > > > +	if (ret < 0) {
> > > > +		dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret);
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	usleep_range(3500, 5000);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int max9271_verify_id(struct max9271_device *dev)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	ret = max9271_read(dev, 0x1e);
> > > > +	if (ret < 0) {
> > > > +		dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
> > > > +			ret);
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	if (ret != MAX9271_ID) {
> > > > +		dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
> > > > +			ret);
> > > > +		return -ENXIO;
> > > > +	}
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int max9271_set_address(struct max9271_device *dev, u8 addr)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	ret = max9271_write(dev, 0x00, addr << 1);
> > > > +	if (ret < 0) {
> > > > +		dev_err(&dev->client->dev,
> > > > +			"MAX9271 I2C address change failed (%d)\n", ret);
> > > > +		return ret;
> > > > +	}
> > > > +	usleep_range(3500, 5000);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +/* --- V4L2 Subdev Ops --- */
> > > > +
> > > > +static int max9271_s_stream(struct v4l2_subdev *sd, int enable)
> > > > +{
> > > > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > > > +
> > > > +	return max9271_set_serial_link(max9271, enable);
> > > > +}
> > > > +
> > > > +static int max9271_enum_mbus_code(struct v4l2_subdev *sd,
> > > > +				  struct v4l2_subdev_state *sd_state,
> > > > +				  struct v4l2_subdev_mbus_code_enum *code)
> > > > +{
> > > > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > > > +
> > > > +	return v4l2_subdev_call(max9271->sensor, pad, enum_mbus_code, NULL,
> > > > +				code);
> > > > +}
> > > > +
> > > > +static int max9271_get_fmt(struct v4l2_subdev *sd,
> > > > +			   struct v4l2_subdev_state *sd_state,
> > > > +			   struct v4l2_subdev_format *format)
> > > > +{
> > > > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > > > +
> > > > +	return v4l2_subdev_call(max9271->sensor, pad, get_fmt, NULL,
> > > > +				format);
> > > > +}
> > > > +
> > > > +static int max9271_set_fmt(struct v4l2_subdev *sd,
> > > > +			   struct v4l2_subdev_state *sd_state,
> > > > +			   struct v4l2_subdev_format *format)
> > > > +{
> > > > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > > > +
> > > > +	return v4l2_subdev_call(max9271->sensor, pad, set_fmt, NULL,
> > > > +				format);
> > > > +}
> > > > +
> > > > +static int max9271_post_register(struct v4l2_subdev *sd)
> > > > +{
> > > > +	struct max9271_device *max9271 = sd_to_max9271(sd);
> > > > +	int ret;
> > > > +
> > > > +	ret = max9271_verify_id(max9271);
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +
> > > > +	ret = max9271_enable_gpios(max9271, MAX9271_GPIO1OUT);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	ret = max9271_configure_gmsl_link(max9271);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static const struct v4l2_subdev_video_ops max9271_video_ops = {
> > > > +	.s_stream	= max9271_s_stream,
> > > > +};
> > > > +
> > > > +static const struct v4l2_subdev_pad_ops max9271_subdev_pad_ops = {
> > > > +	.enum_mbus_code = max9271_enum_mbus_code,
> > > > +	.get_fmt	= max9271_get_fmt,
> > > > +	.set_fmt	= max9271_set_fmt,
> > > > +};
> > > > +
> > > > +static const struct v4l2_subdev_core_ops max9271_core_ops = {
> > > > +	.post_register	= max9271_post_register,
> > > > +};
> > > > +
> > > > +static const struct v4l2_subdev_ops max9271_subdev_ops = {
> > > > +	.core		= &max9271_core_ops,
> > > > +	.video		= &max9271_video_ops,
> > > > +	.pad		= &max9271_subdev_pad_ops,
> > > > +};
> > > > +
> > > > +/* --- V4L2 Async Notifier --- */
> > > > +
> > > > +static int max9271_notify_bound(struct v4l2_async_notifier *notifier,
> > > > +				struct v4l2_subdev *subdev,
> > > > +				struct v4l2_async_subdev *asd)
> > > > +{
> > > > +	struct max9271_device *max9271 = notifier_to_max9271(notifier);
> > > > +	int ret, pad;
> > > > +
> > > > +	/*
> > > > +	 * Reserve more space than necessary for controls inherited by the
> > > > +	 * remote subdev.
> > > > +	 */
> > > > +	ret = v4l2_ctrl_handler_init(&max9271->ctrls, 16);
> > > > +	if (ret < 0) {
> > > > +		dev_err(max9271->dev,
> > > > +			"Unable to initialize control handler: %d\n", ret);
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	ret = v4l2_ctrl_add_handler(&max9271->ctrls, subdev->ctrl_handler,
> > > > +				    NULL, true);
> > > > +	if (ret < 0) {
> > > > +		dev_err(max9271->dev,
> > > > +			"Unable to add subdev control handler: %d\n", ret);
> > > > +		goto error_free_handler;
> > > > +	}
> > > > +	max9271->sd.ctrl_handler = &max9271->ctrls;
> > > > +
> > > > +	/* Create media link with the remote sensor source pad. */
> > > > +	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
> > > > +					  MEDIA_PAD_FL_SOURCE);
> > > > +	if (pad < 0) {
> > > > +		dev_err(max9271->dev,
> > > > +			"Failed to find source pad for %s\n", subdev->name);
> > > > +		ret = pad;
> > > > +		goto error_free_handler;
> > > > +	}
> > > > +
> > > > +	ret = media_create_pad_link(&subdev->entity, pad,
> > > > +				    &max9271->sd.entity, MAX9271_SINK_PAD,
> > > > +				    MEDIA_LNK_FL_ENABLED |
> > > > +				    MEDIA_LNK_FL_IMMUTABLE);
> > > > +	if (ret)
> > > > +		goto error_free_handler;
> > > > +
> > > > +	max9271->sensor = subdev;
> > > > +
> > > > +	/*
> > > > +	 * Hold OV10635 in reset during max9271 configuration. The reset signal
> > >
> > > Hold the sensor in reset ...
> > >
> > > But are all sensors guaranteed to be connected to the GPIO in the same way?
> > >
> > > Will this cause us issues? - does the GPIO need to be modelled somehow
> > > to determine what's connected to them?
> > >
> > > > +	 * has to be asserted for at least 200 microseconds.
> > > > +	 */
> > > > +	ret = max9271_clear_gpios(max9271, MAX9271_GPIO1OUT);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +	usleep_range(200, 500);
> > > > +
> > > > +	/*
> > > > +	 * Release ov10635 from reset and initialize it. The image sensor
> > > > +	 * requires at least 2048 XVCLK cycles (85 micro-seconds at 24MHz)
> > > > +	 * before being available. Stay safe and wait up to 500 micro-seconds.
> > >
> > > More sensor specific ... what needs to be abstracted here?
> >
> > Yes indeed. As pointed out in the cover letter the handling of the
> > sensor's reset line should go through the gpiod API, and will probably
> > require installing a gpiochip in the max9271 driver.
>
> What would be the point though ? I really think this isn't worth it.

That I might agree

> This could would btw need to move to the sensor driver, we don't want to
> encode the reset duration in the max9271 driver, and we don't want to
> specify it in DT either.

Installing a gpio-controller on the max9271 and having the sensor
driver handling its reset is what I was suggesting.

Thanks
   j

>
> > > > +	 */
> > > > +	ret = max9271_set_gpios(max9271, MAX9271_GPIO1OUT);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +	usleep_range(100, 500);
> > > > +
> > > > +	/*
> > > > +	 * Call the sensor post_register operation to complete its
> > > > +	 * initialization.
> > > > +	 */
> > > > +	ret = v4l2_subdev_call(max9271->sensor, core, post_register);
> > > > +	if (ret) {
> > > > +		dev_err(max9271->dev, "Failed to initialize sensor %u\n", ret);
> > > > +		goto error_remove_link;
> > > > +	}
> > > > +
> > > > +	return 0;
> > > > +
> > > > +error_remove_link:
> > > > +	media_entity_remove_links(&max9271->sd.entity);
> > > > +	max9271->sensor = NULL;
> > > > +
> > > > +error_free_handler:
> > > > +	v4l2_ctrl_handler_free(&max9271->ctrls);
> > > > +	max9271->sd.ctrl_handler = NULL;
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +static void max9271_notify_unbind(struct v4l2_async_notifier *notifier,
> > > > +				  struct v4l2_subdev *subdev,
> > > > +				  struct v4l2_async_subdev *asd)
> > > > +{
> > > > +	struct max9271_device *max9271 = notifier_to_max9271(notifier);
> > > > +
> > > > +	media_entity_remove_links(&max9271->sd.entity);
> > > > +	max9271->sensor = NULL;
> > > > +}
> > > > +
> > > > +static const struct v4l2_async_notifier_operations max9271_notifier_ops = {
> > > > +	.bound = max9271_notify_bound,
> > > > +	.unbind = max9271_notify_unbind,
> > > > +};
> > > > +
> > > > +static int max9271_parse_dt(struct max9271_device *max9271)
> > > > +{
> > > > +	struct fwnode_handle *ep, *remote;
> > > > +	struct v4l2_fwnode_endpoint vep = {
> > > > +		.bus_type = V4L2_MBUS_PARALLEL,
> > > > +	};
> > > > +	int ret;
> > > > +
> > > > +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 1, 0, 0);
> > > > +	if (!ep) {
> > > > +		dev_err(max9271->dev, "Unable to get sensor endpoint: %pOF\n",
> > > > +			max9271->dev->of_node);
> > > > +		return -ENOENT;
> > > > +	}
> > > > +
> > > > +	remote = fwnode_graph_get_remote_endpoint(ep);
> > > > +	if (!remote) {
> > > > +		dev_err(max9271->dev, "Unable to get remote endpoint: %pOF\n",
> > > > +			max9271->dev->of_node);
> > > > +		return -ENOENT;
> > > > +	}
> > > > +
> > > > +	ret = v4l2_fwnode_endpoint_parse(ep, &vep);
> > > > +	fwnode_handle_put(ep);
> > > > +	if (ret) {
> > > > +		fwnode_handle_put(remote);
> > > > +		dev_err(max9271->dev, "Unable to parse endpoint: %pOF\n",
> > > > +			to_of_node(ep));
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	v4l2_async_notifier_init(&max9271->notifier);
> > > > +	max9271->asd = v4l2_async_notifier_add_fwnode_subdev(&max9271->notifier,
> > > > +					      remote, struct v4l2_async_subdev);
> > > > +	fwnode_handle_put(remote);
> > > > +	if (IS_ERR(max9271->asd))
> > > > +		return PTR_ERR(max9271->asd);
> > > > +
> > > > +	max9271->notifier.ops = &max9271_notifier_ops;
> > > > +	max9271->notifier.flags = V4L2_ASYNC_NOTIFIER_DEFER_POST_REGISTER;
> > >
> > > This looks new, I'll have to read up on it ...
> >
> > It is. I am pushing to get feedback on this since a few months now.
> > If you wish to help:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=5847
> >
> > > > +	ret = v4l2_async_subdev_notifier_register(&max9271->sd,
> > > > +						  &max9271->notifier);
> > > > +	if (ret < 0) {
> > > > +		v4l2_async_notifier_cleanup(&max9271->notifier);
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int max9271_init(struct max9271_device *max9271)
> > > > +{
> > > > +	int ret;
> > > > +	u8 addr;
> > > > +
> > > > +	max9271_wake_up(max9271);
> > >
> > > This call has just set max9271->client->addr = MAX9271_DEFAULT_ADDR, so
> > > the original has now been lost.
> >
> > Doh! I overlooked the fact max9271_wake_up() silently overwrites the
> > i2c client address. For the reasons explained in the cover letter I've
> > been able to test this patch with a single camera only, so the issue
> > went unnoticed here. Thanks for spotting!
> >
> > > > +
> > > > +	/* Re-program the chip address. */
> > > > +	addr = max9271->client->addr;
> > > > +	max9271->client->addr = MAX9271_DEFAULT_ADDR;
> > > > +	ret = max9271_set_address(max9271, addr);
> > >
> > > So this is currently broken :S
> > >
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +	max9271->client->addr = addr;
> > > > +
> > > > +	/* Serial link disabled during conf as it needs a valid pixel clock. */
> > > > +	ret = max9271_set_serial_link(max9271, false);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	/*
> > > > +	 *  Ensure that we have a good link configuration before attempting to
> > > > +	 *  identify the device.
> > > > +	 */
> > > > +	ret = max9271_configure_i2c(max9271, MAX9271_I2CSLVSH_469NS_234NS |
> > > > +					     MAX9271_I2CSLVTO_1024US |
> > > > +					     MAX9271_I2CMSTBT_105KBPS);
> > >
> > > Are these parameters tied to the max9286 ?
> >
> > More to the I2c bus configuration and should probably be made
> > configurable through the canonical i2c DTS properties.
> >
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	/*
> > > > +	 * Set reverse channel high threshold to increase noise immunity.
> > > > +	 *
> > > > +	 * This should be compensated by increasing the reverse channel
> > > > +	 * amplitude on the remote deserializer side.
> > > > +	 */
> > > > +	return max9271_set_high_threshold(max9271, true);
> > > > +}
> > > > +
> > > > +static int max9271_probe(struct i2c_client *client)
> > > > +{
> > > > +	struct max9271_device *max9271;
> > > > +	struct fwnode_handle *ep;
> > > > +	int ret;
> > > > +
> > > > +	max9271 = devm_kzalloc(&client->dev, sizeof(*max9271), GFP_KERNEL);
> > > > +	if (!max9271)
> > > > +		return -ENOMEM;
> > > > +	max9271->dev = &client->dev;
> > > > +	max9271->client = client;
> > > > +
> > > > +	/* Initialize and register the subdevice. */
> > > > +	v4l2_i2c_subdev_init(&max9271->sd, client, &max9271_subdev_ops);
> > > > +	max9271->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > > > +	max9271->pads[MAX9271_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
> > > > +	max9271->pads[MAX9271_SINK_PAD].flags = MEDIA_PAD_FL_SINK;
> > > > +	max9271->sd.entity.flags |= MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
> > >
> > > Is it a formatter - do we need a new/different entity type?
> >
> > I admit I had an hard time choosing the correct entity type :)
> >
> > > > +	ret = media_entity_pads_init(&max9271->sd.entity, 2, max9271->pads);
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +
> > > > +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(max9271->dev), 0, 0, 0);
> > > > +	if (!ep) {
> > > > +		dev_err(max9271->dev, "Unable to get endpoint 0: %pOF\n",
> > > > +			max9271->dev->of_node);
> > > > +		ret = -ENODEV;
> > > > +		goto error_media_entity;
> > > > +	}
> > > > +
> > > > +	max9271->sd.fwnode = ep;
> > > > +	ret = v4l2_async_register_subdev(&max9271->sd);
> > > > +	if (ret)
> > > > +		goto error_put_node;
> > > > +
> > > > +	ret = max9271_parse_dt(max9271);
> > > > +	if (ret)
> > > > +		goto error_unregister_subdev;
> > > > +
> > > > +	ret = max9271_init(max9271);
> > > > +	if (ret)
> > > > +		goto error_unregister_subdev;
> > > > +
> > > > +	return 0;
> > > > +
> > > > +error_unregister_subdev:
> > > > +	v4l2_async_unregister_subdev(&max9271->sd);
> > > > +error_put_node:
> > > > +	fwnode_handle_put(max9271->sd.fwnode);
> > > > +error_media_entity:
> > > > +	media_entity_cleanup(&max9271->sd.entity);
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +static int max9271_remove(struct i2c_client *client)
> > > > +{
> > > > +	struct max9271_device *max9271 = i2c_to_max9271(client);
> > > > +
> > > > +	v4l2_ctrl_handler_free(&max9271->ctrls);
> > > > +	v4l2_async_notifier_cleanup(&max9271->notifier);
> > > > +	v4l2_async_unregister_subdev(&max9271->sd);
> > > > +	fwnode_handle_put(max9271->sd.fwnode);
> > > > +	media_entity_cleanup(&max9271->sd.entity);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static const struct of_device_id max9271_of_ids[] = {
> > > > +	{ .compatible = "imi,max9271", },
> > >
> > > This should be a 'maxim' prefix now, not IMI.
> > >
> > > > +	{ }
> > > > +};
> > > > +MODULE_DEVICE_TABLE(of, max9271_of_ids);
> > > > +
> > > > +static struct i2c_driver max9271_i2c_driver = {
> > > > +	.driver	= {
> > > > +		.name	= "max9271",
> > > > +		.of_match_table = max9271_of_ids,
> > > > +	},
> > > > +	.probe_new	= max9271_probe,
> > > > +	.remove		= max9271_remove,
> > > > +};
> > > > +
> > > > +module_i2c_driver(max9271_i2c_driver);
> > > > +
> > > > +MODULE_DESCRIPTION("MAX9271 GMSL serializer subdevice driver");
> > > > +MODULE_AUTHOR("Jacopo Mondi");
> > > > +MODULE_LICENSE("GPL");
>
> --
> Regards,
>
> Laurent Pinchart

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

* Re: [RFC 4/5] media: i2c: max9286: Fetch PIXEL_RATE in s_stream
  2021-08-23  7:20     ` Jacopo Mondi
@ 2021-08-23  9:34       ` Laurent Pinchart
  0 siblings, 0 replies; 21+ messages in thread
From: Laurent Pinchart @ 2021-08-23  9:34 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Jacopo Mondi, Mauro Carvalho Chehab, Kieran Bingham,
	Niklas Söderlund, Hans Verkuil, Sakari Ailus,
	Manivannan Sadhasivam, Thomas NIZAN, linux-renesas-soc,
	linux-media

Hi Jacopo,

On Mon, Aug 23, 2021 at 09:20:08AM +0200, Jacopo Mondi wrote:
> On Mon, Aug 23, 2021 at 05:17:05AM +0300, Laurent Pinchart wrote:
> > On Tue, Aug 17, 2021 at 09:27:02AM +0200, Jacopo Mondi wrote:
> > > The max9286 driver needs to fetch the remote serializer PIXEL_RATE
> > > control value in order to compute its own one, as the sum of the
> >
> > s/its own one/its own/ (or its own pixel rate)
> >
> > > values reported by the connected subdevices.
> > >
> > > Currently the control is verified to be present at notifier's bound
> > > time, which requires the serializer driver to register the control at
> > > probe time. As the serializer driver might need to register the control
> > > later, by adding the control handler of its connected sensor, post-pone
> > > the max9286 check for the control availability at start stream time.
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> > > ---
> > >  drivers/media/i2c/max9286.c | 6 +++++-
> > >  1 file changed, 5 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> > > index 1b92d18a1f94..98fc90339a9e 100644
> > > --- a/drivers/media/i2c/max9286.c
> > > +++ b/drivers/media/i2c/max9286.c
> > > @@ -595,7 +595,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> > >  	max9286_check_config_link(priv, priv->source_mask);
> > >  	max9286_configure_i2c(priv, false);
> > >
> > > -	return max9286_set_pixelrate(priv);
> > > +	return 0;
> > >  }
> > >
> > >  static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
> > > @@ -674,6 +674,10 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
> > >  	int ret;
> > >
> > >  	if (enable) {
> > > +		ret = max9286_set_pixelrate(priv);
> > > +		if (ret)
> > > +			return ret;
> >
> > That's very likely not going to work. The CSI-2 receiver connected to
> > the max9286 will need to know the pixel rate as part of its
> > initialization sequence, before calling .s_stream(1) on the max9286.
> 
> How so ? The R-Car CSI-2 receiver feteches the pixel rate at s_stream
> time, I thought it was customary to do so. What is it needed for before
> streamon time ?

At stream on time is usually fine, but the CSI-2 receiver usually needs
to be configured before starting the source, not after.

> > > +
> > >  		/*
> > >  		 * The frame sync between cameras is transmitted across the
> > >  		 * reverse channel as GPIO. We must open all channels while

-- 
Regards,

Laurent Pinchart

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

* Re: [RFC 2/5] media: i2c: Add MAX9271 I2C driver
  2021-08-18  8:27     ` Jacopo Mondi
  2021-08-18 12:38       ` Kieran Bingham
  2021-08-23  2:22       ` Laurent Pinchart
@ 2021-08-23 12:05       ` Geert Uytterhoeven
  2 siblings, 0 replies; 21+ messages in thread
From: Geert Uytterhoeven @ 2021-08-23 12:05 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Kieran Bingham, Jacopo Mondi, Mauro Carvalho Chehab,
	Kieran Bingham, Laurent Pinchart, Niklas Söderlund,
	Hans Verkuil, Sakari Ailus, Manivannan Sadhasivam, Thomas NIZAN,
	Linux-Renesas, Linux Media Mailing List

On Wed, Aug 18, 2021 at 10:26 AM Jacopo Mondi <jacopo@jmondi.org> wrote:
> On Tue, Aug 17, 2021 at 04:49:17PM +0100, Kieran Bingham wrote:
> > On 17/08/2021 08:27, Jacopo Mondi wrote:
> > > The MAX9271 is a GMSL serializer that serializes a video stream
> > > received from an image sensor through the parallel video bus.
> >
> > https://datasheets.maximintegrated.com/en/ds/MAX9271.pdf calls it a
> > "16-Bit GMSL Serializer with Coas or STP Cable Drive"

Surely copy-and-paste would have prevented that typo of coax? ;-)

> Nice we have a public datasheet now!

Looks like I downloaded that one more than 4 years ago?

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* Re: [RFC 0/5] media: i2c: Add MAX9271 subdevice driver
  2021-08-23  2:06 ` Laurent Pinchart
@ 2021-09-13  7:59   ` Jacopo Mondi
  0 siblings, 0 replies; 21+ messages in thread
From: Jacopo Mondi @ 2021-09-13  7:59 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Jacopo Mondi, Mauro Carvalho Chehab, Kieran Bingham,
	Niklas Söderlund, Hans Verkuil, Sakari Ailus,
	Manivannan Sadhasivam, Thomas NIZAN, linux-renesas-soc,
	linux-media

Hi Laurent,

On Mon, Aug 23, 2021 at 05:06:51AM +0300, Laurent Pinchart wrote:
> Hi Jacopo,
>
> On Tue, Aug 17, 2021 at 09:26:58AM +0200, Jacopo Mondi wrote:
> > Hello,
> >    as noticed during the inclusion of the RDACM20/21 cameras, their driver make
> > use of a library driver that exports functions to control the MAX9271 GMSL
> > serializer embedded in the camera module.
> >
> > This series attempts to create an i2c subdevice driver for the MAX9271
> > serializer, to support the camera module operations using the v4l2 subdev
> > operations.
> >
> > The series is based on the currently in-review:
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=5847
> > https://patchwork.linuxtv.org/project/linux-media/list/?series=5949
> >
> > The series:
> > 1) Introduced an i2c subdev driver for the MAX9271 GMSL serializer
> > 2) Adapt the RDACM20 driver by removing the MAX9271 handling from there
> > 3) Modify the DTS layout to describe the MAX9271 chip and the camera module
> >    separately
> >
> > To be done:
> > - bindings
> > - handling of reset lines between max9271 and image sensor
> > - the camera module drivers could be made sensor drivers
> >
> > However I'm not fully convinced this really brings any benefit as the serializer
> > and the image sensor are actually packed together in the same camera module
> > and are tightly coupled.
>
> I'm not convinced either. More than that, I think it will make it
> impossible to handle more complex camera topologies.
>
> > The biggest issue I'm facing, and for which I would be happy to receive pointers
> > to is the following one.
> >
> > The new DTS layout now looks like
> >
> > 	max9286 {
> >
> > 		i2c-mux {
> > 			i2c@0 {
> > 				max9271 {
> > 				}
> >
> > 				rdacm20{
> > 				}
> > 			}
> > 		}
> > 	}
> >
> > If I do rely on the probe sequence implemented by the instantiation of the
> > i2c-mux child nodes:
> >
> > 	- max9286
> > 		-max9271
> > 		-sensor
> >
> > 		-max9271
> > 		-sensor
> >
> > 		...
> >
> > As per each i2c-mux subnode the max9271 and the connected sensor are probed once
> > after the other.
> >
> > This unfortunately doesn't play well with the requirements of GMSL bus, for
> > which the post_register operation is being introduced. With the current
> > RDACM20/21 drivers and post_register in place with two cameras connected to the
> > system, the desired initialization sequence looks like:
> >
> >             MAX9286                  RDACM20/21
> >
> >             probe()
> >                |
> >                ---------------------> |
> >                                       camera 1 probe() {
> >                                          enable_threshold()
> >                                       }
> >                |<--------------------|
> >             v4l2 async bound {
> > 		completed = no
> >                |
> >                ---------------------> |
> >                                       camera 2 probe() {
> >                                          enable_threshold()
> >                                       }
> >                |<--------------------|
> > 		completed = yes
> >
> >                 compensate_amplitude()
> >
> >                 call post_register()
> >                |-------------------->|
> >                                      camera 1 post_register()
> >                                          access camera registers()
> >                                     }
> >                |<-------------------
> >                |-------------------->|
> >                                      camera 2 post_register()
> >                                          access camera registers()
> >                                     }
> >                |<-------------------
> >             }
> >
> > Which guarantees that the bulk access to the camera registers happens after the
> > deserializer has compensated the channel amplitude.
> >
> > With the new model I do have a race between the sensor probe and the
> > post_register() of the serializer in case a single camera is connected.
> >
> > What happes is that as soon as the max9271 registers its async subdev the
> > max9286 notifier completes an call max9271->post_register(). But at that time
> > the sensor subdev has not probed yet, so there is no subdev on which to call
> > post_register in the max9271
> >
> > following:
> >
> >     MAX9286                  MAX9271			SENSOR
> >
> >     probe()
> >        |
> >        ---------------------> |
> > 			      probe() {
> > 				 enable_threshold()
> > 			      }
> >       }
> >        |<--------------------|
> >     v4l2 async bound {
> > 	completed = yes
> >  	subdev->post_register()
> >        |-------------------->|
> > 			     post_register()
> > 				gmsl_bus_config()
> > 				subdev->post_register(NULL)
> > 				segfault
> > 			    }
> > 							probe()
> >     }
> >
> > If I instead do not use post_register() between the max9271 and the sensor,
> > then the model works for a single camera only (as it is implemented in this
> > version)
> >
> >     MAX9286                  MAX9271			SENSOR
> >
> >     probe()
> >        |
> >        ---------------------> |
> > 			      probe() {
> > 				 enable_threshold()
> > 			      }
> >       }
> >        |<--------------------|
> >     v4l2 async bound {
> > 	completed = no
> >        |-------------------->|
> > 							probe() {
> > 							   i2c writes to
> > 							   the sensor without
> > 							   GMSL configuration
> > 							}
> >     }
> >
> > So, my question is: are there examples on how to have the max9271 driver
> > control the probe time the connected sensor without relying on the probe
> > sequence of the I2C-mux device nodes ? If I could do so, what I would like to
> > realize looks like
>
> How about making the sensor a child of the max9271 in DT ?

I think that would ideally be feasible and it would match the actual
HW, as max9271 is actually an i2c multiplexer.

But is something like

 	max9286 {
 		i2c-mux {
 			i2c@0 {
 				max9271@51 {
                                        i2c-mux {
                                                i2c@0 {
                                                        ov13858@61 {

                                                        }
                                                }
                                        }
 				}

 			}
 		}
 	}

Acceptable as a DT layout ??


>
> >
> >     MAX9286                  MAX9271			SENSOR
> >
> >     probe()
> >        |
> >        ---------------------> |
> > 			      camera 1 probe() {
> > 				--------------------->|
> > 							 sensor probe()
> > 				 enable_threshold()
> > 			      }
> >       }
> >        |<--------------------|
> >     v4l2 async bound {
> > 	completed = no
> >        |-------------------->|
> > 			     camera 2 probe() {
> > 				--------------------->|
> > 							sensor probe()
> > 				 enable_threshold()
> > 			      }
> >        |<--------------------|
> > 	completed = yes
> >
> > 	compensate_amplitude()
> > 	for (subdev)
> > 	   subdev->post_register()
> >           |----------------->|
> > 			     camera 1 post_register()
> > 				subdev->post_register()
> > 				--------------------->|
> > 							post_register()
> > 								i2c writes
> > 	   subdev->post_register()
> >           |----------------->|
> > 			     camera 2 post_register()
> > 				subdev->post_register()
> > 				--------------------->|
> > 							post_register()
> > 								i2c writes
> >     }
> >
> >
> > I recall Mauro pointed me to an example when he first suggested to make the
> > MAX9271 library a proper i2c subdevice driver. Do you happen to recall which one
> > was it ?
> >
> > Thanks
> >    j
> >
> > Jacopo Mondi (5):
> >   media: i2c: max9271: Rename max9271 library driver
> >   media: i2c: Add MAX9271 I2C driver
> >   media: i2c: rdacm20: Adapt to work with MAX9271
> >   media: i2c: max9286: Fetch PIXEL_RATE in s_stream
> >   arm64: dts: GMSL: Adapt to the use max9271 driver
> >
> >  MAINTAINERS                                   |  17 +-
> >  arch/arm64/boot/dts/renesas/gmsl-cameras.dtsi |  34 +-
> >  .../arm64/boot/dts/renesas/r8a77970-eagle.dts |   6 +-
> >  drivers/media/i2c/Kconfig                     |  12 +
> >  drivers/media/i2c/Makefile                    |   3 +-
> >  drivers/media/i2c/max9271-lib.c               | 374 +++++++++++++
> >  .../media/i2c/{max9271.h => max9271-lib.h}    |   0
> >  drivers/media/i2c/max9271.c                   | 528 +++++++++++++++---
> >  drivers/media/i2c/max9286.c                   |   6 +-
> >  drivers/media/i2c/rdacm20.c                   | 139 +----
> >  drivers/media/i2c/rdacm21.c                   |   2 +-
> >  11 files changed, 917 insertions(+), 204 deletions(-)
> >  create mode 100644 drivers/media/i2c/max9271-lib.c
> >  rename drivers/media/i2c/{max9271.h => max9271-lib.h} (100%)
>
> --
> Regards,
>
> Laurent Pinchart

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

end of thread, other threads:[~2021-09-13  7:58 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-17  7:26 [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Jacopo Mondi
2021-08-17  7:26 ` [RFC 1/5] media: i2c: max9271: Rename max9271 library driver Jacopo Mondi
2021-08-18 12:48   ` Kieran Bingham
2021-08-23  2:09   ` Laurent Pinchart
2021-08-17  7:27 ` [RFC 2/5] media: i2c: Add MAX9271 I2C driver Jacopo Mondi
2021-08-17 15:49   ` Kieran Bingham
2021-08-18  8:27     ` Jacopo Mondi
2021-08-18 12:38       ` Kieran Bingham
2021-08-23  2:22       ` Laurent Pinchart
2021-08-23  7:21         ` Jacopo Mondi
2021-08-23 12:05       ` Geert Uytterhoeven
2021-08-17  7:27 ` [RFC 3/5] media: i2c: rdacm20: Adapt to work with MAX9271 Jacopo Mondi
2021-08-17  7:27 ` [RFC 4/5] media: i2c: max9286: Fetch PIXEL_RATE in s_stream Jacopo Mondi
2021-08-23  2:17   ` Laurent Pinchart
2021-08-23  7:20     ` Jacopo Mondi
2021-08-23  9:34       ` Laurent Pinchart
2021-08-17  7:27 ` [RFC 5/5] arm64: dts: GMSL: Adapt to the use max9271 driver Jacopo Mondi
2021-08-18 12:47 ` [RFC 0/5] media: i2c: Add MAX9271 subdevice driver Kieran Bingham
2021-08-23  2:12   ` Laurent Pinchart
2021-08-23  2:06 ` Laurent Pinchart
2021-09-13  7:59   ` Jacopo Mondi

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