All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
@ 2022-09-11 20:01 Mikhail Rudenko
  2022-09-11 20:01 ` [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings Mikhail Rudenko
                   ` (3 more replies)
  0 siblings, 4 replies; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-11 20:01 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Jimmy Su,
	Arnd Bergmann, Arec Kao, Laurent Pinchart, Marek Vasut
  Cc: Mikhail Rudenko, linux-media, devicetree, linux-kernel

Hello,

this series implements support for Omnivision OV4689 image
sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
megapixel image sensor. Ihis chip supports high frame rate speeds up
to 90 fps at 2688x1520 resolution. It is programmable through an I2C
interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
connection.

The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
3399-based FriendlyElec NanoPi M4 board with MCAM400 camera module.

While porting the driver, I stumbled upon two issues:

(1) In the original driver, horizontal total size (HTS) was set to a
value (2584) lower then the frame width (2688), resulting in negative
hblank. In this driver, I increased HTS to 2688, but fps dropped from
29.88 to 28.73. What is the preferred way to handle this?

(2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
gain is not linear across that range. Instead, it is piecewise linear
(and discontinuous). 0x0-0xff register values result in 0x-2x gain,
0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
with more linear segments in between. Rockchip's camera engine code
chooses one of the above segments depenging on the desired gain
value. The question is, how should we proceed keeping in mind
libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
libcamera will do the mapping, or the driver will do the mapping
itself and expose some logical gain units not tied to the actual gain
register value? Meanwhile, this driver conservatively exposes only
0x0-0xf8 gain register range.

[1] https://github.com/rockchip-linux/kernel/blob/develop-4.19/drivers/media/i2c/ov4689.c

changes in v2:
- bindings: reword descriptions
- bindings: move clock description to clocks property
- bindings: add data-lanes and link-frequencies properties to port
- driver: validate media bus configuration when probing

Mikhail Rudenko (2):
  media: dt-bindings: media: i2c: document OV4689 DT bindings
  media: i2c: add support for ov4689

 .../bindings/media/i2c/ovti,ov4689.yaml       | 141 +++
 MAINTAINERS                                   |   8 +
 drivers/media/i2c/Kconfig                     |  14 +
 drivers/media/i2c/Makefile                    |   1 +
 drivers/media/i2c/ov4689.c                    | 951 ++++++++++++++++++
 5 files changed, 1115 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
 create mode 100644 drivers/media/i2c/ov4689.c

--
2.37.3

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

* [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings
  2022-09-11 20:01 [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver Mikhail Rudenko
@ 2022-09-11 20:01 ` Mikhail Rudenko
  2022-09-12 10:55   ` Krzysztof Kozlowski
  2022-09-13 14:05   ` Tommaso Merciai
  2022-09-11 20:01 ` [PATCH v2 2/2] media: i2c: add support for ov4689 Mikhail Rudenko
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-11 20:01 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Christian Hemp, Arec Kao, Arnd Bergmann, Laurent Pinchart,
	Daniel Scally, Jimmy Su
  Cc: Mikhail Rudenko, linux-media, devicetree, linux-kernel

Add device-tree binding documentation for OV4689 image sensor driver,
and the relevant MAINTAINERS entries.

Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
---
 .../bindings/media/i2c/ovti,ov4689.yaml       | 141 ++++++++++++++++++
 MAINTAINERS                                   |   7 +
 2 files changed, 148 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml

diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
new file mode 100644
index 000000000000..376330b5572a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
@@ -0,0 +1,141 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ovti,ov4689.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Omnivision OV4689 CMOS
+
+maintainers:
+  - Mikhail Rudenko <mike.rudenko@gmail.com>
+
+description: |
+  The Omnivision OV4689 is a high performance, 1/3-inch, 4 megapixel
+  image sensor. Ihis chip supports high frame rate speeds up to 90 fps
+  at 2688x1520 resolution. It is programmable through an I2C
+  interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
+  connection.
+
+allOf:
+  - $ref: /schemas/media/video-interface-devices.yaml#
+
+properties:
+  compatible:
+    const: ovti,ov4689
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    description:
+      External clock (XVCLK) for the sensor, 6-64 MHz
+    maxItems: 1
+
+  clock-names: true
+
+  dovdd-supply:
+    description:
+      Digital I/O voltage supply, 1.7-3.0 V
+
+  avdd-supply:
+    description:
+      Analog voltage supply, 2.6-3.0 V
+
+  dvdd-supply:
+    description:
+      Digital core voltage supply, 1.1-1.3 V
+
+  powerdown-gpios:
+    maxItems: 1
+    description:
+      GPIO connected to the powerdown pin (active low)
+
+  reset-gpios:
+    maxItems: 1
+    description:
+      GPIO connected to the reset pin (active low)
+
+  orientation: true
+
+  rotation: true
+
+  port:
+    $ref: /schemas/graph.yaml#/$defs/port-base
+    additionalProperties: false
+    description:
+      Output port node, single endpoint describing the CSI-2 transmitter
+
+    properties:
+      endpoint:
+        $ref: /schemas/media/video-interfaces.yaml#
+        unevaluatedProperties: false
+
+        properties:
+          data-lanes:
+            oneOf:
+              - items:
+                  - const: 1
+                  - const: 2
+                  - const: 3
+                  - const: 4
+              - items:
+                  - const: 1
+                  - const: 2
+              - items:
+                  - const: 1
+          link-frequencies: true
+
+        required:
+          - data-lanes
+          - link-frequencies
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - dovdd-supply
+  - avdd-supply
+  - dvdd-supply
+  - powerdown-gpios
+  - reset-gpios
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        ov4689: camera@36 {
+            compatible = "ovti,ov4689";
+            reg = <0x36>;
+
+            clocks = <&ov4689_clk>;
+            clock-names = "xvclk";
+
+            avdd-supply = <&ov4689_avdd>;
+            dovdd-supply = <&ov4689_dovdd>;
+            dvdd-supply = <&ov4689_dvdd>;
+
+            powerdown-gpios = <&pio 107 GPIO_ACTIVE_LOW>;
+            reset-gpios = <&pio 109 GPIO_ACTIVE_LOW>;
+
+            orientation = <2>;
+            rotation = <0>;
+
+            port {
+                wcam_out: endpoint {
+                    remote-endpoint = <&mipi_in_wcam>;
+                    data-lanes = <1 2 3 4>;
+                    link-frequencies = /bits/ 64 <500000000>;
+                };
+            };
+        };
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index f468864fd268..63c4844f26e6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14523,6 +14523,13 @@ S:	Maintained
 T:	git git://linuxtv.org/media_tree.git
 F:	drivers/media/i2c/ov2740.c
 
+OMNIVISION OV4689 SENSOR DRIVER
+M:	Mikhail Rudenko <mike.rudenko@gmail.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+T:	git git://linuxtv.org/media_tree.git
+F:	Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
+
 OMNIVISION OV5640 SENSOR DRIVER
 M:	Steve Longerbeam <slongerbeam@gmail.com>
 L:	linux-media@vger.kernel.org
-- 
2.37.3


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

* [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-11 20:01 [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver Mikhail Rudenko
  2022-09-11 20:01 ` [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings Mikhail Rudenko
@ 2022-09-11 20:01 ` Mikhail Rudenko
  2022-09-11 22:52   ` kernel test robot
                     ` (4 more replies)
  2022-09-14  9:58 ` [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver Dave Stevenson
  2022-09-22  9:53 ` Sakari Ailus
  3 siblings, 5 replies; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-11 20:01 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut
  Cc: Mikhail Rudenko, linux-media, devicetree, linux-kernel

Add a V4L2 sub-device driver for OmniVision OV4689 image sensor. This
is a 4 Mpx image sensor using the I2C bus for control and the CSI-2
bus for data.

This driver supports following features:
- manual exposure and analog gain control support
- test pattern support
- media controller support
- runtime PM support
- support following resolutions:
  + 2688x1520 at 30 fps

The driver provides all mandatory V4L2 controls for compatibility with
libcamera. The sensor supports 1/2/4-lane CSI-2 modes, but the driver
implements 4 lane mode only at this moment.

Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
---
 MAINTAINERS                |   1 +
 drivers/media/i2c/Kconfig  |  14 +
 drivers/media/i2c/Makefile |   1 +
 drivers/media/i2c/ov4689.c | 951 +++++++++++++++++++++++++++++++++++++
 4 files changed, 967 insertions(+)
 create mode 100644 drivers/media/i2c/ov4689.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 63c4844f26e6..1857f3864e1b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14529,6 +14529,7 @@ L:	linux-media@vger.kernel.org
 S:	Maintained
 T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
+F:	drivers/media/i2c/ov5647.c
 
 OMNIVISION OV5640 SENSOR DRIVER
 M:	Steve Longerbeam <slongerbeam@gmail.com>
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index fae2baabb773..4993e1ae2ea8 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -429,6 +429,20 @@ config VIDEO_OV2740
 	  To compile this driver as a module, choose M here: the
 	  module will be called ov2740.
 
+config VIDEO_OV4689
+	tristate "OmniVision OV4689 sensor support"
+	depends on OF
+	depends on GPIOLIB && VIDEO_DEV && I2C
+	select MEDIA_CONTROLLER
+	select VIDEO_V4L2_SUBDEV_API
+	select V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV4689 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov4689.
+
 config VIDEO_OV5640
 	tristate "OmniVision OV5640 sensor support"
 	depends on OF
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 3e1696963e7f..7446c0a1eed0 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
 obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
 obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
 obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
+obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
 obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
 obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
 obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c
new file mode 100644
index 000000000000..9f05e812acf8
--- /dev/null
+++ b/drivers/media/i2c/ov4689.c
@@ -0,0 +1,951 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ov4689 driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-fwnode.h>
+
+#define CHIP_ID				0x004688
+#define OV4689_REG_CHIP_ID		0x300a
+
+#define OV4689_XVCLK_FREQ		24000000
+
+#define OV4689_REG_CTRL_MODE		0x0100
+#define OV4689_MODE_SW_STANDBY		0x0
+#define OV4689_MODE_STREAMING		BIT(0)
+
+#define OV4689_REG_EXPOSURE		0x3500
+#define OV4689_EXPOSURE_MIN		4
+#define OV4689_EXPOSURE_STEP		1
+#define OV4689_VTS_MAX			0x7fff
+
+#define OV4689_REG_GAIN_H		0x3508
+#define OV4689_REG_GAIN_L		0x3509
+#define OV4689_GAIN_H_MASK		0x07
+#define OV4689_GAIN_H_SHIFT		8
+#define OV4689_GAIN_L_MASK		0xff
+#define OV4689_GAIN_MIN			0x10
+#define OV4689_GAIN_MAX			0xf8
+#define OV4689_GAIN_STEP		1
+#define OV4689_GAIN_DEFAULT		0x10
+
+#define OV4689_REG_TEST_PATTERN		0x5040
+#define OV4689_TEST_PATTERN_ENABLE	0x80
+#define OV4689_TEST_PATTERN_DISABLE	0x0
+
+#define OV4689_REG_VTS			0x380e
+
+#define REG_NULL			0xFFFF
+
+#define OV4689_REG_VALUE_08BIT		1
+#define OV4689_REG_VALUE_16BIT		2
+#define OV4689_REG_VALUE_24BIT		3
+
+#define OV4689_LANES			4
+#define OV4689_BITS_PER_SAMPLE		10
+
+static const char *const ov4689_supply_names[] = {
+	"avdd", /* Analog power */
+	"dovdd", /* Digital I/O power */
+	"dvdd", /* Digital core power */
+};
+
+#define OV4689_NUM_SUPPLIES ARRAY_SIZE(ov4689_supply_names)
+
+struct regval {
+	u16 addr;
+	u8 val;
+};
+
+struct ov4689_mode {
+	u32 width;
+	u32 height;
+	u32 max_fps;
+	u32 hts_def;
+	u32 vts_def;
+	u32 exp_def;
+	const struct regval *reg_list;
+};
+
+struct ov4689 {
+	struct i2c_client *client;
+	struct clk *xvclk;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *pwdn_gpio;
+	struct regulator_bulk_data supplies[OV4689_NUM_SUPPLIES];
+
+	struct v4l2_subdev subdev;
+	struct media_pad pad;
+
+	struct mutex mutex; /* lock to protect streaming, ctrls and cur_mode */
+	bool streaming;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *anal_gain;
+	struct v4l2_ctrl *digi_gain;
+	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *test_pattern;
+
+	const struct ov4689_mode *cur_mode;
+};
+
+#define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
+
+/*
+ * Xclk 24Mhz
+ */
+static const struct regval ov4689_global_regs[] = {
+	{ REG_NULL, 0x00 },
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 30fps
+ * mipi_datarate per lane 1008Mbps
+ */
+static const struct regval ov4689_2688x1520_regs[] = {
+	{0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
+	{0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
+	{0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
+	{0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
+	{0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
+	{0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
+	{0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
+	{0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
+	{0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
+	{0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
+	{0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
+	{0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
+	{0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
+	{0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
+	{0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
+	{0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
+	{0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
+	{0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
+	{0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
+	{0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
+	{0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
+	{0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
+	{0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
+	{0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
+	{0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
+	{0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
+	{0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
+	{0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
+	{0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
+	{0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
+	{0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
+	{0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
+	{0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
+	{0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
+	{0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
+	{0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
+	{0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
+	{0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
+	{0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
+	{0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
+	{0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
+	{0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
+	{0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
+	{0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
+	{0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
+	{0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
+	{0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
+	{0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
+	{0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
+	{0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
+	{0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
+	{0x380d, 0x80}, {0x380e, 0x06}, {0x380f, 0x12},
+	{0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
+	{0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
+	{0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
+	{0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
+	{0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
+	{0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
+	{0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
+	{0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
+	{0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
+	{0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
+	{0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
+	{0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
+	{0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
+	{0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
+	{0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
+	{0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
+	{0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
+	{0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
+	{0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
+	{0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
+	{0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
+	{0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
+	{0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
+	{0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
+	{0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
+	{0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
+	{0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
+	{0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
+	{0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
+	{0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
+	{0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
+	{0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
+	{REG_NULL, 0x00},
+};
+
+static const struct ov4689_mode supported_modes[] = {
+	{
+		.width = 2688,
+		.height = 1520,
+		.max_fps = 30,
+		.exp_def = 0x0600,
+		.hts_def = 0x0a80,
+		.vts_def = 0x0612,
+		.reg_list = ov4689_2688x1520_regs,
+	},
+};
+
+#define OV4689_LINK_FREQ_500MHZ 500000000
+static const s64 link_freq_menu_items[] = { OV4689_LINK_FREQ_500MHZ };
+
+static const char *const ov4689_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Color Bar Type 1",
+	"Vertical Color Bar Type 2",
+	"Vertical Color Bar Type 3",
+	"Vertical Color Bar Type 4"
+};
+
+/* Write registers up to 4 at a time */
+static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len,
+			    u32 val)
+{
+	u32 buf_i, val_i;
+	__be32 val_be;
+	u8 *val_p;
+	u8 buf[6];
+
+	if (len > 4)
+		return -EINVAL;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	val_be = cpu_to_be32(val);
+	val_p = (u8 *)&val_be;
+	buf_i = 2;
+	val_i = 4 - len;
+
+	while (val_i < 4)
+		buf[buf_i++] = val_p[val_i++];
+
+	if (i2c_master_send(client, buf, len + 2) != len + 2)
+		return -EIO;
+
+	return 0;
+}
+
+static int ov4689_write_array(struct i2c_client *client,
+			      const struct regval *regs)
+{
+	int ret = 0;
+	u32 i;
+
+	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+		ret = ov4689_write_reg(client, regs[i].addr,
+				       OV4689_REG_VALUE_08BIT, regs[i].val);
+
+	return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
+			   u32 *val)
+{
+	__be16 reg_addr_be = cpu_to_be16(reg);
+	struct i2c_msg msgs[2];
+	__be32 data_be = 0;
+	u8 *data_be_p;
+	int ret;
+
+	if (len > 4 || !len)
+		return -EINVAL;
+
+	data_be_p = (u8 *)&data_be;
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = (u8 *)&reg_addr_be;
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = &data_be_p[4 - len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	*val = be32_to_cpu(data_be);
+
+	return 0;
+}
+
+static void ov4689_fill_fmt(const struct ov4689_mode *mode,
+			    struct v4l2_mbus_framefmt *fmt)
+{
+	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+	fmt->width = mode->width;
+	fmt->height = mode->height;
+	fmt->field = V4L2_FIELD_NONE;
+}
+
+static int ov4689_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_state *sd_state,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
+	struct ov4689 *ov4689 = to_ov4689(sd);
+
+	/* only one mode supported for now */
+	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
+
+	return 0;
+}
+
+static int ov4689_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_state *sd_state,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
+	struct ov4689 *ov4689 = to_ov4689(sd);
+
+	/* only one mode supported for now */
+	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
+
+	return 0;
+}
+
+static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *sd_state,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index != 0)
+		return -EINVAL;
+	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+	return 0;
+}
+
+static int ov4689_enum_frame_sizes(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_state *sd_state,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
+		return -EINVAL;
+
+	fse->min_width = supported_modes[fse->index].width;
+	fse->max_width = supported_modes[fse->index].width;
+	fse->max_height = supported_modes[fse->index].height;
+	fse->min_height = supported_modes[fse->index].height;
+
+	return 0;
+}
+
+static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
+{
+	u32 val;
+
+	if (pattern)
+		val = (pattern - 1) | OV4689_TEST_PATTERN_ENABLE;
+	else
+		val = OV4689_TEST_PATTERN_DISABLE;
+
+	return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN,
+				OV4689_REG_VALUE_08BIT, val);
+}
+
+static int ov4689_get_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_state *state,
+				struct v4l2_subdev_selection *sel)
+{
+	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.top = 0;
+		sel->r.left = 0;
+		sel->r.width = 2720;
+		sel->r.height = 1536;
+		return 0;
+	case V4L2_SEL_TGT_CROP:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.top = 8;
+		sel->r.left = 16;
+		sel->r.width = 2688;
+		sel->r.height = 1520;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct ov4689 *ov4689 = to_ov4689(sd);
+	struct i2c_client *client = ov4689->client;
+	int ret = 0;
+
+	mutex_lock(&ov4689->mutex);
+
+	on = !!on;
+	if (on == ov4689->streaming)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_resume_and_get(&client->dev);
+		if (ret < 0)
+			goto unlock_and_return;
+
+		ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
+		if (ret) {
+			pm_runtime_put(&client->dev);
+			goto unlock_and_return;
+		}
+
+		ret = ov4689_write_array(ov4689->client,
+					 ov4689->cur_mode->reg_list);
+		if (ret) {
+			pm_runtime_put(&client->dev);
+			goto unlock_and_return;
+		}
+
+		ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
+				       OV4689_REG_VALUE_08BIT,
+				       OV4689_MODE_STREAMING);
+		if (ret) {
+			pm_runtime_put(&client->dev);
+			goto unlock_and_return;
+		}
+	} else {
+		ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
+				 OV4689_REG_VALUE_08BIT,
+				 OV4689_MODE_SW_STANDBY);
+		pm_runtime_put(&client->dev);
+	}
+
+	ov4689->streaming = on;
+
+unlock_and_return:
+	mutex_unlock(&ov4689->mutex);
+
+	return ret;
+}
+
+/* Calculate the delay in us by clock rate and clock cycles */
+static inline u32 ov4689_cal_delay(u32 cycles)
+{
+	return DIV_ROUND_UP(cycles, OV4689_XVCLK_FREQ / 1000 / 1000);
+}
+
+static int __ov4689_power_on(struct ov4689 *ov4689)
+{
+	struct device *dev = &ov4689->client->dev;
+	u32 delay_us;
+	int ret;
+
+	ret = clk_prepare_enable(ov4689->xvclk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable xvclk\n");
+		return ret;
+	}
+
+	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
+
+	ret = regulator_bulk_enable(OV4689_NUM_SUPPLIES, ov4689->supplies);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable regulators\n");
+		goto disable_clk;
+	}
+
+	gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
+	usleep_range(500, 1000);
+	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 0);
+
+	/* 8192 cycles prior to first SCCB transaction */
+	delay_us = ov4689_cal_delay(8192);
+	usleep_range(delay_us, delay_us * 2);
+
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(ov4689->xvclk);
+
+	return ret;
+}
+
+static void __ov4689_power_off(struct ov4689 *ov4689)
+{
+	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 1);
+	clk_disable_unprepare(ov4689->xvclk);
+	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
+	regulator_bulk_disable(OV4689_NUM_SUPPLIES, ov4689->supplies);
+}
+
+static int __maybe_unused ov4689_runtime_resume(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov4689 *ov4689 = to_ov4689(sd);
+
+	return __ov4689_power_on(ov4689);
+}
+
+static int __maybe_unused ov4689_runtime_suspend(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov4689 *ov4689 = to_ov4689(sd);
+
+	__ov4689_power_off(ov4689);
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct ov4689 *ov4689 = to_ov4689(sd);
+	struct v4l2_mbus_framefmt *try_fmt;
+
+	mutex_lock(&ov4689->mutex);
+
+	try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0);
+	/* Initialize try_fmt */
+	ov4689_fill_fmt(&supported_modes[0], try_fmt);
+
+	mutex_unlock(&ov4689->mutex);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops ov4689_pm_ops = {
+	SET_RUNTIME_PM_OPS(ov4689_runtime_suspend, ov4689_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
+	.open = ov4689_open,
+};
+#endif
+
+static const struct v4l2_subdev_video_ops ov4689_video_ops = {
+	.s_stream = ov4689_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
+	.enum_mbus_code = ov4689_enum_mbus_code,
+	.enum_frame_size = ov4689_enum_frame_sizes,
+	.get_fmt = ov4689_get_fmt,
+	.set_fmt = ov4689_set_fmt,
+	.get_selection = ov4689_get_selection,
+};
+
+static const struct v4l2_subdev_ops ov4689_subdev_ops = {
+	.video = &ov4689_video_ops,
+	.pad = &ov4689_pad_ops,
+};
+
+static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov4689 *ov4689 =
+		container_of(ctrl->handler, struct ov4689, ctrl_handler);
+	struct i2c_client *client = ov4689->client;
+	s64 max_expo;
+	int ret;
+
+	/* Propagate change of current control to all related controls */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update max exposure while meeting expected vblanking */
+		max_expo = ov4689->cur_mode->height + ctrl->val - 4;
+		__v4l2_ctrl_modify_range(ov4689->exposure,
+					 ov4689->exposure->minimum, max_expo,
+					 ov4689->exposure->step,
+					 ov4689->exposure->default_value);
+		break;
+	}
+
+	if (!pm_runtime_get_if_in_use(&client->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		/* 4 least significant bits of expsoure are fractional part */
+		ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE,
+				       OV4689_REG_VALUE_24BIT, ctrl->val << 4);
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		ret = ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H,
+				       OV4689_REG_VALUE_08BIT,
+				       (ctrl->val >> OV4689_GAIN_H_SHIFT) &
+					       OV4689_GAIN_H_MASK);
+		ret |= ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L,
+					OV4689_REG_VALUE_08BIT,
+					ctrl->val & OV4689_GAIN_L_MASK);
+		break;
+	case V4L2_CID_VBLANK:
+		ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS,
+				       OV4689_REG_VALUE_16BIT,
+				       ctrl->val + ov4689->cur_mode->height);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
+		break;
+	default:
+		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+			 __func__, ctrl->id, ctrl->val);
+		ret = -EINVAL;
+		break;
+	}
+
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
+	.s_ctrl = ov4689_set_ctrl,
+};
+
+static int ov4689_initialize_controls(struct ov4689 *ov4689)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&ov4689->subdev);
+	struct v4l2_fwnode_device_properties props;
+	struct v4l2_ctrl_handler *handler;
+	const struct ov4689_mode *mode;
+	s64 exposure_max, vblank_def;
+	struct v4l2_ctrl *ctrl;
+	u32 h_blank, pixel_rate;
+	int ret;
+
+	handler = &ov4689->ctrl_handler;
+	mode = ov4689->cur_mode;
+	ret = v4l2_ctrl_handler_init(handler, 10);
+	if (ret)
+		return ret;
+	handler->lock = &ov4689->mutex;
+
+	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
+				      link_freq_menu_items);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	pixel_rate = (link_freq_menu_items[0] * 2 * OV4689_LANES) /
+		     OV4689_BITS_PER_SAMPLE;
+	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, pixel_rate, 1,
+			  pixel_rate);
+
+	h_blank = mode->hts_def - mode->width;
+	ov4689->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+					   h_blank, h_blank, 1, h_blank);
+	if (ov4689->hblank)
+		ov4689->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	vblank_def = mode->vts_def - mode->height;
+	ov4689->vblank =
+		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
+				  vblank_def, OV4689_VTS_MAX - mode->height, 1,
+				  vblank_def);
+
+	exposure_max = mode->vts_def - 4;
+	ov4689->exposure =
+		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_EXPOSURE,
+				  OV4689_EXPOSURE_MIN, exposure_max,
+				  OV4689_EXPOSURE_STEP, mode->exp_def);
+
+	ov4689->anal_gain =
+		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops,
+				  V4L2_CID_ANALOGUE_GAIN, OV4689_GAIN_MIN,
+				  OV4689_GAIN_MAX, OV4689_GAIN_STEP,
+				  OV4689_GAIN_DEFAULT);
+
+	ov4689->test_pattern =
+		v4l2_ctrl_new_std_menu_items(handler, &ov4689_ctrl_ops,
+					     V4L2_CID_TEST_PATTERN,
+					     ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
+					     0, 0, ov4689_test_pattern_menu);
+
+	if (handler->error) {
+		ret = handler->error;
+		dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n",
+			ret);
+		goto err_free_handler;
+	}
+
+	ret = v4l2_fwnode_device_parse(&client->dev, &props);
+	if (ret)
+		goto err_free_handler;
+
+	ret = v4l2_ctrl_new_fwnode_properties(handler, &ov4689_ctrl_ops,
+					      &props);
+	if (ret)
+		goto err_free_handler;
+
+	ov4689->subdev.ctrl_handler = handler;
+
+	return 0;
+
+err_free_handler:
+	v4l2_ctrl_handler_free(handler);
+
+	return ret;
+}
+
+static int ov4689_check_sensor_id(struct ov4689 *ov4689,
+				  struct i2c_client *client)
+{
+	struct device *dev = &ov4689->client->dev;
+	u32 id = 0;
+	int ret;
+
+	ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID,
+			      OV4689_REG_VALUE_16BIT, &id);
+	if (id != CHIP_ID) {
+		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
+
+	return 0;
+}
+
+static int ov4689_configure_regulators(struct ov4689 *ov4689)
+{
+	unsigned int i;
+
+	for (i = 0; i < OV4689_NUM_SUPPLIES; i++)
+		ov4689->supplies[i].supply = ov4689_supply_names[i];
+
+	return devm_regulator_bulk_get(&ov4689->client->dev,
+				       OV4689_NUM_SUPPLIES, ov4689->supplies);
+}
+
+static int ov4689_check_hwcfg(struct device *dev)
+{
+	struct fwnode_handle *fwnode = dev_fwnode(dev);
+	struct v4l2_fwnode_endpoint bus_cfg = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY,
+	};
+	struct fwnode_handle *endpoint;
+	unsigned int i;
+	int ret;
+
+	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
+	if (!endpoint)
+		return -EPROBE_DEFER;
+
+	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
+	fwnode_handle_put(endpoint);
+	if (ret)
+		return ret;
+
+	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
+		dev_err(dev, "only a 4-lane CSI2 config is supported");
+		ret = -EINVAL;
+		goto out_free_bus_cfg;
+	}
+
+	if (!bus_cfg.nr_of_link_frequencies) {
+		dev_err(dev, "no link frequencies defined\n");
+		ret = -EINVAL;
+		goto out_free_bus_cfg;
+	}
+
+	for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
+		if (bus_cfg.link_frequencies[i] == OV4689_LINK_FREQ_500MHZ)
+			break;
+
+	if (i == bus_cfg.nr_of_link_frequencies) {
+		dev_err(dev, "supported link freq %ull not found\n",
+			OV4689_LINK_FREQ_500MHZ);
+		ret = -EINVAL;
+		goto out_free_bus_cfg;
+	}
+
+out_free_bus_cfg:
+	v4l2_fwnode_endpoint_free(&bus_cfg);
+
+	return ret;
+}
+
+static int ov4689_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct v4l2_subdev *sd;
+	struct ov4689 *ov4689;
+	int ret;
+
+	ret = ov4689_check_hwcfg(dev);
+	if (ret)
+		return ret;
+
+	ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
+	if (!ov4689)
+		return -ENOMEM;
+
+	ov4689->client = client;
+	ov4689->cur_mode = &supported_modes[0];
+
+	ov4689->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(ov4689->xvclk)) {
+		dev_err(dev, "Failed to get xvclk\n");
+		return -EINVAL;
+	}
+
+	ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
+		return ret;
+	}
+	if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
+		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
+
+	ov4689->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ov4689->reset_gpio)) {
+		dev_err(dev, "Failed to get reset-gpios\n");
+		return -EINVAL;
+	}
+
+	ov4689->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
+	if (IS_ERR(ov4689->pwdn_gpio)) {
+		dev_err(dev, "Failed to get pwdn-gpios\n");
+		return -EINVAL;
+	}
+
+	ret = ov4689_configure_regulators(ov4689);
+	if (ret) {
+		dev_err(dev, "Failed to get power regulators\n");
+		return ret;
+	}
+
+	mutex_init(&ov4689->mutex);
+
+	sd = &ov4689->subdev;
+	v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops);
+	ret = ov4689_initialize_controls(ov4689);
+	if (ret)
+		goto err_destroy_mutex;
+
+	ret = __ov4689_power_on(ov4689);
+	if (ret)
+		goto err_free_handler;
+
+	ret = ov4689_check_sensor_id(ov4689, client);
+	if (ret)
+		goto err_power_off;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	sd->internal_ops = &ov4689_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
+	if (ret < 0)
+		goto err_power_off;
+#endif
+
+	ret = v4l2_async_register_subdev_sensor(sd);
+	if (ret) {
+		dev_err(dev, "v4l2 async register subdev failed\n");
+		goto err_clean_entity;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_idle(dev);
+
+	return 0;
+
+err_clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+err_power_off:
+	__ov4689_power_off(ov4689);
+err_free_handler:
+	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
+err_destroy_mutex:
+	mutex_destroy(&ov4689->mutex);
+
+	return ret;
+}
+
+static int ov4689_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov4689 *ov4689 = to_ov4689(sd);
+
+	v4l2_async_unregister_subdev(sd);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
+	mutex_destroy(&ov4689->mutex);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		__ov4689_power_off(ov4689);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov4689_id[] = {
+	{ "ov4689", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, ov4689_id);
+
+static const struct of_device_id ov4689_of_match[] = {
+	{ .compatible = "ovti,ov4689" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ov4689_of_match);
+
+static struct i2c_driver ov4689_i2c_driver = {
+	.driver = {
+		.name = "ov4689",
+		.pm = &ov4689_pm_ops,
+		.of_match_table = of_match_ptr(ov4689_of_match),
+	},
+	.probe = ov4689_probe,
+	.remove	= ov4689_remove,
+	.id_table = ov4689_id,
+};
+
+module_i2c_driver(ov4689_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision ov4689 sensor driver");
+MODULE_LICENSE("GPL");
-- 
2.37.3


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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-11 20:01 ` [PATCH v2 2/2] media: i2c: add support for ov4689 Mikhail Rudenko
@ 2022-09-11 22:52   ` kernel test robot
  2022-09-12 10:56   ` Krzysztof Kozlowski
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 41+ messages in thread
From: kernel test robot @ 2022-09-11 22:52 UTC (permalink / raw)
  To: Mikhail Rudenko, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Sakari Ailus, Hans Verkuil, Jacopo Mondi,
	Shawn Tu, Randy Dunlap, Daniel Scally, Christian Hemp,
	Laurent Pinchart, Marek Vasut
  Cc: kbuild-all, linux-media, Mikhail Rudenko, devicetree, linux-kernel

Hi Mikhail,

I love your patch! Perhaps something to improve:

[auto build test WARNING on media-tree/master]
[also build test WARNING on robh/for-next krzk-dt/for-next linus/master v6.0-rc5 next-20220909]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Mikhail-Rudenko/Add-Omnivision-OV4689-image-sensor-driver/20220912-040337
base:   git://linuxtv.org/media_tree.git master
config: x86_64-allyesconfig (https://download.01.org/0day-ci/archive/20220912/202209120637.Ypw3tniN-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/7c4d2965802d2be20badfef953b1d6f0d13d718f
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Mikhail-Rudenko/Add-Omnivision-OV4689-image-sensor-driver/20220912-040337
        git checkout 7c4d2965802d2be20badfef953b1d6f0d13d718f
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/media/i2c/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/media/i2c/ov4689.c:112:28: warning: 'ov4689_global_regs' defined but not used [-Wunused-const-variable=]
     112 | static const struct regval ov4689_global_regs[] = {
         |                            ^~~~~~~~~~~~~~~~~~


vim +/ov4689_global_regs +112 drivers/media/i2c/ov4689.c

   108	
   109	/*
   110	 * Xclk 24Mhz
   111	 */
 > 112	static const struct regval ov4689_global_regs[] = {
   113		{ REG_NULL, 0x00 },
   114	};
   115	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

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

* Re: [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings
  2022-09-11 20:01 ` [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings Mikhail Rudenko
@ 2022-09-12 10:55   ` Krzysztof Kozlowski
  2022-09-15 12:16     ` Mikhail Rudenko
  2022-09-13 14:05   ` Tommaso Merciai
  1 sibling, 1 reply; 41+ messages in thread
From: Krzysztof Kozlowski @ 2022-09-12 10:55 UTC (permalink / raw)
  To: Mikhail Rudenko, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Sakari Ailus, Hans Verkuil, Jacopo Mondi,
	Shawn Tu, Christian Hemp, Arec Kao, Arnd Bergmann,
	Laurent Pinchart, Daniel Scally, Jimmy Su
  Cc: linux-media, devicetree, linux-kernel

On 11/09/2022 22:01, Mikhail Rudenko wrote:
> Add device-tree binding documentation for OV4689 image sensor driver,
> and the relevant MAINTAINERS entries.
> 
> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>

Too many "media" prefixes in the subject. Also you duplicated dt
bindings as prefix and commit msg (skip the latter).

> ---
>  .../bindings/media/i2c/ovti,ov4689.yaml       | 141 ++++++++++++++++++
>  MAINTAINERS                                   |   7 +
>  2 files changed, 148 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> new file mode 100644
> index 000000000000..376330b5572a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> @@ -0,0 +1,141 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/i2c/ovti,ov4689.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Omnivision OV4689 CMOS
> +
> +maintainers:
> +  - Mikhail Rudenko <mike.rudenko@gmail.com>
> +
> +description: |
> +  The Omnivision OV4689 is a high performance, 1/3-inch, 4 megapixel
> +  image sensor. Ihis chip supports high frame rate speeds up to 90 fps
> +  at 2688x1520 resolution. It is programmable through an I2C
> +  interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> +  connection.
> +
> +allOf:
> +  - $ref: /schemas/media/video-interface-devices.yaml#
> +
> +properties:
> +  compatible:
> +    const: ovti,ov4689
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    description:
> +      External clock (XVCLK) for the sensor, 6-64 MHz
> +    maxItems: 1
> +
> +  clock-names: true

This has to be strictly defined - which name you expect.

> +
> +  dovdd-supply:
> +    description:
> +      Digital I/O voltage supply, 1.7-3.0 V
> +
> +  avdd-supply:
> +    description:
> +      Analog voltage supply, 2.6-3.0 V
> +
> +  dvdd-supply:
> +    description:
> +      Digital core voltage supply, 1.1-1.3 V
> +
> +  powerdown-gpios:
> +    maxItems: 1

You can skip here maxItems - it is defined by gpio-consumer-common.

> +    description:
> +      GPIO connected to the powerdown pin (active low)
> +
> +  reset-gpios:
> +    maxItems: 1
> +    description:
> +      GPIO connected to the reset pin (active low)
> +

Best regards,
Krzysztof

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-11 20:01 ` [PATCH v2 2/2] media: i2c: add support for ov4689 Mikhail Rudenko
  2022-09-11 22:52   ` kernel test robot
@ 2022-09-12 10:56   ` Krzysztof Kozlowski
  2022-09-15 20:40     ` Mikhail Rudenko
  2022-09-14 15:51   ` Tommaso Merciai
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 41+ messages in thread
From: Krzysztof Kozlowski @ 2022-09-12 10:56 UTC (permalink / raw)
  To: Mikhail Rudenko, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Sakari Ailus, Hans Verkuil, Jacopo Mondi,
	Shawn Tu, Randy Dunlap, Daniel Scally, Christian Hemp,
	Laurent Pinchart, Marek Vasut
  Cc: linux-media, devicetree, linux-kernel

On 11/09/2022 22:01, Mikhail Rudenko wrote:
> +static const struct i2c_device_id ov4689_id[] = {
> +	{ "ov4689", 0 },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(i2c, ov4689_id);
> +
> +static const struct of_device_id ov4689_of_match[] = {
> +	{ .compatible = "ovti,ov4689" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, ov4689_of_match);
> +
> +static struct i2c_driver ov4689_i2c_driver = {
> +	.driver = {
> +		.name = "ov4689",
> +		.pm = &ov4689_pm_ops,
> +		.of_match_table = of_match_ptr(ov4689_of_match),

of_match_ptr is usually paired with maybe_unused, otherwise you will
have compile test warnings.

> +	},
> +	.probe = ov4689_probe,
> +	.remove	= ov4689_remove,
> +	.id_table = ov4689_id,
> +};
> +
> +module_i2c_driver(ov4689_i2c_driver);
> +
> +MODULE_DESCRIPTION("OmniVision ov4689 sensor driver");
> +MODULE_LICENSE("GPL");


Best regards,
Krzysztof

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

* Re: [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings
  2022-09-11 20:01 ` [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings Mikhail Rudenko
  2022-09-12 10:55   ` Krzysztof Kozlowski
@ 2022-09-13 14:05   ` Tommaso Merciai
  2022-09-15 20:11     ` Mikhail Rudenko
  1 sibling, 1 reply; 41+ messages in thread
From: Tommaso Merciai @ 2022-09-13 14:05 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Christian Hemp, Arec Kao, Arnd Bergmann, Laurent Pinchart,
	Daniel Scally, Jimmy Su, linux-media, devicetree, linux-kernel

Hi Mikhail,

On Sun, Sep 11, 2022 at 11:01:34PM +0300, Mikhail Rudenko wrote:
> Add device-tree binding documentation for OV4689 image sensor driver,
> and the relevant MAINTAINERS entries.
> 
> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
> ---
>  .../bindings/media/i2c/ovti,ov4689.yaml       | 141 ++++++++++++++++++
>  MAINTAINERS                                   |   7 +
>  2 files changed, 148 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> new file mode 100644
> index 000000000000..376330b5572a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> @@ -0,0 +1,141 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/i2c/ovti,ov4689.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Omnivision OV4689 CMOS
> +
> +maintainers:
> +  - Mikhail Rudenko <mike.rudenko@gmail.com>
> +
> +description: |
> +  The Omnivision OV4689 is a high performance, 1/3-inch, 4 megapixel
> +  image sensor. Ihis chip supports high frame rate speeds up to 90 fps
> +  at 2688x1520 resolution. It is programmable through an I2C
> +  interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> +  connection.
> +
> +allOf:
> +  - $ref: /schemas/media/video-interface-devices.yaml#
> +
> +properties:
> +  compatible:
> +    const: ovti,ov4689
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    description:
> +      External clock (XVCLK) for the sensor, 6-64 MHz
> +    maxItems: 1
> +
> +  clock-names: true
> +
> +  dovdd-supply:
> +    description:
> +      Digital I/O voltage supply, 1.7-3.0 V
> +
> +  avdd-supply:
> +    description:
> +      Analog voltage supply, 2.6-3.0 V
> +
> +  dvdd-supply:
> +    description:
> +      Digital core voltage supply, 1.1-1.3 V
> +
> +  powerdown-gpios:
> +    maxItems: 1
> +    description:
> +      GPIO connected to the powerdown pin (active low)
> +
> +  reset-gpios:
> +    maxItems: 1
> +    description:
> +      GPIO connected to the reset pin (active low)
> +
> +  orientation: true
> +
> +  rotation: true
> +
> +  port:
> +    $ref: /schemas/graph.yaml#/$defs/port-base
> +    additionalProperties: false
> +    description:
> +      Output port node, single endpoint describing the CSI-2 transmitter
> +
> +    properties:
> +      endpoint:
> +        $ref: /schemas/media/video-interfaces.yaml#
> +        unevaluatedProperties: false
> +
> +        properties:
> +          data-lanes:
> +            oneOf:
> +              - items:
> +                  - const: 1
> +                  - const: 2
> +                  - const: 3
> +                  - const: 4
> +              - items:
> +                  - const: 1
> +                  - const: 2
> +              - items:
> +                  - const: 1
> +          link-frequencies: true
> +
> +        required:
> +          - data-lanes
> +          - link-frequencies
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - clock-names
> +  - dovdd-supply
> +  - avdd-supply
> +  - dvdd-supply
> +  - powerdown-gpios
> +  - reset-gpios
> +  - port

I think we don't need all of these entries as required.
The only let me say "really" required are:

- compatible
- reg
- clocks
- port

Regards,
Tommaso

> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +
> +    i2c {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        ov4689: camera@36 {
> +            compatible = "ovti,ov4689";
> +            reg = <0x36>;
> +
> +            clocks = <&ov4689_clk>;
> +            clock-names = "xvclk";
> +
> +            avdd-supply = <&ov4689_avdd>;
> +            dovdd-supply = <&ov4689_dovdd>;
> +            dvdd-supply = <&ov4689_dvdd>;
> +
> +            powerdown-gpios = <&pio 107 GPIO_ACTIVE_LOW>;
> +            reset-gpios = <&pio 109 GPIO_ACTIVE_LOW>;
> +
> +            orientation = <2>;
> +            rotation = <0>;
> +
> +            port {
> +                wcam_out: endpoint {
> +                    remote-endpoint = <&mipi_in_wcam>;
> +                    data-lanes = <1 2 3 4>;
> +                    link-frequencies = /bits/ 64 <500000000>;
> +                };
> +            };
> +        };
> +    };
> +
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f468864fd268..63c4844f26e6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14523,6 +14523,13 @@ S:	Maintained
>  T:	git git://linuxtv.org/media_tree.git
>  F:	drivers/media/i2c/ov2740.c
>  
> +OMNIVISION OV4689 SENSOR DRIVER
> +M:	Mikhail Rudenko <mike.rudenko@gmail.com>
> +L:	linux-media@vger.kernel.org
> +S:	Maintained
> +T:	git git://linuxtv.org/media_tree.git
> +F:	Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> +
>  OMNIVISION OV5640 SENSOR DRIVER
>  M:	Steve Longerbeam <slongerbeam@gmail.com>
>  L:	linux-media@vger.kernel.org
> -- 
> 2.37.3
> 

-- 
Tommaso Merciai
Embedded Linux Engineer
tommaso.merciai@amarulasolutions.com
__________________________________

Amarula Solutions SRL
Via Le Canevare 30, 31100 Treviso, Veneto, IT
T. +39 042 243 5310
info@amarulasolutions.com
www.amarulasolutions.com

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-11 20:01 [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver Mikhail Rudenko
  2022-09-11 20:01 ` [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings Mikhail Rudenko
  2022-09-11 20:01 ` [PATCH v2 2/2] media: i2c: add support for ov4689 Mikhail Rudenko
@ 2022-09-14  9:58 ` Dave Stevenson
  2022-09-15 21:27   ` Mikhail Rudenko
  2022-09-22  9:53 ` Sakari Ailus
  3 siblings, 1 reply; 41+ messages in thread
From: Dave Stevenson @ 2022-09-14  9:58 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Jimmy Su,
	Arnd Bergmann, Arec Kao, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail

On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko <mike.rudenko@gmail.com> wrote:
>
> Hello,
>
> this series implements support for Omnivision OV4689 image
> sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
> megapixel image sensor. Ihis chip supports high frame rate speeds up
> to 90 fps at 2688x1520 resolution. It is programmable through an I2C
> interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> connection.
>
> The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
> and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
> 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera module.
>
> While porting the driver, I stumbled upon two issues:
>
> (1) In the original driver, horizontal total size (HTS) was set to a
> value (2584) lower then the frame width (2688), resulting in negative
> hblank. In this driver, I increased HTS to 2688, but fps dropped from
> 29.88 to 28.73. What is the preferred way to handle this?

This is one of the joys of sensors - they don't all work in the same way.

I don't have an official datasheet for OV4689 from Omnivision, but
found one on the internet [1]. That should allow you to reverse the
PLL configuration to confirm that the pixel rate is the value you've
computed based on link frequency (they aren't necessarily related). Do
the frame rate calculations work using width + HBLANK, height +
VBLANK, and pixel rate?
The datasheet claims the sensor supports 2688x1520 @ 90 fps, so
something doesn't hold true between 4 data lanes at 500MHz/1Gbit/s per
lane when your default hts/vts is 2688x1554 and it only gives
28.73fps.

I have seen modes in sensors where the HTS register is in units of 2
pixels, so what range of HTS (and VTS) values actually works on this
sensor? (I don't see it documented, but I'm not surprised).

[1] https://cdn.hackaday.io/files/19354828041536/OV4689-OmniVision.pdf

> (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
> gain is not linear across that range. Instead, it is piecewise linear
> (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
> 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
> with more linear segments in between. Rockchip's camera engine code
> chooses one of the above segments depenging on the desired gain
> value. The question is, how should we proceed keeping in mind
> libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
> libcamera will do the mapping, or the driver will do the mapping
> itself and expose some logical gain units not tied to the actual gain
> register value? Meanwhile, this driver conservatively exposes only
> 0x0-0xf8 gain register range.

The datasheet linked above says "for the gain formula, please contact
your local OmniVision FAE" :-(
I would assume that the range is from 1x rather than 0x - people
rarely want a totally black image that 0x would give. Or is it ranges
of 1x - 2x, 2x - 4x, 4x - 8x, and 8x - 16x?

Other sensors expose the full range of the register via
V4L2_CID_ANALOGUE_GAIN, and require userspace (mainly libcamera now)
to know how to convert a gain into the register value. If the gain
range goes up to x16, then exposing that would be useful. I'd advocate
just exposing the full range of 0x000 - 0x7ff, as then you can have
the accuracy of 256 values between x1 to x2, but also the full range.

I might see if I can pick up one of these sensors and see if I can get
it running on a Raspberry Pi. Thanks for trying to upstream this -
it's nice to have such a range of sensor drivers to choose from.

  Dave

> [1] https://github.com/rockchip-linux/kernel/blob/develop-4.19/drivers/media/i2c/ov4689.c
>
> changes in v2:
> - bindings: reword descriptions
> - bindings: move clock description to clocks property
> - bindings: add data-lanes and link-frequencies properties to port
> - driver: validate media bus configuration when probing
>
> Mikhail Rudenko (2):
>   media: dt-bindings: media: i2c: document OV4689 DT bindings
>   media: i2c: add support for ov4689
>
>  .../bindings/media/i2c/ovti,ov4689.yaml       | 141 +++
>  MAINTAINERS                                   |   8 +
>  drivers/media/i2c/Kconfig                     |  14 +
>  drivers/media/i2c/Makefile                    |   1 +
>  drivers/media/i2c/ov4689.c                    | 951 ++++++++++++++++++
>  5 files changed, 1115 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>  create mode 100644 drivers/media/i2c/ov4689.c
>
> --
> 2.37.3

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-11 20:01 ` [PATCH v2 2/2] media: i2c: add support for ov4689 Mikhail Rudenko
  2022-09-11 22:52   ` kernel test robot
  2022-09-12 10:56   ` Krzysztof Kozlowski
@ 2022-09-14 15:51   ` Tommaso Merciai
  2022-09-15 20:50     ` Mikhail Rudenko
  2022-09-22  9:53   ` Sakari Ailus
  2022-09-22 10:54   ` Dave Stevenson
  4 siblings, 1 reply; 41+ messages in thread
From: Tommaso Merciai @ 2022-09-14 15:51 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail,
I do a first round on reviewing your driver :)

On Sun, Sep 11, 2022 at 11:01:35PM +0300, Mikhail Rudenko wrote:
> Add a V4L2 sub-device driver for OmniVision OV4689 image sensor. This
> is a 4 Mpx image sensor using the I2C bus for control and the CSI-2
> bus for data.
> 
> This driver supports following features:
> - manual exposure and analog gain control support
> - test pattern support
> - media controller support
> - runtime PM support
> - support following resolutions:
>   + 2688x1520 at 30 fps
> 
> The driver provides all mandatory V4L2 controls for compatibility with
> libcamera. The sensor supports 1/2/4-lane CSI-2 modes, but the driver
> implements 4 lane mode only at this moment.
> 
> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
> ---
>  MAINTAINERS                |   1 +
>  drivers/media/i2c/Kconfig  |  14 +
>  drivers/media/i2c/Makefile |   1 +
>  drivers/media/i2c/ov4689.c | 951 +++++++++++++++++++++++++++++++++++++
>  4 files changed, 967 insertions(+)
>  create mode 100644 drivers/media/i2c/ov4689.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 63c4844f26e6..1857f3864e1b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14529,6 +14529,7 @@ L:	linux-media@vger.kernel.org
>  S:	Maintained
>  T:	git git://linuxtv.org/media_tree.git
>  F:	Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> +F:	drivers/media/i2c/ov5647.c
>  
>  OMNIVISION OV5640 SENSOR DRIVER
>  M:	Steve Longerbeam <slongerbeam@gmail.com>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index fae2baabb773..4993e1ae2ea8 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -429,6 +429,20 @@ config VIDEO_OV2740
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ov2740.
>  
> +config VIDEO_OV4689
> +	tristate "OmniVision OV4689 sensor support"
> +	depends on OF
> +	depends on GPIOLIB && VIDEO_DEV && I2C
> +	select MEDIA_CONTROLLER
> +	select VIDEO_V4L2_SUBDEV_API
> +	select V4L2_FWNODE
> +	help
> +	  This is a Video4Linux2 sensor-level driver for the OmniVision
> +	  OV4689 camera.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called ov4689.
> +
>  config VIDEO_OV5640
>  	tristate "OmniVision OV5640 sensor support"
>  	depends on OF
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 3e1696963e7f..7446c0a1eed0 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
>  obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
>  obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
>  obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
> +obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
>  obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>  obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
>  obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
> diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c
> new file mode 100644
> index 000000000000..9f05e812acf8
> --- /dev/null
> +++ b/drivers/media/i2c/ov4689.c
> @@ -0,0 +1,951 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ov4689 driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-fwnode.h>
> +
> +#define CHIP_ID				0x004688
> +#define OV4689_REG_CHIP_ID		0x300a
> +
> +#define OV4689_XVCLK_FREQ		24000000
> +
> +#define OV4689_REG_CTRL_MODE		0x0100
> +#define OV4689_MODE_SW_STANDBY		0x0
> +#define OV4689_MODE_STREAMING		BIT(0)
> +
> +#define OV4689_REG_EXPOSURE		0x3500
> +#define OV4689_EXPOSURE_MIN		4
> +#define OV4689_EXPOSURE_STEP		1
> +#define OV4689_VTS_MAX			0x7fff
> +
> +#define OV4689_REG_GAIN_H		0x3508
> +#define OV4689_REG_GAIN_L		0x3509
> +#define OV4689_GAIN_H_MASK		0x07
> +#define OV4689_GAIN_H_SHIFT		8
> +#define OV4689_GAIN_L_MASK		0xff
> +#define OV4689_GAIN_MIN			0x10
> +#define OV4689_GAIN_MAX			0xf8
> +#define OV4689_GAIN_STEP		1
> +#define OV4689_GAIN_DEFAULT		0x10
> +
> +#define OV4689_REG_TEST_PATTERN		0x5040
> +#define OV4689_TEST_PATTERN_ENABLE	0x80
> +#define OV4689_TEST_PATTERN_DISABLE	0x0
> +
> +#define OV4689_REG_VTS			0x380e
> +
> +#define REG_NULL			0xFFFF
> +
> +#define OV4689_REG_VALUE_08BIT		1
> +#define OV4689_REG_VALUE_16BIT		2
> +#define OV4689_REG_VALUE_24BIT		3
> +
> +#define OV4689_LANES			4
> +#define OV4689_BITS_PER_SAMPLE		10
> +
> +static const char *const ov4689_supply_names[] = {
> +	"avdd", /* Analog power */
> +	"dovdd", /* Digital I/O power */
> +	"dvdd", /* Digital core power */
> +};
> +
> +#define OV4689_NUM_SUPPLIES ARRAY_SIZE(ov4689_supply_names)
> +
> +struct regval {
> +	u16 addr;
> +	u8 val;
> +};

What about use ov4689_mode_id? I think could be usefull for the future:

+ enum ov4689_mode_id {
+	OV4689_MODE_2688_1520 = 0,
+	OV4689_NUM_MODES,
+	};


> +
> +struct ov4689_mode {
+	enum ov4689_mode_id id;
> +	u32 width;
> +	u32 height;
> +	u32 max_fps;
> +	u32 hts_def;
> +	u32 vts_def;
> +	u32 exp_def;
> +	const struct regval *reg_list;
> +};
> +
> +struct ov4689 {
> +	struct i2c_client *client;
> +	struct clk *xvclk;
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *pwdn_gpio;
> +	struct regulator_bulk_data supplies[OV4689_NUM_SUPPLIES];
> +
> +	struct v4l2_subdev subdev;
> +	struct media_pad pad;
> +
> +	struct mutex mutex; /* lock to protect streaming, ctrls and cur_mode */
> +	bool streaming;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_ctrl *exposure;
> +	struct v4l2_ctrl *anal_gain;
> +	struct v4l2_ctrl *digi_gain;
> +	struct v4l2_ctrl *hblank;
> +	struct v4l2_ctrl *vblank;
> +	struct v4l2_ctrl *test_pattern;
> +
> +	const struct ov4689_mode *cur_mode;
> +};
> +
> +#define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
> +
> +/*
> + * Xclk 24Mhz
> + */
> +static const struct regval ov4689_global_regs[] = {
> +	{ REG_NULL, 0x00 },
> +};
> +
> +/*
> + * Xclk 24Mhz
> + * max_framerate 30fps
> + * mipi_datarate per lane 1008Mbps
> + */
> +static const struct regval ov4689_2688x1520_regs[] = {
> +	{0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
> +	{0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
> +	{0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
> +	{0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
> +	{0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
> +	{0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
> +	{0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
> +	{0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
> +	{0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
> +	{0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
> +	{0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
> +	{0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
> +	{0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
> +	{0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
> +	{0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
> +	{0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
> +	{0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
> +	{0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
> +	{0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
> +	{0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
> +	{0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
> +	{0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
> +	{0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
> +	{0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
> +	{0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
> +	{0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
> +	{0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
> +	{0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
> +	{0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
> +	{0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
> +	{0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
> +	{0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
> +	{0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
> +	{0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
> +	{0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
> +	{0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
> +	{0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
> +	{0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
> +	{0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
> +	{0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
> +	{0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
> +	{0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
> +	{0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
> +	{0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
> +	{0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
> +	{0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
> +	{0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
> +	{0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
> +	{0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
> +	{0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
> +	{0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
> +	{0x380d, 0x80}, {0x380e, 0x06}, {0x380f, 0x12},
> +	{0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
> +	{0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
> +	{0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
> +	{0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
> +	{0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
> +	{0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
> +	{0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
> +	{0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
> +	{0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
> +	{0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
> +	{0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
> +	{0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
> +	{0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
> +	{0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
> +	{0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
> +	{0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
> +	{0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
> +	{0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
> +	{0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
> +	{0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
> +	{0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
> +	{0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
> +	{0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
> +	{0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
> +	{0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
> +	{0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
> +	{0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
> +	{0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
> +	{0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
> +	{0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
> +	{0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
> +	{0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
> +	{REG_NULL, 0x00},
> +};
> +
> +static const struct ov4689_mode supported_modes[] = {
> +	{
+		.id = OV4689_MODE_2688_1520,
> +		.width = 2688,
> +		.height = 1520,
> +		.max_fps = 30,
> +		.exp_def = 0x0600,
> +		.hts_def = 0x0a80,
> +		.vts_def = 0x0612,
> +		.reg_list = ov4689_2688x1520_regs,
> +	},
> +};
> +
> +#define OV4689_LINK_FREQ_500MHZ 500000000
> +static const s64 link_freq_menu_items[] = { OV4689_LINK_FREQ_500MHZ };
> +
> +static const char *const ov4689_test_pattern_menu[] = {
> +	"Disabled",
> +	"Vertical Color Bar Type 1",
> +	"Vertical Color Bar Type 2",
> +	"Vertical Color Bar Type 3",
> +	"Vertical Color Bar Type 4"
> +};
> +
> +/* Write registers up to 4 at a time */
> +static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len,
> +			    u32 val)
> +{
> +	u32 buf_i, val_i;
> +	__be32 val_be;
> +	u8 *val_p;
> +	u8 buf[6];
> +
> +	if (len > 4)
> +		return -EINVAL;
> +
> +	buf[0] = reg >> 8;
> +	buf[1] = reg & 0xff;
> +
> +	val_be = cpu_to_be32(val);
> +	val_p = (u8 *)&val_be;
> +	buf_i = 2;
> +	val_i = 4 - len;
> +
> +	while (val_i < 4)
> +		buf[buf_i++] = val_p[val_i++];
> +
> +	if (i2c_master_send(client, buf, len + 2) != len + 2)
> +		return -EIO;
> +
> +	return 0;
> +}
> +
> +static int ov4689_write_array(struct i2c_client *client,
> +			      const struct regval *regs)
> +{
> +	int ret = 0;
> +	u32 i;
> +
> +	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
> +		ret = ov4689_write_reg(client, regs[i].addr,
> +				       OV4689_REG_VALUE_08BIT, regs[i].val);
> +
> +	return ret;
> +}
> +
> +/* Read registers up to 4 at a time */
> +static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
> +			   u32 *val)
> +{
> +	__be16 reg_addr_be = cpu_to_be16(reg);
> +	struct i2c_msg msgs[2];
> +	__be32 data_be = 0;
> +	u8 *data_be_p;
> +	int ret;
> +
> +	if (len > 4 || !len)
> +		return -EINVAL;
> +
> +	data_be_p = (u8 *)&data_be;
> +	/* Write register address */
> +	msgs[0].addr = client->addr;
> +	msgs[0].flags = 0;
> +	msgs[0].len = 2;
> +	msgs[0].buf = (u8 *)&reg_addr_be;
> +
> +	/* Read data from register */
> +	msgs[1].addr = client->addr;
> +	msgs[1].flags = I2C_M_RD;
> +	msgs[1].len = len;
> +	msgs[1].buf = &data_be_p[4 - len];
> +
> +	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> +	if (ret != ARRAY_SIZE(msgs))
> +		return -EIO;
> +
> +	*val = be32_to_cpu(data_be);
> +
> +	return 0;
> +}
> +
> +static void ov4689_fill_fmt(const struct ov4689_mode *mode,
> +			    struct v4l2_mbus_framefmt *fmt)
> +{
> +	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +	fmt->width = mode->width;
> +	fmt->height = mode->height;
> +	fmt->field = V4L2_FIELD_NONE;
> +}
> +
> +static int ov4689_set_fmt(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_state *sd_state,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +	/* only one mode supported for now */
> +	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
> +
> +	return 0;
> +}
> +
> +static int ov4689_get_fmt(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_state *sd_state,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +	/* only one mode supported for now */
> +	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
> +
> +	return 0;
> +}
> +
> +static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_state *sd_state,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	if (code->index != 0)
> +		return -EINVAL;
> +	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +
> +	return 0;
> +}
> +
> +static int ov4689_enum_frame_sizes(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_state *sd_state,
> +				   struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	if (fse->index >= ARRAY_SIZE(supported_modes))
> +		return -EINVAL;
> +
> +	if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
> +		return -EINVAL;
> +
> +	fse->min_width = supported_modes[fse->index].width;
> +	fse->max_width = supported_modes[fse->index].width;
> +	fse->max_height = supported_modes[fse->index].height;
> +	fse->min_height = supported_modes[fse->index].height;
> +
> +	return 0;
> +}
> +
> +static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
> +{
> +	u32 val;
> +
> +	if (pattern)
> +		val = (pattern - 1) | OV4689_TEST_PATTERN_ENABLE;
> +	else
> +		val = OV4689_TEST_PATTERN_DISABLE;
> +
> +	return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN,
> +				OV4689_REG_VALUE_08BIT, val);
> +}
> +
> +static int ov4689_get_selection(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_state *state,
> +				struct v4l2_subdev_selection *sel)
> +{
> +	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
> +		return -EINVAL;
> +
> +	switch (sel->target) {
> +	case V4L2_SEL_TGT_CROP_BOUNDS:
> +		sel->r.top = 0;
> +		sel->r.left = 0;
> +		sel->r.width = 2720;
> +		sel->r.height = 1536;
> +		return 0;
> +	case V4L2_SEL_TGT_CROP:
> +	case V4L2_SEL_TGT_CROP_DEFAULT:
> +		sel->r.top = 8;
> +		sel->r.left = 16;
> +		sel->r.width = 2688;
> +		sel->r.height = 1520;
> +		return 0;
> +	}
> +	return -EINVAL;
> +}
> +
> +static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
> +{
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +	struct i2c_client *client = ov4689->client;
> +	int ret = 0;
> +
> +	mutex_lock(&ov4689->mutex);
> +
> +	on = !!on;
> +	if (on == ov4689->streaming)
> +		goto unlock_and_return;
> +
> +	if (on) {
> +		ret = pm_runtime_resume_and_get(&client->dev);
> +		if (ret < 0)
> +			goto unlock_and_return;
> +
> +		ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
> +		if (ret) {
> +			pm_runtime_put(&client->dev);
> +			goto unlock_and_return;
> +		}
> +
> +		ret = ov4689_write_array(ov4689->client,
> +					 ov4689->cur_mode->reg_list);
> +		if (ret) {
> +			pm_runtime_put(&client->dev);
> +			goto unlock_and_return;
> +		}
> +
> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
> +				       OV4689_REG_VALUE_08BIT,
> +				       OV4689_MODE_STREAMING);
> +		if (ret) {
> +			pm_runtime_put(&client->dev);
> +			goto unlock_and_return;
> +		}
> +	} else {
> +		ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
> +				 OV4689_REG_VALUE_08BIT,
> +				 OV4689_MODE_SW_STANDBY);
> +		pm_runtime_put(&client->dev);
> +	}
> +
> +	ov4689->streaming = on;
> +
> +unlock_and_return:
> +	mutex_unlock(&ov4689->mutex);
> +
> +	return ret;
> +}
> +
> +/* Calculate the delay in us by clock rate and clock cycles */
> +static inline u32 ov4689_cal_delay(u32 cycles)
> +{
> +	return DIV_ROUND_UP(cycles, OV4689_XVCLK_FREQ / 1000 / 1000);
> +}
> +
> +static int __ov4689_power_on(struct ov4689 *ov4689)

Just a doubt on this name function. Why __ ? Is this name reserved for?

> +{
> +	struct device *dev = &ov4689->client->dev;
> +	u32 delay_us;
> +	int ret;
> +
> +	ret = clk_prepare_enable(ov4689->xvclk);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable xvclk\n");
> +		return ret;
> +	}
> +
> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
> +
> +	ret = regulator_bulk_enable(OV4689_NUM_SUPPLIES, ov4689->supplies);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable regulators\n");
> +		goto disable_clk;
> +	}
> +
> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
> +	usleep_range(500, 1000);
> +	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 0);
> +
> +	/* 8192 cycles prior to first SCCB transaction */
> +	delay_us = ov4689_cal_delay(8192);
> +	usleep_range(delay_us, delay_us * 2);
> +
> +	return 0;
> +
> +disable_clk:
> +	clk_disable_unprepare(ov4689->xvclk);
> +
> +	return ret;
> +}
> +
> +static void __ov4689_power_off(struct ov4689 *ov4689)
> +{
> +	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 1);
> +	clk_disable_unprepare(ov4689->xvclk);
> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
> +	regulator_bulk_disable(OV4689_NUM_SUPPLIES, ov4689->supplies);
> +}
> +
> +static int __maybe_unused ov4689_runtime_resume(struct device *dev)
> +{
> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +	return __ov4689_power_on(ov4689);
> +}
> +
> +static int __maybe_unused ov4689_runtime_suspend(struct device *dev)
> +{
> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +	__ov4689_power_off(ov4689);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +	struct v4l2_mbus_framefmt *try_fmt;
> +
> +	mutex_lock(&ov4689->mutex);
> +
> +	try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0);
> +	/* Initialize try_fmt */
> +	ov4689_fill_fmt(&supported_modes[0], try_fmt);

In this way instead of use magic number we can use this:

	ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt);

> +
> +	mutex_unlock(&ov4689->mutex);
> +
> +	return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops ov4689_pm_ops = {
> +	SET_RUNTIME_PM_OPS(ov4689_runtime_suspend, ov4689_runtime_resume, NULL)
> +};
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
> +	.open = ov4689_open,
> +};
> +#endif
> +
> +static const struct v4l2_subdev_video_ops ov4689_video_ops = {
> +	.s_stream = ov4689_s_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
> +	.enum_mbus_code = ov4689_enum_mbus_code,
> +	.enum_frame_size = ov4689_enum_frame_sizes,
> +	.get_fmt = ov4689_get_fmt,
> +	.set_fmt = ov4689_set_fmt,
> +	.get_selection = ov4689_get_selection,
> +};
> +
> +static const struct v4l2_subdev_ops ov4689_subdev_ops = {
> +	.video = &ov4689_video_ops,
> +	.pad = &ov4689_pad_ops,
> +};
> +
> +static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct ov4689 *ov4689 =
> +		container_of(ctrl->handler, struct ov4689, ctrl_handler);
> +	struct i2c_client *client = ov4689->client;
> +	s64 max_expo;
> +	int ret;
> +
> +	/* Propagate change of current control to all related controls */
> +	switch (ctrl->id) {
> +	case V4L2_CID_VBLANK:
> +		/* Update max exposure while meeting expected vblanking */
> +		max_expo = ov4689->cur_mode->height + ctrl->val - 4;
> +		__v4l2_ctrl_modify_range(ov4689->exposure,
> +					 ov4689->exposure->minimum, max_expo,
> +					 ov4689->exposure->step,
> +					 ov4689->exposure->default_value);
> +		break;
> +	}
> +
> +	if (!pm_runtime_get_if_in_use(&client->dev))
> +		return 0;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_EXPOSURE:
> +		/* 4 least significant bits of expsoure are fractional part */
> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE,
> +				       OV4689_REG_VALUE_24BIT, ctrl->val << 4);
> +		break;
> +	case V4L2_CID_ANALOGUE_GAIN:
> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H,
> +				       OV4689_REG_VALUE_08BIT,
> +				       (ctrl->val >> OV4689_GAIN_H_SHIFT) &
> +					       OV4689_GAIN_H_MASK);
> +		ret |= ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L,
> +					OV4689_REG_VALUE_08BIT,
> +					ctrl->val & OV4689_GAIN_L_MASK);
> +		break;
> +	case V4L2_CID_VBLANK:
> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS,
> +				       OV4689_REG_VALUE_16BIT,
> +				       ctrl->val + ov4689->cur_mode->height);
> +		break;
> +	case V4L2_CID_TEST_PATTERN:
> +		ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
> +		break;
> +	default:
> +		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
> +			 __func__, ctrl->id, ctrl->val);
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	pm_runtime_put(&client->dev);
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
> +	.s_ctrl = ov4689_set_ctrl,
> +};
> +
> +static int ov4689_initialize_controls(struct ov4689 *ov4689)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&ov4689->subdev);
> +	struct v4l2_fwnode_device_properties props;
> +	struct v4l2_ctrl_handler *handler;
> +	const struct ov4689_mode *mode;
> +	s64 exposure_max, vblank_def;
> +	struct v4l2_ctrl *ctrl;
> +	u32 h_blank, pixel_rate;
> +	int ret;
> +
> +	handler = &ov4689->ctrl_handler;
> +	mode = ov4689->cur_mode;
> +	ret = v4l2_ctrl_handler_init(handler, 10);
> +	if (ret)
> +		return ret;
> +	handler->lock = &ov4689->mutex;
> +
> +	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
> +				      link_freq_menu_items);
> +	if (ctrl)
> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	pixel_rate = (link_freq_menu_items[0] * 2 * OV4689_LANES) /
> +		     OV4689_BITS_PER_SAMPLE;
> +	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, pixel_rate, 1,
> +			  pixel_rate);
> +
> +	h_blank = mode->hts_def - mode->width;
> +	ov4689->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
> +					   h_blank, h_blank, 1, h_blank);
> +	if (ov4689->hblank)
> +		ov4689->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	vblank_def = mode->vts_def - mode->height;
> +	ov4689->vblank =
> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
> +				  vblank_def, OV4689_VTS_MAX - mode->height, 1,
> +				  vblank_def);
> +
> +	exposure_max = mode->vts_def - 4;
> +	ov4689->exposure =
> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_EXPOSURE,
> +				  OV4689_EXPOSURE_MIN, exposure_max,
> +				  OV4689_EXPOSURE_STEP, mode->exp_def);
> +
> +	ov4689->anal_gain =
> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops,
> +				  V4L2_CID_ANALOGUE_GAIN, OV4689_GAIN_MIN,
> +				  OV4689_GAIN_MAX, OV4689_GAIN_STEP,
> +				  OV4689_GAIN_DEFAULT);
> +
> +	ov4689->test_pattern =
> +		v4l2_ctrl_new_std_menu_items(handler, &ov4689_ctrl_ops,
> +					     V4L2_CID_TEST_PATTERN,
> +					     ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
> +					     0, 0, ov4689_test_pattern_menu);
> +
> +	if (handler->error) {
> +		ret = handler->error;
> +		dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n",
> +			ret);
> +		goto err_free_handler;
> +	}
> +
> +	ret = v4l2_fwnode_device_parse(&client->dev, &props);
> +	if (ret)
> +		goto err_free_handler;
> +
> +	ret = v4l2_ctrl_new_fwnode_properties(handler, &ov4689_ctrl_ops,
> +					      &props);
> +	if (ret)
> +		goto err_free_handler;
> +
> +	ov4689->subdev.ctrl_handler = handler;
> +
> +	return 0;
> +
> +err_free_handler:
> +	v4l2_ctrl_handler_free(handler);
> +
> +	return ret;
> +}
> +
> +static int ov4689_check_sensor_id(struct ov4689 *ov4689,
> +				  struct i2c_client *client)
> +{
> +	struct device *dev = &ov4689->client->dev;
> +	u32 id = 0;
> +	int ret;
> +
> +	ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID,
> +			      OV4689_REG_VALUE_16BIT, &id);
> +	if (id != CHIP_ID) {
> +		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
> +		return -ENODEV;
> +	}
> +
> +	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
> +
> +	return 0;
> +}
> +
> +static int ov4689_configure_regulators(struct ov4689 *ov4689)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < OV4689_NUM_SUPPLIES; i++)
> +		ov4689->supplies[i].supply = ov4689_supply_names[i];
> +
> +	return devm_regulator_bulk_get(&ov4689->client->dev,
> +				       OV4689_NUM_SUPPLIES, ov4689->supplies);
> +}
> +
> +static int ov4689_check_hwcfg(struct device *dev)
> +{
> +	struct fwnode_handle *fwnode = dev_fwnode(dev);
> +	struct v4l2_fwnode_endpoint bus_cfg = {
> +		.bus_type = V4L2_MBUS_CSI2_DPHY,
> +	};
> +	struct fwnode_handle *endpoint;
> +	unsigned int i;
> +	int ret;
> +
> +	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
> +	if (!endpoint)
> +		return -EPROBE_DEFER;
> +
> +	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
> +	fwnode_handle_put(endpoint);
> +	if (ret)
> +		return ret;
> +
> +	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
> +		dev_err(dev, "only a 4-lane CSI2 config is supported");
> +		ret = -EINVAL;
> +		goto out_free_bus_cfg;
> +	}
> +
> +	if (!bus_cfg.nr_of_link_frequencies) {
> +		dev_err(dev, "no link frequencies defined\n");
> +		ret = -EINVAL;
> +		goto out_free_bus_cfg;
> +	}
> +
> +	for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
> +		if (bus_cfg.link_frequencies[i] == OV4689_LINK_FREQ_500MHZ)
> +			break;
> +
> +	if (i == bus_cfg.nr_of_link_frequencies) {
> +		dev_err(dev, "supported link freq %ull not found\n",
> +			OV4689_LINK_FREQ_500MHZ);
> +		ret = -EINVAL;
> +		goto out_free_bus_cfg;
> +	}
> +
> +out_free_bus_cfg:
> +	v4l2_fwnode_endpoint_free(&bus_cfg);
> +
> +	return ret;
> +}
> +
> +static int ov4689_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)

We are sure that we need need i2c_device_id *id?

> +{
> +	struct device *dev = &client->dev;
> +	struct v4l2_subdev *sd;
> +	struct ov4689 *ov4689;
> +	int ret;
> +
> +	ret = ov4689_check_hwcfg(dev);
> +	if (ret)
> +		return ret;
> +
> +	ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
> +	if (!ov4689)
> +		return -ENOMEM;
> +
> +	ov4689->client = client;
> +	ov4689->cur_mode = &supported_modes[0];

Here aswell we can use:
	ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520];

> +
> +	ov4689->xvclk = devm_clk_get(dev, "xvclk");
> +	if (IS_ERR(ov4689->xvclk)) {
> +		dev_err(dev, "Failed to get xvclk\n");
> +		return -EINVAL;
> +	}

^ I think is better to use devm_clk_get_optional instead of clck_get.
clck_get can fail in CPU's that use ACPI

> +
> +	ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
> +		return ret;
> +	}
> +	if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
> +		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");


What do you think about?
Thanks.

Regards,
Tommaso

> +
> +	ov4689->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(ov4689->reset_gpio)) {
> +		dev_err(dev, "Failed to get reset-gpios\n");
> +		return -EINVAL;
> +	}
> +
> +	ov4689->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
> +	if (IS_ERR(ov4689->pwdn_gpio)) {
> +		dev_err(dev, "Failed to get pwdn-gpios\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = ov4689_configure_regulators(ov4689);
> +	if (ret) {
> +		dev_err(dev, "Failed to get power regulators\n");
> +		return ret;
> +	}
> +
> +	mutex_init(&ov4689->mutex);
> +
> +	sd = &ov4689->subdev;
> +	v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops);
> +	ret = ov4689_initialize_controls(ov4689);
> +	if (ret)
> +		goto err_destroy_mutex;
> +
> +	ret = __ov4689_power_on(ov4689);
> +	if (ret)
> +		goto err_free_handler;
> +
> +	ret = ov4689_check_sensor_id(ov4689, client);
> +	if (ret)
> +		goto err_power_off;
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +	sd->internal_ops = &ov4689_internal_ops;
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +#endif
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +	ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
> +	if (ret < 0)
> +		goto err_power_off;
> +#endif
> +
> +	ret = v4l2_async_register_subdev_sensor(sd);
> +	if (ret) {
> +		dev_err(dev, "v4l2 async register subdev failed\n");
> +		goto err_clean_entity;
> +	}
> +
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
> +	pm_runtime_idle(dev);
> +
> +	return 0;
> +
> +err_clean_entity:
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	media_entity_cleanup(&sd->entity);
> +#endif
> +err_power_off:
> +	__ov4689_power_off(ov4689);
> +err_free_handler:
> +	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
> +err_destroy_mutex:
> +	mutex_destroy(&ov4689->mutex);
> +
> +	return ret;
> +}
> +
> +static int ov4689_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +	v4l2_async_unregister_subdev(sd);
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	media_entity_cleanup(&sd->entity);
> +#endif
> +	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
> +	mutex_destroy(&ov4689->mutex);
> +
> +	pm_runtime_disable(&client->dev);
> +	if (!pm_runtime_status_suspended(&client->dev))
> +		__ov4689_power_off(ov4689);
> +	pm_runtime_set_suspended(&client->dev);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ov4689_id[] = {
> +	{ "ov4689", 0 },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(i2c, ov4689_id);
> +
> +static const struct of_device_id ov4689_of_match[] = {
> +	{ .compatible = "ovti,ov4689" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, ov4689_of_match);
> +
> +static struct i2c_driver ov4689_i2c_driver = {
> +	.driver = {
> +		.name = "ov4689",
> +		.pm = &ov4689_pm_ops,
> +		.of_match_table = of_match_ptr(ov4689_of_match),
> +	},
> +	.probe = ov4689_probe,
> +	.remove	= ov4689_remove,
> +	.id_table = ov4689_id,
> +};
> +
> +module_i2c_driver(ov4689_i2c_driver);
> +
> +MODULE_DESCRIPTION("OmniVision ov4689 sensor driver");
> +MODULE_LICENSE("GPL");
> -- 
> 2.37.3
> 

-- 
Tommaso Merciai
Embedded Linux Engineer
tommaso.merciai@amarulasolutions.com
__________________________________

Amarula Solutions SRL
Via Le Canevare 30, 31100 Treviso, Veneto, IT
T. +39 042 243 5310
info@amarulasolutions.com
www.amarulasolutions.com

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

* Re: [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings
  2022-09-12 10:55   ` Krzysztof Kozlowski
@ 2022-09-15 12:16     ` Mikhail Rudenko
  2022-09-16  9:42       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-15 12:16 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Christian Hemp, Arec Kao, Arnd Bergmann, Laurent Pinchart,
	Daniel Scally, Jimmy Su, linux-media, devicetree, linux-kernel

Hi Krzysztof,

On 2022-09-12 at 12:55 +02, Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> wrote:
> On 11/09/2022 22:01, Mikhail Rudenko wrote:
>> Add device-tree binding documentation for OV4689 image sensor driver,
>> and the relevant MAINTAINERS entries.
>>
>> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
>
> Too many "media" prefixes in the subject.

I see, will drop the first "media:" in v3.

> Also you duplicated dt
> bindings as prefix and commit msg (skip the latter).

Just to be clear, do you mean dropping "device-tree binding" phrase from
the commit message?

>> ---
>>  .../bindings/media/i2c/ovti,ov4689.yaml       | 141 ++++++++++++++++++
>>  MAINTAINERS                                   |   7 +
>>  2 files changed, 148 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>> new file mode 100644
>> index 000000000000..376330b5572a
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>> @@ -0,0 +1,141 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/media/i2c/ovti,ov4689.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Omnivision OV4689 CMOS
>> +
>> +maintainers:
>> +  - Mikhail Rudenko <mike.rudenko@gmail.com>
>> +
>> +description: |
>> +  The Omnivision OV4689 is a high performance, 1/3-inch, 4 megapixel
>> +  image sensor. Ihis chip supports high frame rate speeds up to 90 fps
>> +  at 2688x1520 resolution. It is programmable through an I2C
>> +  interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
>> +  connection.
>> +
>> +allOf:
>> +  - $ref: /schemas/media/video-interface-devices.yaml#
>> +
>> +properties:
>> +  compatible:
>> +    const: ovti,ov4689
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  clocks:
>> +    description:
>> +      External clock (XVCLK) for the sensor, 6-64 MHz
>> +    maxItems: 1
>> +
>> +  clock-names: true
>
> This has to be strictly defined - which name you expect.

Will fix in v3. Or maybe we should drop clock-names altogether and use
devm_clk_get(&client->dev, NULL) in the driver instead (I've seen this
approach in some existing drivers)?

>> +
>> +  dovdd-supply:
>> +    description:
>> +      Digital I/O voltage supply, 1.7-3.0 V
>> +
>> +  avdd-supply:
>> +    description:
>> +      Analog voltage supply, 2.6-3.0 V
>> +
>> +  dvdd-supply:
>> +    description:
>> +      Digital core voltage supply, 1.1-1.3 V
>> +
>> +  powerdown-gpios:
>> +    maxItems: 1
>
> You can skip here maxItems - it is defined by gpio-consumer-common.

Ack, will fix in v3. Does this also apply to reset-gpios?

>> +    description:
>> +      GPIO connected to the powerdown pin (active low)
>> +
>> +  reset-gpios:
>> +    maxItems: 1
>> +    description:
>> +      GPIO connected to the reset pin (active low)
>> +
>
> Best regards,
> Krzysztof

Thanks for review,

--
Best regards,
Mikhail Rudenko

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

* Re: [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings
  2022-09-13 14:05   ` Tommaso Merciai
@ 2022-09-15 20:11     ` Mikhail Rudenko
  2022-09-16 13:15       ` Tommaso Merciai
  0 siblings, 1 reply; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-15 20:11 UTC (permalink / raw)
  To: Tommaso Merciai
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Christian Hemp, Arec Kao, Arnd Bergmann, Laurent Pinchart,
	Daniel Scally, Jimmy Su, linux-media, devicetree, linux-kernel


Hi Tommaso,

On 2022-09-13 at 16:05 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
> Hi Mikhail,
>
> On Sun, Sep 11, 2022 at 11:01:34PM +0300, Mikhail Rudenko wrote:
>> Add device-tree binding documentation for OV4689 image sensor driver,
>> and the relevant MAINTAINERS entries.
>>
>> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
>> ---
>>  .../bindings/media/i2c/ovti,ov4689.yaml       | 141 ++++++++++++++++++
>>  MAINTAINERS                                   |   7 +
>>  2 files changed, 148 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>> new file mode 100644
>> index 000000000000..376330b5572a
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>> @@ -0,0 +1,141 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/media/i2c/ovti,ov4689.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Omnivision OV4689 CMOS
>> +
>> +maintainers:
>> +  - Mikhail Rudenko <mike.rudenko@gmail.com>
>> +
>> +description: |
>> +  The Omnivision OV4689 is a high performance, 1/3-inch, 4 megapixel
>> +  image sensor. Ihis chip supports high frame rate speeds up to 90 fps
>> +  at 2688x1520 resolution. It is programmable through an I2C
>> +  interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
>> +  connection.
>> +
>> +allOf:
>> +  - $ref: /schemas/media/video-interface-devices.yaml#
>> +
>> +properties:
>> +  compatible:
>> +    const: ovti,ov4689
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  clocks:
>> +    description:
>> +      External clock (XVCLK) for the sensor, 6-64 MHz
>> +    maxItems: 1
>> +
>> +  clock-names: true
>> +
>> +  dovdd-supply:
>> +    description:
>> +      Digital I/O voltage supply, 1.7-3.0 V
>> +
>> +  avdd-supply:
>> +    description:
>> +      Analog voltage supply, 2.6-3.0 V
>> +
>> +  dvdd-supply:
>> +    description:
>> +      Digital core voltage supply, 1.1-1.3 V
>> +
>> +  powerdown-gpios:
>> +    maxItems: 1
>> +    description:
>> +      GPIO connected to the powerdown pin (active low)
>> +
>> +  reset-gpios:
>> +    maxItems: 1
>> +    description:
>> +      GPIO connected to the reset pin (active low)
>> +
>> +  orientation: true
>> +
>> +  rotation: true
>> +
>> +  port:
>> +    $ref: /schemas/graph.yaml#/$defs/port-base
>> +    additionalProperties: false
>> +    description:
>> +      Output port node, single endpoint describing the CSI-2 transmitter
>> +
>> +    properties:
>> +      endpoint:
>> +        $ref: /schemas/media/video-interfaces.yaml#
>> +        unevaluatedProperties: false
>> +
>> +        properties:
>> +          data-lanes:
>> +            oneOf:
>> +              - items:
>> +                  - const: 1
>> +                  - const: 2
>> +                  - const: 3
>> +                  - const: 4
>> +              - items:
>> +                  - const: 1
>> +                  - const: 2
>> +              - items:
>> +                  - const: 1
>> +          link-frequencies: true
>> +
>> +        required:
>> +          - data-lanes
>> +          - link-frequencies
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - clocks
>> +  - clock-names
>> +  - dovdd-supply
>> +  - avdd-supply
>> +  - dvdd-supply
>> +  - powerdown-gpios
>> +  - reset-gpios
>> +  - port
>
> I think we don't need all of these entries as required.
> The only let me say "really" required are:
>
> - compatible
> - reg
> - clocks
> - port

Thanks for the review! I agree that the driver may be modified to work
without powerdown and reset gpios and they are not required for sensor
operation. On contrary, supplies are obviously required. Of course, linux
provides dummy regulators if supplies are missing from device tree, but
I though the intention was to document hardware, not implementation
details. What do think of this?

> Regards,
> Tommaso

--
Best regards,
Mikhail Rudenko

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-12 10:56   ` Krzysztof Kozlowski
@ 2022-09-15 20:40     ` Mikhail Rudenko
  2022-09-16  9:43       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-15 20:40 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel


Hi Krzysztof,

On 2022-09-12 at 12:56 +02, Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> wrote:
> On 11/09/2022 22:01, Mikhail Rudenko wrote:
>> +static const struct i2c_device_id ov4689_id[] = {
>> +	{ "ov4689", 0 },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(i2c, ov4689_id);
>> +
>> +static const struct of_device_id ov4689_of_match[] = {
>> +	{ .compatible = "ovti,ov4689" },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, ov4689_of_match);
>> +
>> +static struct i2c_driver ov4689_i2c_driver = {
>> +	.driver = {
>> +		.name = "ov4689",
>> +		.pm = &ov4689_pm_ops,
>> +		.of_match_table = of_match_ptr(ov4689_of_match),
>
> of_match_ptr is usually paired with maybe_unused, otherwise you will
> have compile test warnings.

I see. I think we could also use `#if IS_ENABLED(CONFIG_OF)` around
`ov4689_of_match` and the corresponding `MODULE_DEVICE_TABLE`. Is it
appropriate here?

>> +	},
>> +	.probe = ov4689_probe,
>> +	.remove	= ov4689_remove,
>> +	.id_table = ov4689_id,
>> +};
>> +
>> +module_i2c_driver(ov4689_i2c_driver);
>> +
>> +MODULE_DESCRIPTION("OmniVision ov4689 sensor driver");
>> +MODULE_LICENSE("GPL");
>
>
> Best regards,
> Krzysztof


--
Best regards,
Mikhail Rudenko

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-14 15:51   ` Tommaso Merciai
@ 2022-09-15 20:50     ` Mikhail Rudenko
  2022-09-16 13:34       ` Tommaso Merciai
  0 siblings, 1 reply; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-15 20:50 UTC (permalink / raw)
  To: Tommaso Merciai
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel


Hi Tommaso,

On 2022-09-14 at 17:51 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
> Hi Mikhail,
> I do a first round on reviewing your driver :)
>
> On Sun, Sep 11, 2022 at 11:01:35PM +0300, Mikhail Rudenko wrote:
>> Add a V4L2 sub-device driver for OmniVision OV4689 image sensor. This
>> is a 4 Mpx image sensor using the I2C bus for control and the CSI-2
>> bus for data.
>>
>> This driver supports following features:
>> - manual exposure and analog gain control support
>> - test pattern support
>> - media controller support
>> - runtime PM support
>> - support following resolutions:
>>   + 2688x1520 at 30 fps
>>
>> The driver provides all mandatory V4L2 controls for compatibility with
>> libcamera. The sensor supports 1/2/4-lane CSI-2 modes, but the driver
>> implements 4 lane mode only at this moment.
>>
>> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
>> ---
>>  MAINTAINERS                |   1 +
>>  drivers/media/i2c/Kconfig  |  14 +
>>  drivers/media/i2c/Makefile |   1 +
>>  drivers/media/i2c/ov4689.c | 951 +++++++++++++++++++++++++++++++++++++
>>  4 files changed, 967 insertions(+)
>>  create mode 100644 drivers/media/i2c/ov4689.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 63c4844f26e6..1857f3864e1b 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -14529,6 +14529,7 @@ L:	linux-media@vger.kernel.org
>>  S:	Maintained
>>  T:	git git://linuxtv.org/media_tree.git
>>  F:	Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>> +F:	drivers/media/i2c/ov5647.c
>>
>>  OMNIVISION OV5640 SENSOR DRIVER
>>  M:	Steve Longerbeam <slongerbeam@gmail.com>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index fae2baabb773..4993e1ae2ea8 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -429,6 +429,20 @@ config VIDEO_OV2740
>>  	  To compile this driver as a module, choose M here: the
>>  	  module will be called ov2740.
>>
>> +config VIDEO_OV4689
>> +	tristate "OmniVision OV4689 sensor support"
>> +	depends on OF
>> +	depends on GPIOLIB && VIDEO_DEV && I2C
>> +	select MEDIA_CONTROLLER
>> +	select VIDEO_V4L2_SUBDEV_API
>> +	select V4L2_FWNODE
>> +	help
>> +	  This is a Video4Linux2 sensor-level driver for the OmniVision
>> +	  OV4689 camera.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called ov4689.
>> +
>>  config VIDEO_OV5640
>>  	tristate "OmniVision OV5640 sensor support"
>>  	depends on OF
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index 3e1696963e7f..7446c0a1eed0 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
>>  obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
>>  obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
>>  obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
>> +obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
>>  obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>>  obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
>>  obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
>> diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c
>> new file mode 100644
>> index 000000000000..9f05e812acf8
>> --- /dev/null
>> +++ b/drivers/media/i2c/ov4689.c
>> @@ -0,0 +1,951 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * ov4689 driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/device.h>
>> +#include <linux/delay.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/i2c.h>
>> +#include <linux/module.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-async.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-subdev.h>
>> +#include <media/v4l2-fwnode.h>
>> +
>> +#define CHIP_ID				0x004688
>> +#define OV4689_REG_CHIP_ID		0x300a
>> +
>> +#define OV4689_XVCLK_FREQ		24000000
>> +
>> +#define OV4689_REG_CTRL_MODE		0x0100
>> +#define OV4689_MODE_SW_STANDBY		0x0
>> +#define OV4689_MODE_STREAMING		BIT(0)
>> +
>> +#define OV4689_REG_EXPOSURE		0x3500
>> +#define OV4689_EXPOSURE_MIN		4
>> +#define OV4689_EXPOSURE_STEP		1
>> +#define OV4689_VTS_MAX			0x7fff
>> +
>> +#define OV4689_REG_GAIN_H		0x3508
>> +#define OV4689_REG_GAIN_L		0x3509
>> +#define OV4689_GAIN_H_MASK		0x07
>> +#define OV4689_GAIN_H_SHIFT		8
>> +#define OV4689_GAIN_L_MASK		0xff
>> +#define OV4689_GAIN_MIN			0x10
>> +#define OV4689_GAIN_MAX			0xf8
>> +#define OV4689_GAIN_STEP		1
>> +#define OV4689_GAIN_DEFAULT		0x10
>> +
>> +#define OV4689_REG_TEST_PATTERN		0x5040
>> +#define OV4689_TEST_PATTERN_ENABLE	0x80
>> +#define OV4689_TEST_PATTERN_DISABLE	0x0
>> +
>> +#define OV4689_REG_VTS			0x380e
>> +
>> +#define REG_NULL			0xFFFF
>> +
>> +#define OV4689_REG_VALUE_08BIT		1
>> +#define OV4689_REG_VALUE_16BIT		2
>> +#define OV4689_REG_VALUE_24BIT		3
>> +
>> +#define OV4689_LANES			4
>> +#define OV4689_BITS_PER_SAMPLE		10
>> +
>> +static const char *const ov4689_supply_names[] = {
>> +	"avdd", /* Analog power */
>> +	"dovdd", /* Digital I/O power */
>> +	"dvdd", /* Digital core power */
>> +};
>> +
>> +#define OV4689_NUM_SUPPLIES ARRAY_SIZE(ov4689_supply_names)
>> +
>> +struct regval {
>> +	u16 addr;
>> +	u8 val;
>> +};
>
> What about use ov4689_mode_id? I think could be usefull for the future:
>
> + enum ov4689_mode_id {
> +	OV4689_MODE_2688_1520 = 0,
> +	OV4689_NUM_MODES,
> +	};

Looks like a good idea, will add in v3.

>> +
>> +struct ov4689_mode {
> +	enum ov4689_mode_id id;

Same.

>> +	u32 width;
>> +	u32 height;
>> +	u32 max_fps;
>> +	u32 hts_def;
>> +	u32 vts_def;
>> +	u32 exp_def;
>> +	const struct regval *reg_list;
>> +};
>> +
>> +struct ov4689 {
>> +	struct i2c_client *client;
>> +	struct clk *xvclk;
>> +	struct gpio_desc *reset_gpio;
>> +	struct gpio_desc *pwdn_gpio;
>> +	struct regulator_bulk_data supplies[OV4689_NUM_SUPPLIES];
>> +
>> +	struct v4l2_subdev subdev;
>> +	struct media_pad pad;
>> +
>> +	struct mutex mutex; /* lock to protect streaming, ctrls and cur_mode */
>> +	bool streaming;
>> +	struct v4l2_ctrl_handler ctrl_handler;
>> +	struct v4l2_ctrl *exposure;
>> +	struct v4l2_ctrl *anal_gain;
>> +	struct v4l2_ctrl *digi_gain;
>> +	struct v4l2_ctrl *hblank;
>> +	struct v4l2_ctrl *vblank;
>> +	struct v4l2_ctrl *test_pattern;
>> +
>> +	const struct ov4689_mode *cur_mode;
>> +};
>> +
>> +#define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
>> +
>> +/*
>> + * Xclk 24Mhz
>> + */
>> +static const struct regval ov4689_global_regs[] = {
>> +	{ REG_NULL, 0x00 },
>> +};
>> +
>> +/*
>> + * Xclk 24Mhz
>> + * max_framerate 30fps
>> + * mipi_datarate per lane 1008Mbps
>> + */
>> +static const struct regval ov4689_2688x1520_regs[] = {
>> +	{0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
>> +	{0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
>> +	{0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
>> +	{0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
>> +	{0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
>> +	{0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
>> +	{0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
>> +	{0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
>> +	{0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
>> +	{0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
>> +	{0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
>> +	{0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
>> +	{0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
>> +	{0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
>> +	{0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
>> +	{0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
>> +	{0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
>> +	{0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
>> +	{0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
>> +	{0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
>> +	{0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
>> +	{0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
>> +	{0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
>> +	{0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
>> +	{0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
>> +	{0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
>> +	{0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
>> +	{0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
>> +	{0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
>> +	{0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
>> +	{0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
>> +	{0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
>> +	{0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
>> +	{0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
>> +	{0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
>> +	{0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
>> +	{0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
>> +	{0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
>> +	{0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
>> +	{0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
>> +	{0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
>> +	{0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
>> +	{0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
>> +	{0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
>> +	{0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
>> +	{0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
>> +	{0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
>> +	{0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
>> +	{0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
>> +	{0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
>> +	{0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
>> +	{0x380d, 0x80}, {0x380e, 0x06}, {0x380f, 0x12},
>> +	{0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
>> +	{0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
>> +	{0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
>> +	{0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
>> +	{0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
>> +	{0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
>> +	{0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
>> +	{0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
>> +	{0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
>> +	{0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
>> +	{0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
>> +	{0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
>> +	{0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
>> +	{0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
>> +	{0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
>> +	{0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
>> +	{0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
>> +	{0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
>> +	{0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
>> +	{0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
>> +	{0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
>> +	{0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
>> +	{0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
>> +	{0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
>> +	{0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
>> +	{0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
>> +	{0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
>> +	{0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
>> +	{0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
>> +	{0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
>> +	{0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
>> +	{0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
>> +	{REG_NULL, 0x00},
>> +};
>> +
>> +static const struct ov4689_mode supported_modes[] = {
>> +	{
> +		.id = OV4689_MODE_2688_1520,

Same.

>> +		.width = 2688,
>> +		.height = 1520,
>> +		.max_fps = 30,
>> +		.exp_def = 0x0600,
>> +		.hts_def = 0x0a80,
>> +		.vts_def = 0x0612,
>> +		.reg_list = ov4689_2688x1520_regs,
>> +	},
>> +};
>> +
>> +#define OV4689_LINK_FREQ_500MHZ 500000000
>> +static const s64 link_freq_menu_items[] = { OV4689_LINK_FREQ_500MHZ };
>> +
>> +static const char *const ov4689_test_pattern_menu[] = {
>> +	"Disabled",
>> +	"Vertical Color Bar Type 1",
>> +	"Vertical Color Bar Type 2",
>> +	"Vertical Color Bar Type 3",
>> +	"Vertical Color Bar Type 4"
>> +};
>> +
>> +/* Write registers up to 4 at a time */
>> +static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len,
>> +			    u32 val)
>> +{
>> +	u32 buf_i, val_i;
>> +	__be32 val_be;
>> +	u8 *val_p;
>> +	u8 buf[6];
>> +
>> +	if (len > 4)
>> +		return -EINVAL;
>> +
>> +	buf[0] = reg >> 8;
>> +	buf[1] = reg & 0xff;
>> +
>> +	val_be = cpu_to_be32(val);
>> +	val_p = (u8 *)&val_be;
>> +	buf_i = 2;
>> +	val_i = 4 - len;
>> +
>> +	while (val_i < 4)
>> +		buf[buf_i++] = val_p[val_i++];
>> +
>> +	if (i2c_master_send(client, buf, len + 2) != len + 2)
>> +		return -EIO;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_write_array(struct i2c_client *client,
>> +			      const struct regval *regs)
>> +{
>> +	int ret = 0;
>> +	u32 i;
>> +
>> +	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
>> +		ret = ov4689_write_reg(client, regs[i].addr,
>> +				       OV4689_REG_VALUE_08BIT, regs[i].val);
>> +
>> +	return ret;
>> +}
>> +
>> +/* Read registers up to 4 at a time */
>> +static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
>> +			   u32 *val)
>> +{
>> +	__be16 reg_addr_be = cpu_to_be16(reg);
>> +	struct i2c_msg msgs[2];
>> +	__be32 data_be = 0;
>> +	u8 *data_be_p;
>> +	int ret;
>> +
>> +	if (len > 4 || !len)
>> +		return -EINVAL;
>> +
>> +	data_be_p = (u8 *)&data_be;
>> +	/* Write register address */
>> +	msgs[0].addr = client->addr;
>> +	msgs[0].flags = 0;
>> +	msgs[0].len = 2;
>> +	msgs[0].buf = (u8 *)&reg_addr_be;
>> +
>> +	/* Read data from register */
>> +	msgs[1].addr = client->addr;
>> +	msgs[1].flags = I2C_M_RD;
>> +	msgs[1].len = len;
>> +	msgs[1].buf = &data_be_p[4 - len];
>> +
>> +	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
>> +	if (ret != ARRAY_SIZE(msgs))
>> +		return -EIO;
>> +
>> +	*val = be32_to_cpu(data_be);
>> +
>> +	return 0;
>> +}
>> +
>> +static void ov4689_fill_fmt(const struct ov4689_mode *mode,
>> +			    struct v4l2_mbus_framefmt *fmt)
>> +{
>> +	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +	fmt->width = mode->width;
>> +	fmt->height = mode->height;
>> +	fmt->field = V4L2_FIELD_NONE;
>> +}
>> +
>> +static int ov4689_set_fmt(struct v4l2_subdev *sd,
>> +			  struct v4l2_subdev_state *sd_state,
>> +			  struct v4l2_subdev_format *fmt)
>> +{
>> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +
>> +	/* only one mode supported for now */
>> +	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_get_fmt(struct v4l2_subdev *sd,
>> +			  struct v4l2_subdev_state *sd_state,
>> +			  struct v4l2_subdev_format *fmt)
>> +{
>> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +
>> +	/* only one mode supported for now */
>> +	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
>> +				 struct v4l2_subdev_state *sd_state,
>> +				 struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +	if (code->index != 0)
>> +		return -EINVAL;
>> +	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_enum_frame_sizes(struct v4l2_subdev *sd,
>> +				   struct v4l2_subdev_state *sd_state,
>> +				   struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> +	if (fse->index >= ARRAY_SIZE(supported_modes))
>> +		return -EINVAL;
>> +
>> +	if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
>> +		return -EINVAL;
>> +
>> +	fse->min_width = supported_modes[fse->index].width;
>> +	fse->max_width = supported_modes[fse->index].width;
>> +	fse->max_height = supported_modes[fse->index].height;
>> +	fse->min_height = supported_modes[fse->index].height;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
>> +{
>> +	u32 val;
>> +
>> +	if (pattern)
>> +		val = (pattern - 1) | OV4689_TEST_PATTERN_ENABLE;
>> +	else
>> +		val = OV4689_TEST_PATTERN_DISABLE;
>> +
>> +	return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN,
>> +				OV4689_REG_VALUE_08BIT, val);
>> +}
>> +
>> +static int ov4689_get_selection(struct v4l2_subdev *sd,
>> +				struct v4l2_subdev_state *state,
>> +				struct v4l2_subdev_selection *sel)
>> +{
>> +	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
>> +		return -EINVAL;
>> +
>> +	switch (sel->target) {
>> +	case V4L2_SEL_TGT_CROP_BOUNDS:
>> +		sel->r.top = 0;
>> +		sel->r.left = 0;
>> +		sel->r.width = 2720;
>> +		sel->r.height = 1536;
>> +		return 0;
>> +	case V4L2_SEL_TGT_CROP:
>> +	case V4L2_SEL_TGT_CROP_DEFAULT:
>> +		sel->r.top = 8;
>> +		sel->r.left = 16;
>> +		sel->r.width = 2688;
>> +		sel->r.height = 1520;
>> +		return 0;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
>> +{
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +	struct i2c_client *client = ov4689->client;
>> +	int ret = 0;
>> +
>> +	mutex_lock(&ov4689->mutex);
>> +
>> +	on = !!on;
>> +	if (on == ov4689->streaming)
>> +		goto unlock_and_return;
>> +
>> +	if (on) {
>> +		ret = pm_runtime_resume_and_get(&client->dev);
>> +		if (ret < 0)
>> +			goto unlock_and_return;
>> +
>> +		ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
>> +		if (ret) {
>> +			pm_runtime_put(&client->dev);
>> +			goto unlock_and_return;
>> +		}
>> +
>> +		ret = ov4689_write_array(ov4689->client,
>> +					 ov4689->cur_mode->reg_list);
>> +		if (ret) {
>> +			pm_runtime_put(&client->dev);
>> +			goto unlock_and_return;
>> +		}
>> +
>> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
>> +				       OV4689_REG_VALUE_08BIT,
>> +				       OV4689_MODE_STREAMING);
>> +		if (ret) {
>> +			pm_runtime_put(&client->dev);
>> +			goto unlock_and_return;
>> +		}
>> +	} else {
>> +		ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
>> +				 OV4689_REG_VALUE_08BIT,
>> +				 OV4689_MODE_SW_STANDBY);
>> +		pm_runtime_put(&client->dev);
>> +	}
>> +
>> +	ov4689->streaming = on;
>> +
>> +unlock_and_return:
>> +	mutex_unlock(&ov4689->mutex);
>> +
>> +	return ret;
>> +}
>> +
>> +/* Calculate the delay in us by clock rate and clock cycles */
>> +static inline u32 ov4689_cal_delay(u32 cycles)
>> +{
>> +	return DIV_ROUND_UP(cycles, OV4689_XVCLK_FREQ / 1000 / 1000);
>> +}
>> +
>> +static int __ov4689_power_on(struct ov4689 *ov4689)
>
> Just a doubt on this name function. Why __ ? Is this name reserved for?

Just a leftover from BSP driver, will clean this up in v3.

>> +{
>> +	struct device *dev = &ov4689->client->dev;
>> +	u32 delay_us;
>> +	int ret;
>> +
>> +	ret = clk_prepare_enable(ov4689->xvclk);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable xvclk\n");
>> +		return ret;
>> +	}
>> +
>> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
>> +
>> +	ret = regulator_bulk_enable(OV4689_NUM_SUPPLIES, ov4689->supplies);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable regulators\n");
>> +		goto disable_clk;
>> +	}
>> +
>> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
>> +	usleep_range(500, 1000);
>> +	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 0);
>> +
>> +	/* 8192 cycles prior to first SCCB transaction */
>> +	delay_us = ov4689_cal_delay(8192);
>> +	usleep_range(delay_us, delay_us * 2);
>> +
>> +	return 0;
>> +
>> +disable_clk:
>> +	clk_disable_unprepare(ov4689->xvclk);
>> +
>> +	return ret;
>> +}
>> +
>> +static void __ov4689_power_off(struct ov4689 *ov4689)
>> +{
>> +	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 1);
>> +	clk_disable_unprepare(ov4689->xvclk);
>> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
>> +	regulator_bulk_disable(OV4689_NUM_SUPPLIES, ov4689->supplies);
>> +}
>> +
>> +static int __maybe_unused ov4689_runtime_resume(struct device *dev)
>> +{
>> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +
>> +	return __ov4689_power_on(ov4689);
>> +}
>> +
>> +static int __maybe_unused ov4689_runtime_suspend(struct device *dev)
>> +{
>> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +
>> +	__ov4689_power_off(ov4689);
>> +
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +	struct v4l2_mbus_framefmt *try_fmt;
>> +
>> +	mutex_lock(&ov4689->mutex);
>> +
>> +	try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0);
>> +	/* Initialize try_fmt */
>> +	ov4689_fill_fmt(&supported_modes[0], try_fmt);
>
> In this way instead of use magic number we can use this:
>
> 	ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt);

Ack, will fix in v3.

>> +
>> +	mutex_unlock(&ov4689->mutex);
>> +
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static const struct dev_pm_ops ov4689_pm_ops = {
>> +	SET_RUNTIME_PM_OPS(ov4689_runtime_suspend, ov4689_runtime_resume, NULL)
>> +};
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
>> +	.open = ov4689_open,
>> +};
>> +#endif
>> +
>> +static const struct v4l2_subdev_video_ops ov4689_video_ops = {
>> +	.s_stream = ov4689_s_stream,
>> +};
>> +
>> +static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
>> +	.enum_mbus_code = ov4689_enum_mbus_code,
>> +	.enum_frame_size = ov4689_enum_frame_sizes,
>> +	.get_fmt = ov4689_get_fmt,
>> +	.set_fmt = ov4689_set_fmt,
>> +	.get_selection = ov4689_get_selection,
>> +};
>> +
>> +static const struct v4l2_subdev_ops ov4689_subdev_ops = {
>> +	.video = &ov4689_video_ops,
>> +	.pad = &ov4689_pad_ops,
>> +};
>> +
>> +static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct ov4689 *ov4689 =
>> +		container_of(ctrl->handler, struct ov4689, ctrl_handler);
>> +	struct i2c_client *client = ov4689->client;
>> +	s64 max_expo;
>> +	int ret;
>> +
>> +	/* Propagate change of current control to all related controls */
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_VBLANK:
>> +		/* Update max exposure while meeting expected vblanking */
>> +		max_expo = ov4689->cur_mode->height + ctrl->val - 4;
>> +		__v4l2_ctrl_modify_range(ov4689->exposure,
>> +					 ov4689->exposure->minimum, max_expo,
>> +					 ov4689->exposure->step,
>> +					 ov4689->exposure->default_value);
>> +		break;
>> +	}
>> +
>> +	if (!pm_runtime_get_if_in_use(&client->dev))
>> +		return 0;
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_EXPOSURE:
>> +		/* 4 least significant bits of expsoure are fractional part */
>> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE,
>> +				       OV4689_REG_VALUE_24BIT, ctrl->val << 4);
>> +		break;
>> +	case V4L2_CID_ANALOGUE_GAIN:
>> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H,
>> +				       OV4689_REG_VALUE_08BIT,
>> +				       (ctrl->val >> OV4689_GAIN_H_SHIFT) &
>> +					       OV4689_GAIN_H_MASK);
>> +		ret |= ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L,
>> +					OV4689_REG_VALUE_08BIT,
>> +					ctrl->val & OV4689_GAIN_L_MASK);
>> +		break;
>> +	case V4L2_CID_VBLANK:
>> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS,
>> +				       OV4689_REG_VALUE_16BIT,
>> +				       ctrl->val + ov4689->cur_mode->height);
>> +		break;
>> +	case V4L2_CID_TEST_PATTERN:
>> +		ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
>> +		break;
>> +	default:
>> +		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
>> +			 __func__, ctrl->id, ctrl->val);
>> +		ret = -EINVAL;
>> +		break;
>> +	}
>> +
>> +	pm_runtime_put(&client->dev);
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
>> +	.s_ctrl = ov4689_set_ctrl,
>> +};
>> +
>> +static int ov4689_initialize_controls(struct ov4689 *ov4689)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&ov4689->subdev);
>> +	struct v4l2_fwnode_device_properties props;
>> +	struct v4l2_ctrl_handler *handler;
>> +	const struct ov4689_mode *mode;
>> +	s64 exposure_max, vblank_def;
>> +	struct v4l2_ctrl *ctrl;
>> +	u32 h_blank, pixel_rate;
>> +	int ret;
>> +
>> +	handler = &ov4689->ctrl_handler;
>> +	mode = ov4689->cur_mode;
>> +	ret = v4l2_ctrl_handler_init(handler, 10);
>> +	if (ret)
>> +		return ret;
>> +	handler->lock = &ov4689->mutex;
>> +
>> +	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
>> +				      link_freq_menu_items);
>> +	if (ctrl)
>> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> +	pixel_rate = (link_freq_menu_items[0] * 2 * OV4689_LANES) /
>> +		     OV4689_BITS_PER_SAMPLE;
>> +	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, pixel_rate, 1,
>> +			  pixel_rate);
>> +
>> +	h_blank = mode->hts_def - mode->width;
>> +	ov4689->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
>> +					   h_blank, h_blank, 1, h_blank);
>> +	if (ov4689->hblank)
>> +		ov4689->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> +	vblank_def = mode->vts_def - mode->height;
>> +	ov4689->vblank =
>> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
>> +				  vblank_def, OV4689_VTS_MAX - mode->height, 1,
>> +				  vblank_def);
>> +
>> +	exposure_max = mode->vts_def - 4;
>> +	ov4689->exposure =
>> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_EXPOSURE,
>> +				  OV4689_EXPOSURE_MIN, exposure_max,
>> +				  OV4689_EXPOSURE_STEP, mode->exp_def);
>> +
>> +	ov4689->anal_gain =
>> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops,
>> +				  V4L2_CID_ANALOGUE_GAIN, OV4689_GAIN_MIN,
>> +				  OV4689_GAIN_MAX, OV4689_GAIN_STEP,
>> +				  OV4689_GAIN_DEFAULT);
>> +
>> +	ov4689->test_pattern =
>> +		v4l2_ctrl_new_std_menu_items(handler, &ov4689_ctrl_ops,
>> +					     V4L2_CID_TEST_PATTERN,
>> +					     ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
>> +					     0, 0, ov4689_test_pattern_menu);
>> +
>> +	if (handler->error) {
>> +		ret = handler->error;
>> +		dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n",
>> +			ret);
>> +		goto err_free_handler;
>> +	}
>> +
>> +	ret = v4l2_fwnode_device_parse(&client->dev, &props);
>> +	if (ret)
>> +		goto err_free_handler;
>> +
>> +	ret = v4l2_ctrl_new_fwnode_properties(handler, &ov4689_ctrl_ops,
>> +					      &props);
>> +	if (ret)
>> +		goto err_free_handler;
>> +
>> +	ov4689->subdev.ctrl_handler = handler;
>> +
>> +	return 0;
>> +
>> +err_free_handler:
>> +	v4l2_ctrl_handler_free(handler);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov4689_check_sensor_id(struct ov4689 *ov4689,
>> +				  struct i2c_client *client)
>> +{
>> +	struct device *dev = &ov4689->client->dev;
>> +	u32 id = 0;
>> +	int ret;
>> +
>> +	ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID,
>> +			      OV4689_REG_VALUE_16BIT, &id);
>> +	if (id != CHIP_ID) {
>> +		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
>> +		return -ENODEV;
>> +	}
>> +
>> +	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_configure_regulators(struct ov4689 *ov4689)
>> +{
>> +	unsigned int i;
>> +
>> +	for (i = 0; i < OV4689_NUM_SUPPLIES; i++)
>> +		ov4689->supplies[i].supply = ov4689_supply_names[i];
>> +
>> +	return devm_regulator_bulk_get(&ov4689->client->dev,
>> +				       OV4689_NUM_SUPPLIES, ov4689->supplies);
>> +}
>> +
>> +static int ov4689_check_hwcfg(struct device *dev)
>> +{
>> +	struct fwnode_handle *fwnode = dev_fwnode(dev);
>> +	struct v4l2_fwnode_endpoint bus_cfg = {
>> +		.bus_type = V4L2_MBUS_CSI2_DPHY,
>> +	};
>> +	struct fwnode_handle *endpoint;
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
>> +	if (!endpoint)
>> +		return -EPROBE_DEFER;
>> +
>> +	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
>> +	fwnode_handle_put(endpoint);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
>> +		dev_err(dev, "only a 4-lane CSI2 config is supported");
>> +		ret = -EINVAL;
>> +		goto out_free_bus_cfg;
>> +	}
>> +
>> +	if (!bus_cfg.nr_of_link_frequencies) {
>> +		dev_err(dev, "no link frequencies defined\n");
>> +		ret = -EINVAL;
>> +		goto out_free_bus_cfg;
>> +	}
>> +
>> +	for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
>> +		if (bus_cfg.link_frequencies[i] == OV4689_LINK_FREQ_500MHZ)
>> +			break;
>> +
>> +	if (i == bus_cfg.nr_of_link_frequencies) {
>> +		dev_err(dev, "supported link freq %ull not found\n",
>> +			OV4689_LINK_FREQ_500MHZ);
>> +		ret = -EINVAL;
>> +		goto out_free_bus_cfg;
>> +	}
>> +
>> +out_free_bus_cfg:
>> +	v4l2_fwnode_endpoint_free(&bus_cfg);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov4689_probe(struct i2c_client *client,
>> +			const struct i2c_device_id *id)
>
> We are sure that we need need i2c_device_id *id?

I see, will convert to single-argument .probe_new in v3.

>> +{
>> +	struct device *dev = &client->dev;
>> +	struct v4l2_subdev *sd;
>> +	struct ov4689 *ov4689;
>> +	int ret;
>> +
>> +	ret = ov4689_check_hwcfg(dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
>> +	if (!ov4689)
>> +		return -ENOMEM;
>> +
>> +	ov4689->client = client;
>> +	ov4689->cur_mode = &supported_modes[0];
>
> Here aswell we can use:
> 	ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520]

Ack.

>> +
>> +	ov4689->xvclk = devm_clk_get(dev, "xvclk");
>> +	if (IS_ERR(ov4689->xvclk)) {
>> +		dev_err(dev, "Failed to get xvclk\n");
>> +		return -EINVAL;
>> +	}
>
> ^ I think is better to use devm_clk_get_optional instead of clck_get.
> clck_get can fail in CPU's that use ACPI
>
>> +
>> +	ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
>> +		return ret;
>> +	}
>> +	if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
>> +		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
>
>
> What do you think about?
> Thanks.

Unfortunately, I have no experience with ACPI-based devices. :(

Do you mean that in the case of an ACPI device and devm_clk_get_optional
returning NULL we should assume that the clock is already enabled and
will stay enabled during sensor operation? How should we distinguish it
from the case of an OF-based system and clock just missing from device
tree?

As a note to myself:

-static const struct ov4689_mode supported_modes[]
+static const struct ov4689_mode supported_modes[OV4689_NUM_MODES]

Overall, thanks for the review!

> Regards,
> Tommaso
>
--
Best regards,
Mikhail Rudenko

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-14  9:58 ` [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver Dave Stevenson
@ 2022-09-15 21:27   ` Mikhail Rudenko
  2022-09-19  6:40     ` Sakari Ailus
  0 siblings, 1 reply; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-15 21:27 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Jimmy Su,
	Arnd Bergmann, Arec Kao, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

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


Hi Dave,

On 2022-09-14 at 10:58 +01, Dave Stevenson <dave.stevenson@raspberrypi.com> wrote:
> Hi Mikhail
>
> On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko <mike.rudenko@gmail.com> wrote:
>>
>> Hello,
>>
>> this series implements support for Omnivision OV4689 image
>> sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
>> megapixel image sensor. Ihis chip supports high frame rate speeds up
>> to 90 fps at 2688x1520 resolution. It is programmable through an I2C
>> interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
>> connection.
>>
>> The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
>> and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
>> 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera module.
>>
>> While porting the driver, I stumbled upon two issues:
>>
>> (1) In the original driver, horizontal total size (HTS) was set to a
>> value (2584) lower then the frame width (2688), resulting in negative
>> hblank. In this driver, I increased HTS to 2688, but fps dropped from
>> 29.88 to 28.73. What is the preferred way to handle this?
>
> This is one of the joys of sensors - they don't all work in the same way.
>
> I don't have an official datasheet for OV4689 from Omnivision, but
> found one on the internet [1]. That should allow you to reverse the
> PLL configuration to confirm that the pixel rate is the value you've
> computed based on link frequency (they aren't necessarily related). Do
> the frame rate calculations work using width + HBLANK, height +
> VBLANK, and pixel rate?
> The datasheet claims the sensor supports 2688x1520 @ 90 fps, so
> something doesn't hold true between 4 data lanes at 500MHz/1Gbit/s per
> lane when your default hts/vts is 2688x1554 and it only gives
> 28.73fps.

Seems like those 90 fps is about CSI throughput, not actual sensor
performance. I've checked the datasheet and the register values, and it
seems like the pixel clock is 126 Mhz in this configuration (the maximum
is 150 MHz according to the datasheet). This corresponds to a
theoretical fps of 30.16 at hts=2688 and vts=1554. At the same time the
observed fps is 28.73. I'm not sure where those 1.43 frames are lost,
hope to do more experimentation with VTS and HTS over the weekend.

> I have seen modes in sensors where the HTS register is in units of 2
> pixels, so what range of HTS (and VTS) values actually works on this
> sensor? (I don't see it documented, but I'm not surprised).
>
> [1] https://cdn.hackaday.io/files/19354828041536/OV4689-OmniVision.pdf
>
>> (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
>> gain is not linear across that range. Instead, it is piecewise linear
>> (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
>> 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
>> with more linear segments in between. Rockchip's camera engine code
>> chooses one of the above segments depenging on the desired gain
>> value. The question is, how should we proceed keeping in mind
>> libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
>> libcamera will do the mapping, or the driver will do the mapping
>> itself and expose some logical gain units not tied to the actual gain
>> register value? Meanwhile, this driver conservatively exposes only
>> 0x0-0xf8 gain register range.
>
> The datasheet linked above says "for the gain formula, please contact
> your local OmniVision FAE" :-(
> I would assume that the range is from 1x rather than 0x - people
> rarely want a totally black image that 0x would give. Or is it ranges
> of 1x - 2x, 2x - 4x, 4x - 8x, and 8x - 16x?

A picture is worth a thousand words, so I've attached the results of my
experimentation with the gain register. They were obtained with Rockchip
3399, with AEC, AGC and black level subtraction disabled. The image was
converted from 10-bit RGGB to 8-bit YUV 4:2:0 by the Rockchip ISP.

> Other sensors expose the full range of the register via
> V4L2_CID_ANALOGUE_GAIN, and require userspace (mainly libcamera now)
> to know how to convert a gain into the register value. If the gain
> range goes up to x16, then exposing that would be useful. I'd advocate
> just exposing the full range of 0x000 - 0x7ff, as then you can have
> the accuracy of 256 values between x1 to x2, but also the full range.

I also like this approach, although libcamera's CameraSensorHelper
doesn't support piecewise-linear gain code mapping yet. Nevertheless,
I believe exposing the full range is a good idea and will do so in v3.

> I might see if I can pick up one of these sensors and see if I can get
> it running on a Raspberry Pi. Thanks for trying to upstream this -
> it's nice to have such a range of sensor drivers to choose from.
>
>   Dave
>

Thanks for your elucidating tips!

--
Best regards,
Mikhail Rudenko


[-- Attachment #2: gain.png --]
[-- Type: image/png, Size: 49130 bytes --]

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

* Re: [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings
  2022-09-15 12:16     ` Mikhail Rudenko
@ 2022-09-16  9:42       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 41+ messages in thread
From: Krzysztof Kozlowski @ 2022-09-16  9:42 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Christian Hemp, Arec Kao, Arnd Bergmann, Laurent Pinchart,
	Daniel Scally, Jimmy Su, linux-media, devicetree, linux-kernel

On 15/09/2022 13:16, Mikhail Rudenko wrote:

> 
> I see, will drop the first "media:" in v3.
> 
>> Also you duplicated dt
>> bindings as prefix and commit msg (skip the latter).
> 
> Just to be clear, do you mean dropping "device-tree binding" phrase from
> the commit message?

Ah, sorry, I meant in the subject. You already have dt-bindings as
prefix, so no "DT bindings" at the end. Just:
media: dt-bindings: i2c: document OV4689

> 
>>> ---
>>>  .../bindings/media/i2c/ovti,ov4689.yaml       | 141 ++++++++++++++++++
>>>  MAINTAINERS                                   |   7 +
>>>  2 files changed, 148 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>>> new file mode 100644
>>> index 000000000000..376330b5572a
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>>> @@ -0,0 +1,141 @@
>>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/media/i2c/ovti,ov4689.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: Omnivision OV4689 CMOS
>>> +
>>> +maintainers:
>>> +  - Mikhail Rudenko <mike.rudenko@gmail.com>
>>> +
>>> +description: |
>>> +  The Omnivision OV4689 is a high performance, 1/3-inch, 4 megapixel
>>> +  image sensor. Ihis chip supports high frame rate speeds up to 90 fps
>>> +  at 2688x1520 resolution. It is programmable through an I2C
>>> +  interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
>>> +  connection.
>>> +
>>> +allOf:
>>> +  - $ref: /schemas/media/video-interface-devices.yaml#
>>> +
>>> +properties:
>>> +  compatible:
>>> +    const: ovti,ov4689
>>> +
>>> +  reg:
>>> +    maxItems: 1
>>> +
>>> +  clocks:
>>> +    description:
>>> +      External clock (XVCLK) for the sensor, 6-64 MHz
>>> +    maxItems: 1
>>> +
>>> +  clock-names: true
>>
>> This has to be strictly defined - which name you expect.
> 
> Will fix in v3. Or maybe we should drop clock-names altogether and use
> devm_clk_get(&client->dev, NULL) in the driver instead (I've seen this
> approach in some existing drivers)?

Yes, usually clock-names for one entry does not make sense.

> 
>>> +
>>> +  dovdd-supply:
>>> +    description:
>>> +      Digital I/O voltage supply, 1.7-3.0 V
>>> +
>>> +  avdd-supply:
>>> +    description:
>>> +      Analog voltage supply, 2.6-3.0 V
>>> +
>>> +  dvdd-supply:
>>> +    description:
>>> +      Digital core voltage supply, 1.1-1.3 V
>>> +
>>> +  powerdown-gpios:
>>> +    maxItems: 1
>>
>> You can skip here maxItems - it is defined by gpio-consumer-common.
> 
> Ack, will fix in v3. Does this also apply to reset-gpios?

No.

https://elixir.bootlin.com/linux/v6.0-rc1/source/Documentation/devicetree/bindings/gpio/gpio-consumer-common.yaml


Best regards,
Krzysztof

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-15 20:40     ` Mikhail Rudenko
@ 2022-09-16  9:43       ` Krzysztof Kozlowski
  2022-09-16 21:51         ` Sakari Ailus
  0 siblings, 1 reply; 41+ messages in thread
From: Krzysztof Kozlowski @ 2022-09-16  9:43 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

On 15/09/2022 21:40, Mikhail Rudenko wrote:
> 
> Hi Krzysztof,
> 
> On 2022-09-12 at 12:56 +02, Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> wrote:
>> On 11/09/2022 22:01, Mikhail Rudenko wrote:
>>> +static const struct i2c_device_id ov4689_id[] = {
>>> +	{ "ov4689", 0 },
>>> +	{},
>>> +};
>>> +MODULE_DEVICE_TABLE(i2c, ov4689_id);
>>> +
>>> +static const struct of_device_id ov4689_of_match[] = {
>>> +	{ .compatible = "ovti,ov4689" },
>>> +	{},
>>> +};
>>> +MODULE_DEVICE_TABLE(of, ov4689_of_match);
>>> +
>>> +static struct i2c_driver ov4689_i2c_driver = {
>>> +	.driver = {
>>> +		.name = "ov4689",
>>> +		.pm = &ov4689_pm_ops,
>>> +		.of_match_table = of_match_ptr(ov4689_of_match),
>>
>> of_match_ptr is usually paired with maybe_unused, otherwise you will
>> have compile test warnings.
> 
> I see. I think we could also use `#if IS_ENABLED(CONFIG_OF)` around
> `ov4689_of_match` and the corresponding `MODULE_DEVICE_TABLE`. Is it
> appropriate here?

Would work, but ifdefs are not nice. Just use maybe_unused. Warnings
should disappear.

Best regards,
Krzysztof

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

* Re: [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings
  2022-09-15 20:11     ` Mikhail Rudenko
@ 2022-09-16 13:15       ` Tommaso Merciai
  2022-09-16 13:42         ` Mikhail Rudenko
  0 siblings, 1 reply; 41+ messages in thread
From: Tommaso Merciai @ 2022-09-16 13:15 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Christian Hemp, Arec Kao, Arnd Bergmann, Laurent Pinchart,
	Daniel Scally, Jimmy Su, linux-media, devicetree, linux-kernel

Hi Mikhail,

On Thu, Sep 15, 2022 at 11:11:57PM +0300, Mikhail Rudenko wrote:
> 
> Hi Tommaso,
> 
> On 2022-09-13 at 16:05 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
> > Hi Mikhail,
> >
> > On Sun, Sep 11, 2022 at 11:01:34PM +0300, Mikhail Rudenko wrote:
> >> Add device-tree binding documentation for OV4689 image sensor driver,
> >> and the relevant MAINTAINERS entries.
> >>
> >> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
> >> ---
> >>  .../bindings/media/i2c/ovti,ov4689.yaml       | 141 ++++++++++++++++++
> >>  MAINTAINERS                                   |   7 +
> >>  2 files changed, 148 insertions(+)
> >>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> >>
> >> diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> >> new file mode 100644
> >> index 000000000000..376330b5572a
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> >> @@ -0,0 +1,141 @@
> >> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> >> +%YAML 1.2
> >> +---
> >> +$id: http://devicetree.org/schemas/media/i2c/ovti,ov4689.yaml#
> >> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> >> +
> >> +title: Omnivision OV4689 CMOS
> >> +
> >> +maintainers:
> >> +  - Mikhail Rudenko <mike.rudenko@gmail.com>
> >> +
> >> +description: |
> >> +  The Omnivision OV4689 is a high performance, 1/3-inch, 4 megapixel
> >> +  image sensor. Ihis chip supports high frame rate speeds up to 90 fps
> >> +  at 2688x1520 resolution. It is programmable through an I2C
> >> +  interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> >> +  connection.
> >> +
> >> +allOf:
> >> +  - $ref: /schemas/media/video-interface-devices.yaml#
> >> +
> >> +properties:
> >> +  compatible:
> >> +    const: ovti,ov4689
> >> +
> >> +  reg:
> >> +    maxItems: 1
> >> +
> >> +  clocks:
> >> +    description:
> >> +      External clock (XVCLK) for the sensor, 6-64 MHz
> >> +    maxItems: 1
> >> +
> >> +  clock-names: true
> >> +
> >> +  dovdd-supply:
> >> +    description:
> >> +      Digital I/O voltage supply, 1.7-3.0 V
> >> +
> >> +  avdd-supply:
> >> +    description:
> >> +      Analog voltage supply, 2.6-3.0 V
> >> +
> >> +  dvdd-supply:
> >> +    description:
> >> +      Digital core voltage supply, 1.1-1.3 V
> >> +
> >> +  powerdown-gpios:
> >> +    maxItems: 1
> >> +    description:
> >> +      GPIO connected to the powerdown pin (active low)
> >> +
> >> +  reset-gpios:
> >> +    maxItems: 1
> >> +    description:
> >> +      GPIO connected to the reset pin (active low)
> >> +
> >> +  orientation: true
> >> +
> >> +  rotation: true
> >> +
> >> +  port:
> >> +    $ref: /schemas/graph.yaml#/$defs/port-base
> >> +    additionalProperties: false
> >> +    description:
> >> +      Output port node, single endpoint describing the CSI-2 transmitter
> >> +
> >> +    properties:
> >> +      endpoint:
> >> +        $ref: /schemas/media/video-interfaces.yaml#
> >> +        unevaluatedProperties: false
> >> +
> >> +        properties:
> >> +          data-lanes:
> >> +            oneOf:
> >> +              - items:
> >> +                  - const: 1
> >> +                  - const: 2
> >> +                  - const: 3
> >> +                  - const: 4
> >> +              - items:
> >> +                  - const: 1
> >> +                  - const: 2
> >> +              - items:
> >> +                  - const: 1
> >> +          link-frequencies: true
> >> +
> >> +        required:
> >> +          - data-lanes
> >> +          - link-frequencies
> >> +
> >> +required:
> >> +  - compatible
> >> +  - reg
> >> +  - clocks
> >> +  - clock-names
> >> +  - dovdd-supply
> >> +  - avdd-supply
> >> +  - dvdd-supply
> >> +  - powerdown-gpios
> >> +  - reset-gpios
> >> +  - port
> >
> > I think we don't need all of these entries as required.
> > The only let me say "really" required are:
> >
> > - compatible
> > - reg
> > - clocks
> > - port
> 
> Thanks for the review! I agree that the driver may be modified to work
> without powerdown and reset gpios and they are not required for sensor
> operation. On contrary, supplies are obviously required. Of course, linux
> provides dummy regulators if supplies are missing from device tree, but
> I though the intention was to document hardware, not implementation
> details. What do think of this?

We have already discuss on this on the following thread sometimes ago :)

https://www.patchwork.linux-fancy.com/project/linux-fancy/patch/20220630134835.592521-6-tommaso.merciai@amarulasolutions.com/

Take a look and let me know.

Thanks,
Tommaso
> 
> > Regards,
> > Tommaso
> 
> --
> Best regards,
> Mikhail Rudenko

-- 
Tommaso Merciai
Embedded Linux Engineer
tommaso.merciai@amarulasolutions.com
__________________________________

Amarula Solutions SRL
Via Le Canevare 30, 31100 Treviso, Veneto, IT
T. +39 042 243 5310
info@amarulasolutions.com
www.amarulasolutions.com

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-15 20:50     ` Mikhail Rudenko
@ 2022-09-16 13:34       ` Tommaso Merciai
  2022-09-16 13:44         ` Mikhail Rudenko
  2022-09-19  6:33         ` Sakari Ailus
  0 siblings, 2 replies; 41+ messages in thread
From: Tommaso Merciai @ 2022-09-16 13:34 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail,

On Thu, Sep 15, 2022 at 11:50:23PM +0300, Mikhail Rudenko wrote:
> 
> Hi Tommaso,
> 
> On 2022-09-14 at 17:51 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
> > Hi Mikhail,
> > I do a first round on reviewing your driver :)
> >
> > On Sun, Sep 11, 2022 at 11:01:35PM +0300, Mikhail Rudenko wrote:
> >> Add a V4L2 sub-device driver for OmniVision OV4689 image sensor. This
> >> is a 4 Mpx image sensor using the I2C bus for control and the CSI-2
> >> bus for data.
> >>
> >> This driver supports following features:
> >> - manual exposure and analog gain control support
> >> - test pattern support
> >> - media controller support
> >> - runtime PM support
> >> - support following resolutions:
> >>   + 2688x1520 at 30 fps
> >>
> >> The driver provides all mandatory V4L2 controls for compatibility with
> >> libcamera. The sensor supports 1/2/4-lane CSI-2 modes, but the driver
> >> implements 4 lane mode only at this moment.
> >>
> >> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
> >> ---
> >>  MAINTAINERS                |   1 +
> >>  drivers/media/i2c/Kconfig  |  14 +
> >>  drivers/media/i2c/Makefile |   1 +
> >>  drivers/media/i2c/ov4689.c | 951 +++++++++++++++++++++++++++++++++++++
> >>  4 files changed, 967 insertions(+)
> >>  create mode 100644 drivers/media/i2c/ov4689.c
> >>
> >> diff --git a/MAINTAINERS b/MAINTAINERS
> >> index 63c4844f26e6..1857f3864e1b 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -14529,6 +14529,7 @@ L:	linux-media@vger.kernel.org
> >>  S:	Maintained
> >>  T:	git git://linuxtv.org/media_tree.git
> >>  F:	Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> >> +F:	drivers/media/i2c/ov5647.c
> >>
> >>  OMNIVISION OV5640 SENSOR DRIVER
> >>  M:	Steve Longerbeam <slongerbeam@gmail.com>
> >> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> >> index fae2baabb773..4993e1ae2ea8 100644
> >> --- a/drivers/media/i2c/Kconfig
> >> +++ b/drivers/media/i2c/Kconfig
> >> @@ -429,6 +429,20 @@ config VIDEO_OV2740
> >>  	  To compile this driver as a module, choose M here: the
> >>  	  module will be called ov2740.
> >>
> >> +config VIDEO_OV4689
> >> +	tristate "OmniVision OV4689 sensor support"
> >> +	depends on OF
> >> +	depends on GPIOLIB && VIDEO_DEV && I2C
> >> +	select MEDIA_CONTROLLER
> >> +	select VIDEO_V4L2_SUBDEV_API
> >> +	select V4L2_FWNODE
> >> +	help
> >> +	  This is a Video4Linux2 sensor-level driver for the OmniVision
> >> +	  OV4689 camera.
> >> +
> >> +	  To compile this driver as a module, choose M here: the
> >> +	  module will be called ov4689.
> >> +
> >>  config VIDEO_OV5640
> >>  	tristate "OmniVision OV5640 sensor support"
> >>  	depends on OF
> >> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> >> index 3e1696963e7f..7446c0a1eed0 100644
> >> --- a/drivers/media/i2c/Makefile
> >> +++ b/drivers/media/i2c/Makefile
> >> @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
> >>  obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
> >>  obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
> >>  obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
> >> +obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
> >>  obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
> >>  obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
> >>  obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
> >> diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c
> >> new file mode 100644
> >> index 000000000000..9f05e812acf8
> >> --- /dev/null
> >> +++ b/drivers/media/i2c/ov4689.c
> >> @@ -0,0 +1,951 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * ov4689 driver
> >> + *
> >> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> >> + */
> >> +
> >> +#include <linux/clk.h>
> >> +#include <linux/device.h>
> >> +#include <linux/delay.h>
> >> +#include <linux/gpio/consumer.h>
> >> +#include <linux/i2c.h>
> >> +#include <linux/module.h>
> >> +#include <linux/pm_runtime.h>
> >> +#include <linux/regulator/consumer.h>
> >> +#include <media/media-entity.h>
> >> +#include <media/v4l2-async.h>
> >> +#include <media/v4l2-ctrls.h>
> >> +#include <media/v4l2-subdev.h>
> >> +#include <media/v4l2-fwnode.h>
> >> +
> >> +#define CHIP_ID				0x004688
> >> +#define OV4689_REG_CHIP_ID		0x300a
> >> +
> >> +#define OV4689_XVCLK_FREQ		24000000
> >> +
> >> +#define OV4689_REG_CTRL_MODE		0x0100
> >> +#define OV4689_MODE_SW_STANDBY		0x0
> >> +#define OV4689_MODE_STREAMING		BIT(0)
> >> +
> >> +#define OV4689_REG_EXPOSURE		0x3500
> >> +#define OV4689_EXPOSURE_MIN		4
> >> +#define OV4689_EXPOSURE_STEP		1
> >> +#define OV4689_VTS_MAX			0x7fff
> >> +
> >> +#define OV4689_REG_GAIN_H		0x3508
> >> +#define OV4689_REG_GAIN_L		0x3509
> >> +#define OV4689_GAIN_H_MASK		0x07
> >> +#define OV4689_GAIN_H_SHIFT		8
> >> +#define OV4689_GAIN_L_MASK		0xff
> >> +#define OV4689_GAIN_MIN			0x10
> >> +#define OV4689_GAIN_MAX			0xf8
> >> +#define OV4689_GAIN_STEP		1
> >> +#define OV4689_GAIN_DEFAULT		0x10
> >> +
> >> +#define OV4689_REG_TEST_PATTERN		0x5040
> >> +#define OV4689_TEST_PATTERN_ENABLE	0x80
> >> +#define OV4689_TEST_PATTERN_DISABLE	0x0
> >> +
> >> +#define OV4689_REG_VTS			0x380e
> >> +
> >> +#define REG_NULL			0xFFFF
> >> +
> >> +#define OV4689_REG_VALUE_08BIT		1
> >> +#define OV4689_REG_VALUE_16BIT		2
> >> +#define OV4689_REG_VALUE_24BIT		3
> >> +
> >> +#define OV4689_LANES			4
> >> +#define OV4689_BITS_PER_SAMPLE		10
> >> +
> >> +static const char *const ov4689_supply_names[] = {
> >> +	"avdd", /* Analog power */
> >> +	"dovdd", /* Digital I/O power */
> >> +	"dvdd", /* Digital core power */
> >> +};
> >> +
> >> +#define OV4689_NUM_SUPPLIES ARRAY_SIZE(ov4689_supply_names)
> >> +
> >> +struct regval {
> >> +	u16 addr;
> >> +	u8 val;
> >> +};
> >
> > What about use ov4689_mode_id? I think could be usefull for the future:
> >
> > + enum ov4689_mode_id {
> > +	OV4689_MODE_2688_1520 = 0,
> > +	OV4689_NUM_MODES,
> > +	};
> 
> Looks like a good idea, will add in v3.
> 
> >> +
> >> +struct ov4689_mode {
> > +	enum ov4689_mode_id id;
> 
> Same.
> 
> >> +	u32 width;
> >> +	u32 height;
> >> +	u32 max_fps;
> >> +	u32 hts_def;
> >> +	u32 vts_def;
> >> +	u32 exp_def;
> >> +	const struct regval *reg_list;
> >> +};
> >> +
> >> +struct ov4689 {
> >> +	struct i2c_client *client;
> >> +	struct clk *xvclk;
> >> +	struct gpio_desc *reset_gpio;
> >> +	struct gpio_desc *pwdn_gpio;
> >> +	struct regulator_bulk_data supplies[OV4689_NUM_SUPPLIES];
> >> +
> >> +	struct v4l2_subdev subdev;
> >> +	struct media_pad pad;
> >> +
> >> +	struct mutex mutex; /* lock to protect streaming, ctrls and cur_mode */
> >> +	bool streaming;
> >> +	struct v4l2_ctrl_handler ctrl_handler;
> >> +	struct v4l2_ctrl *exposure;
> >> +	struct v4l2_ctrl *anal_gain;
> >> +	struct v4l2_ctrl *digi_gain;
> >> +	struct v4l2_ctrl *hblank;
> >> +	struct v4l2_ctrl *vblank;
> >> +	struct v4l2_ctrl *test_pattern;
> >> +
> >> +	const struct ov4689_mode *cur_mode;
> >> +};
> >> +
> >> +#define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
> >> +
> >> +/*
> >> + * Xclk 24Mhz
> >> + */
> >> +static const struct regval ov4689_global_regs[] = {
> >> +	{ REG_NULL, 0x00 },
> >> +};
> >> +
> >> +/*
> >> + * Xclk 24Mhz
> >> + * max_framerate 30fps
> >> + * mipi_datarate per lane 1008Mbps
> >> + */
> >> +static const struct regval ov4689_2688x1520_regs[] = {
> >> +	{0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
> >> +	{0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
> >> +	{0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
> >> +	{0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
> >> +	{0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
> >> +	{0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
> >> +	{0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
> >> +	{0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
> >> +	{0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
> >> +	{0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
> >> +	{0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
> >> +	{0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
> >> +	{0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
> >> +	{0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
> >> +	{0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
> >> +	{0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
> >> +	{0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
> >> +	{0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
> >> +	{0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
> >> +	{0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
> >> +	{0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
> >> +	{0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
> >> +	{0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
> >> +	{0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
> >> +	{0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
> >> +	{0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
> >> +	{0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
> >> +	{0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
> >> +	{0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
> >> +	{0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
> >> +	{0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
> >> +	{0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
> >> +	{0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
> >> +	{0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
> >> +	{0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
> >> +	{0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
> >> +	{0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
> >> +	{0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
> >> +	{0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
> >> +	{0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
> >> +	{0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
> >> +	{0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
> >> +	{0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
> >> +	{0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
> >> +	{0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
> >> +	{0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
> >> +	{0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
> >> +	{0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
> >> +	{0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
> >> +	{0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
> >> +	{0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
> >> +	{0x380d, 0x80}, {0x380e, 0x06}, {0x380f, 0x12},
> >> +	{0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
> >> +	{0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
> >> +	{0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
> >> +	{0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
> >> +	{0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
> >> +	{0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
> >> +	{0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
> >> +	{0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
> >> +	{0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
> >> +	{0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
> >> +	{0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
> >> +	{0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
> >> +	{0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
> >> +	{0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
> >> +	{0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
> >> +	{0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
> >> +	{0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
> >> +	{0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
> >> +	{0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
> >> +	{0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
> >> +	{0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
> >> +	{0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
> >> +	{0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
> >> +	{0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
> >> +	{0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
> >> +	{0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
> >> +	{0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
> >> +	{0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
> >> +	{0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
> >> +	{0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
> >> +	{0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
> >> +	{0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
> >> +	{REG_NULL, 0x00},
> >> +};
> >> +
> >> +static const struct ov4689_mode supported_modes[] = {
> >> +	{
> > +		.id = OV4689_MODE_2688_1520,
> 
> Same.
> 
> >> +		.width = 2688,
> >> +		.height = 1520,
> >> +		.max_fps = 30,
> >> +		.exp_def = 0x0600,
> >> +		.hts_def = 0x0a80,
> >> +		.vts_def = 0x0612,
> >> +		.reg_list = ov4689_2688x1520_regs,
> >> +	},
> >> +};
> >> +
> >> +#define OV4689_LINK_FREQ_500MHZ 500000000
> >> +static const s64 link_freq_menu_items[] = { OV4689_LINK_FREQ_500MHZ };
> >> +
> >> +static const char *const ov4689_test_pattern_menu[] = {
> >> +	"Disabled",
> >> +	"Vertical Color Bar Type 1",
> >> +	"Vertical Color Bar Type 2",
> >> +	"Vertical Color Bar Type 3",
> >> +	"Vertical Color Bar Type 4"
> >> +};
> >> +
> >> +/* Write registers up to 4 at a time */
> >> +static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len,
> >> +			    u32 val)
> >> +{
> >> +	u32 buf_i, val_i;
> >> +	__be32 val_be;
> >> +	u8 *val_p;
> >> +	u8 buf[6];
> >> +
> >> +	if (len > 4)
> >> +		return -EINVAL;
> >> +
> >> +	buf[0] = reg >> 8;
> >> +	buf[1] = reg & 0xff;
> >> +
> >> +	val_be = cpu_to_be32(val);
> >> +	val_p = (u8 *)&val_be;
> >> +	buf_i = 2;
> >> +	val_i = 4 - len;
> >> +
> >> +	while (val_i < 4)
> >> +		buf[buf_i++] = val_p[val_i++];
> >> +
> >> +	if (i2c_master_send(client, buf, len + 2) != len + 2)
> >> +		return -EIO;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int ov4689_write_array(struct i2c_client *client,
> >> +			      const struct regval *regs)
> >> +{
> >> +	int ret = 0;
> >> +	u32 i;
> >> +
> >> +	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
> >> +		ret = ov4689_write_reg(client, regs[i].addr,
> >> +				       OV4689_REG_VALUE_08BIT, regs[i].val);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +/* Read registers up to 4 at a time */
> >> +static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
> >> +			   u32 *val)
> >> +{
> >> +	__be16 reg_addr_be = cpu_to_be16(reg);
> >> +	struct i2c_msg msgs[2];
> >> +	__be32 data_be = 0;
> >> +	u8 *data_be_p;
> >> +	int ret;
> >> +
> >> +	if (len > 4 || !len)
> >> +		return -EINVAL;
> >> +
> >> +	data_be_p = (u8 *)&data_be;
> >> +	/* Write register address */
> >> +	msgs[0].addr = client->addr;
> >> +	msgs[0].flags = 0;
> >> +	msgs[0].len = 2;
> >> +	msgs[0].buf = (u8 *)&reg_addr_be;
> >> +
> >> +	/* Read data from register */
> >> +	msgs[1].addr = client->addr;
> >> +	msgs[1].flags = I2C_M_RD;
> >> +	msgs[1].len = len;
> >> +	msgs[1].buf = &data_be_p[4 - len];
> >> +
> >> +	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> >> +	if (ret != ARRAY_SIZE(msgs))
> >> +		return -EIO;
> >> +
> >> +	*val = be32_to_cpu(data_be);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static void ov4689_fill_fmt(const struct ov4689_mode *mode,
> >> +			    struct v4l2_mbus_framefmt *fmt)
> >> +{
> >> +	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> >> +	fmt->width = mode->width;
> >> +	fmt->height = mode->height;
> >> +	fmt->field = V4L2_FIELD_NONE;
> >> +}
> >> +
> >> +static int ov4689_set_fmt(struct v4l2_subdev *sd,
> >> +			  struct v4l2_subdev_state *sd_state,
> >> +			  struct v4l2_subdev_format *fmt)
> >> +{
> >> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
> >> +	struct ov4689 *ov4689 = to_ov4689(sd);
> >> +
> >> +	/* only one mode supported for now */
> >> +	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int ov4689_get_fmt(struct v4l2_subdev *sd,
> >> +			  struct v4l2_subdev_state *sd_state,
> >> +			  struct v4l2_subdev_format *fmt)
> >> +{
> >> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
> >> +	struct ov4689 *ov4689 = to_ov4689(sd);
> >> +
> >> +	/* only one mode supported for now */
> >> +	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
> >> +				 struct v4l2_subdev_state *sd_state,
> >> +				 struct v4l2_subdev_mbus_code_enum *code)
> >> +{
> >> +	if (code->index != 0)
> >> +		return -EINVAL;
> >> +	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int ov4689_enum_frame_sizes(struct v4l2_subdev *sd,
> >> +				   struct v4l2_subdev_state *sd_state,
> >> +				   struct v4l2_subdev_frame_size_enum *fse)
> >> +{
> >> +	if (fse->index >= ARRAY_SIZE(supported_modes))
> >> +		return -EINVAL;
> >> +
> >> +	if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
> >> +		return -EINVAL;
> >> +
> >> +	fse->min_width = supported_modes[fse->index].width;
> >> +	fse->max_width = supported_modes[fse->index].width;
> >> +	fse->max_height = supported_modes[fse->index].height;
> >> +	fse->min_height = supported_modes[fse->index].height;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
> >> +{
> >> +	u32 val;
> >> +
> >> +	if (pattern)
> >> +		val = (pattern - 1) | OV4689_TEST_PATTERN_ENABLE;
> >> +	else
> >> +		val = OV4689_TEST_PATTERN_DISABLE;
> >> +
> >> +	return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN,
> >> +				OV4689_REG_VALUE_08BIT, val);
> >> +}
> >> +
> >> +static int ov4689_get_selection(struct v4l2_subdev *sd,
> >> +				struct v4l2_subdev_state *state,
> >> +				struct v4l2_subdev_selection *sel)
> >> +{
> >> +	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
> >> +		return -EINVAL;
> >> +
> >> +	switch (sel->target) {
> >> +	case V4L2_SEL_TGT_CROP_BOUNDS:
> >> +		sel->r.top = 0;
> >> +		sel->r.left = 0;
> >> +		sel->r.width = 2720;
> >> +		sel->r.height = 1536;
> >> +		return 0;
> >> +	case V4L2_SEL_TGT_CROP:
> >> +	case V4L2_SEL_TGT_CROP_DEFAULT:
> >> +		sel->r.top = 8;
> >> +		sel->r.left = 16;
> >> +		sel->r.width = 2688;
> >> +		sel->r.height = 1520;
> >> +		return 0;
> >> +	}
> >> +	return -EINVAL;
> >> +}
> >> +
> >> +static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
> >> +{
> >> +	struct ov4689 *ov4689 = to_ov4689(sd);
> >> +	struct i2c_client *client = ov4689->client;
> >> +	int ret = 0;
> >> +
> >> +	mutex_lock(&ov4689->mutex);
> >> +
> >> +	on = !!on;
> >> +	if (on == ov4689->streaming)
> >> +		goto unlock_and_return;
> >> +
> >> +	if (on) {
> >> +		ret = pm_runtime_resume_and_get(&client->dev);
> >> +		if (ret < 0)
> >> +			goto unlock_and_return;
> >> +
> >> +		ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
> >> +		if (ret) {
> >> +			pm_runtime_put(&client->dev);
> >> +			goto unlock_and_return;
> >> +		}
> >> +
> >> +		ret = ov4689_write_array(ov4689->client,
> >> +					 ov4689->cur_mode->reg_list);
> >> +		if (ret) {
> >> +			pm_runtime_put(&client->dev);
> >> +			goto unlock_and_return;
> >> +		}
> >> +
> >> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
> >> +				       OV4689_REG_VALUE_08BIT,
> >> +				       OV4689_MODE_STREAMING);
> >> +		if (ret) {
> >> +			pm_runtime_put(&client->dev);
> >> +			goto unlock_and_return;
> >> +		}
> >> +	} else {
> >> +		ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
> >> +				 OV4689_REG_VALUE_08BIT,
> >> +				 OV4689_MODE_SW_STANDBY);
> >> +		pm_runtime_put(&client->dev);
> >> +	}
> >> +
> >> +	ov4689->streaming = on;
> >> +
> >> +unlock_and_return:
> >> +	mutex_unlock(&ov4689->mutex);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +/* Calculate the delay in us by clock rate and clock cycles */
> >> +static inline u32 ov4689_cal_delay(u32 cycles)
> >> +{
> >> +	return DIV_ROUND_UP(cycles, OV4689_XVCLK_FREQ / 1000 / 1000);
> >> +}
> >> +
> >> +static int __ov4689_power_on(struct ov4689 *ov4689)
> >
> > Just a doubt on this name function. Why __ ? Is this name reserved for?
> 
> Just a leftover from BSP driver, will clean this up in v3.
> 
> >> +{
> >> +	struct device *dev = &ov4689->client->dev;
> >> +	u32 delay_us;
> >> +	int ret;
> >> +
> >> +	ret = clk_prepare_enable(ov4689->xvclk);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "Failed to enable xvclk\n");
> >> +		return ret;
> >> +	}
> >> +
> >> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
> >> +
> >> +	ret = regulator_bulk_enable(OV4689_NUM_SUPPLIES, ov4689->supplies);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "Failed to enable regulators\n");
> >> +		goto disable_clk;
> >> +	}
> >> +
> >> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
> >> +	usleep_range(500, 1000);
> >> +	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 0);
> >> +
> >> +	/* 8192 cycles prior to first SCCB transaction */
> >> +	delay_us = ov4689_cal_delay(8192);
> >> +	usleep_range(delay_us, delay_us * 2);
> >> +
> >> +	return 0;
> >> +
> >> +disable_clk:
> >> +	clk_disable_unprepare(ov4689->xvclk);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static void __ov4689_power_off(struct ov4689 *ov4689)
> >> +{
> >> +	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 1);
> >> +	clk_disable_unprepare(ov4689->xvclk);
> >> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
> >> +	regulator_bulk_disable(OV4689_NUM_SUPPLIES, ov4689->supplies);
> >> +}
> >> +
> >> +static int __maybe_unused ov4689_runtime_resume(struct device *dev)
> >> +{
> >> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> >> +	struct ov4689 *ov4689 = to_ov4689(sd);
> >> +
> >> +	return __ov4689_power_on(ov4689);
> >> +}
> >> +
> >> +static int __maybe_unused ov4689_runtime_suspend(struct device *dev)
> >> +{
> >> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> >> +	struct ov4689 *ov4689 = to_ov4689(sd);
> >> +
> >> +	__ov4689_power_off(ov4689);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> >> +static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> >> +{
> >> +	struct ov4689 *ov4689 = to_ov4689(sd);
> >> +	struct v4l2_mbus_framefmt *try_fmt;
> >> +
> >> +	mutex_lock(&ov4689->mutex);
> >> +
> >> +	try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0);
> >> +	/* Initialize try_fmt */
> >> +	ov4689_fill_fmt(&supported_modes[0], try_fmt);
> >
> > In this way instead of use magic number we can use this:
> >
> > 	ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt);
> 
> Ack, will fix in v3.
> 
> >> +
> >> +	mutex_unlock(&ov4689->mutex);
> >> +
> >> +	return 0;
> >> +}
> >> +#endif
> >> +
> >> +static const struct dev_pm_ops ov4689_pm_ops = {
> >> +	SET_RUNTIME_PM_OPS(ov4689_runtime_suspend, ov4689_runtime_resume, NULL)
> >> +};
> >> +
> >> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> >> +static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
> >> +	.open = ov4689_open,
> >> +};
> >> +#endif
> >> +
> >> +static const struct v4l2_subdev_video_ops ov4689_video_ops = {
> >> +	.s_stream = ov4689_s_stream,
> >> +};
> >> +
> >> +static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
> >> +	.enum_mbus_code = ov4689_enum_mbus_code,
> >> +	.enum_frame_size = ov4689_enum_frame_sizes,
> >> +	.get_fmt = ov4689_get_fmt,
> >> +	.set_fmt = ov4689_set_fmt,
> >> +	.get_selection = ov4689_get_selection,
> >> +};
> >> +
> >> +static const struct v4l2_subdev_ops ov4689_subdev_ops = {
> >> +	.video = &ov4689_video_ops,
> >> +	.pad = &ov4689_pad_ops,
> >> +};
> >> +
> >> +static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
> >> +{
> >> +	struct ov4689 *ov4689 =
> >> +		container_of(ctrl->handler, struct ov4689, ctrl_handler);
> >> +	struct i2c_client *client = ov4689->client;
> >> +	s64 max_expo;
> >> +	int ret;
> >> +
> >> +	/* Propagate change of current control to all related controls */
> >> +	switch (ctrl->id) {
> >> +	case V4L2_CID_VBLANK:
> >> +		/* Update max exposure while meeting expected vblanking */
> >> +		max_expo = ov4689->cur_mode->height + ctrl->val - 4;
> >> +		__v4l2_ctrl_modify_range(ov4689->exposure,
> >> +					 ov4689->exposure->minimum, max_expo,
> >> +					 ov4689->exposure->step,
> >> +					 ov4689->exposure->default_value);
> >> +		break;
> >> +	}
> >> +
> >> +	if (!pm_runtime_get_if_in_use(&client->dev))
> >> +		return 0;
> >> +
> >> +	switch (ctrl->id) {
> >> +	case V4L2_CID_EXPOSURE:
> >> +		/* 4 least significant bits of expsoure are fractional part */
> >> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE,
> >> +				       OV4689_REG_VALUE_24BIT, ctrl->val << 4);
> >> +		break;
> >> +	case V4L2_CID_ANALOGUE_GAIN:
> >> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H,
> >> +				       OV4689_REG_VALUE_08BIT,
> >> +				       (ctrl->val >> OV4689_GAIN_H_SHIFT) &
> >> +					       OV4689_GAIN_H_MASK);
> >> +		ret |= ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L,
> >> +					OV4689_REG_VALUE_08BIT,
> >> +					ctrl->val & OV4689_GAIN_L_MASK);
> >> +		break;
> >> +	case V4L2_CID_VBLANK:
> >> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS,
> >> +				       OV4689_REG_VALUE_16BIT,
> >> +				       ctrl->val + ov4689->cur_mode->height);
> >> +		break;
> >> +	case V4L2_CID_TEST_PATTERN:
> >> +		ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
> >> +		break;
> >> +	default:
> >> +		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
> >> +			 __func__, ctrl->id, ctrl->val);
> >> +		ret = -EINVAL;
> >> +		break;
> >> +	}
> >> +
> >> +	pm_runtime_put(&client->dev);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
> >> +	.s_ctrl = ov4689_set_ctrl,
> >> +};
> >> +
> >> +static int ov4689_initialize_controls(struct ov4689 *ov4689)
> >> +{
> >> +	struct i2c_client *client = v4l2_get_subdevdata(&ov4689->subdev);
> >> +	struct v4l2_fwnode_device_properties props;
> >> +	struct v4l2_ctrl_handler *handler;
> >> +	const struct ov4689_mode *mode;
> >> +	s64 exposure_max, vblank_def;
> >> +	struct v4l2_ctrl *ctrl;
> >> +	u32 h_blank, pixel_rate;
> >> +	int ret;
> >> +
> >> +	handler = &ov4689->ctrl_handler;
> >> +	mode = ov4689->cur_mode;
> >> +	ret = v4l2_ctrl_handler_init(handler, 10);
> >> +	if (ret)
> >> +		return ret;
> >> +	handler->lock = &ov4689->mutex;
> >> +
> >> +	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
> >> +				      link_freq_menu_items);
> >> +	if (ctrl)
> >> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> >> +
> >> +	pixel_rate = (link_freq_menu_items[0] * 2 * OV4689_LANES) /
> >> +		     OV4689_BITS_PER_SAMPLE;
> >> +	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, pixel_rate, 1,
> >> +			  pixel_rate);
> >> +
> >> +	h_blank = mode->hts_def - mode->width;
> >> +	ov4689->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
> >> +					   h_blank, h_blank, 1, h_blank);
> >> +	if (ov4689->hblank)
> >> +		ov4689->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> >> +
> >> +	vblank_def = mode->vts_def - mode->height;
> >> +	ov4689->vblank =
> >> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
> >> +				  vblank_def, OV4689_VTS_MAX - mode->height, 1,
> >> +				  vblank_def);
> >> +
> >> +	exposure_max = mode->vts_def - 4;
> >> +	ov4689->exposure =
> >> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_EXPOSURE,
> >> +				  OV4689_EXPOSURE_MIN, exposure_max,
> >> +				  OV4689_EXPOSURE_STEP, mode->exp_def);
> >> +
> >> +	ov4689->anal_gain =
> >> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops,
> >> +				  V4L2_CID_ANALOGUE_GAIN, OV4689_GAIN_MIN,
> >> +				  OV4689_GAIN_MAX, OV4689_GAIN_STEP,
> >> +				  OV4689_GAIN_DEFAULT);
> >> +
> >> +	ov4689->test_pattern =
> >> +		v4l2_ctrl_new_std_menu_items(handler, &ov4689_ctrl_ops,
> >> +					     V4L2_CID_TEST_PATTERN,
> >> +					     ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
> >> +					     0, 0, ov4689_test_pattern_menu);
> >> +
> >> +	if (handler->error) {
> >> +		ret = handler->error;
> >> +		dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n",
> >> +			ret);
> >> +		goto err_free_handler;
> >> +	}
> >> +
> >> +	ret = v4l2_fwnode_device_parse(&client->dev, &props);
> >> +	if (ret)
> >> +		goto err_free_handler;
> >> +
> >> +	ret = v4l2_ctrl_new_fwnode_properties(handler, &ov4689_ctrl_ops,
> >> +					      &props);
> >> +	if (ret)
> >> +		goto err_free_handler;
> >> +
> >> +	ov4689->subdev.ctrl_handler = handler;
> >> +
> >> +	return 0;
> >> +
> >> +err_free_handler:
> >> +	v4l2_ctrl_handler_free(handler);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int ov4689_check_sensor_id(struct ov4689 *ov4689,
> >> +				  struct i2c_client *client)
> >> +{
> >> +	struct device *dev = &ov4689->client->dev;
> >> +	u32 id = 0;
> >> +	int ret;
> >> +
> >> +	ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID,
> >> +			      OV4689_REG_VALUE_16BIT, &id);
> >> +	if (id != CHIP_ID) {
> >> +		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
> >> +		return -ENODEV;
> >> +	}
> >> +
> >> +	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int ov4689_configure_regulators(struct ov4689 *ov4689)
> >> +{
> >> +	unsigned int i;
> >> +
> >> +	for (i = 0; i < OV4689_NUM_SUPPLIES; i++)
> >> +		ov4689->supplies[i].supply = ov4689_supply_names[i];
> >> +
> >> +	return devm_regulator_bulk_get(&ov4689->client->dev,
> >> +				       OV4689_NUM_SUPPLIES, ov4689->supplies);
> >> +}
> >> +
> >> +static int ov4689_check_hwcfg(struct device *dev)
> >> +{
> >> +	struct fwnode_handle *fwnode = dev_fwnode(dev);
> >> +	struct v4l2_fwnode_endpoint bus_cfg = {
> >> +		.bus_type = V4L2_MBUS_CSI2_DPHY,
> >> +	};
> >> +	struct fwnode_handle *endpoint;
> >> +	unsigned int i;
> >> +	int ret;
> >> +
> >> +	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
> >> +	if (!endpoint)
> >> +		return -EPROBE_DEFER;
> >> +
> >> +	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
> >> +	fwnode_handle_put(endpoint);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
> >> +		dev_err(dev, "only a 4-lane CSI2 config is supported");
> >> +		ret = -EINVAL;
> >> +		goto out_free_bus_cfg;
> >> +	}
> >> +
> >> +	if (!bus_cfg.nr_of_link_frequencies) {
> >> +		dev_err(dev, "no link frequencies defined\n");
> >> +		ret = -EINVAL;
> >> +		goto out_free_bus_cfg;
> >> +	}
> >> +
> >> +	for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
> >> +		if (bus_cfg.link_frequencies[i] == OV4689_LINK_FREQ_500MHZ)
> >> +			break;
> >> +
> >> +	if (i == bus_cfg.nr_of_link_frequencies) {
> >> +		dev_err(dev, "supported link freq %ull not found\n",
> >> +			OV4689_LINK_FREQ_500MHZ);
> >> +		ret = -EINVAL;
> >> +		goto out_free_bus_cfg;
> >> +	}
> >> +
> >> +out_free_bus_cfg:
> >> +	v4l2_fwnode_endpoint_free(&bus_cfg);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int ov4689_probe(struct i2c_client *client,
> >> +			const struct i2c_device_id *id)
> >
> > We are sure that we need need i2c_device_id *id?
> 
> I see, will convert to single-argument .probe_new in v3.
> 
> >> +{
> >> +	struct device *dev = &client->dev;
> >> +	struct v4l2_subdev *sd;
> >> +	struct ov4689 *ov4689;
> >> +	int ret;
> >> +
> >> +	ret = ov4689_check_hwcfg(dev);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
> >> +	if (!ov4689)
> >> +		return -ENOMEM;
> >> +
> >> +	ov4689->client = client;
> >> +	ov4689->cur_mode = &supported_modes[0];
> >
> > Here aswell we can use:
> > 	ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520]
> 
> Ack.
> 
> >> +
> >> +	ov4689->xvclk = devm_clk_get(dev, "xvclk");
> >> +	if (IS_ERR(ov4689->xvclk)) {
> >> +		dev_err(dev, "Failed to get xvclk\n");
> >> +		return -EINVAL;
> >> +	}
> >
> > ^ I think is better to use devm_clk_get_optional instead of clck_get.
> > clck_get can fail in CPU's that use ACPI
> >
> >> +
> >> +	ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
> >> +		return ret;
> >> +	}
> >> +	if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
> >> +		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
> >
> >
> > What do you think about?
> > Thanks.
> 
> Unfortunately, I have no experience with ACPI-based devices. :(
> 
> Do you mean that in the case of an ACPI device and devm_clk_get_optional
> returning NULL we should assume that the clock is already enabled and
> will stay enabled during sensor operation? How should we distinguish it
> from the case of an OF-based system and clock just missing from device
> tree?

Not exaclty :)

I copy comment from [1]

if you use ov5693->xvclk to identify the ACPI vs OF use case shouldn't
you use the get_optionl() version ? Otherwise in the ACPI case you will have
-ENOENT if there's not 'xvclk' property and bail out.

Unless my understanding is wrong on ACPI we have "clock-frequency" and
on OF "xvclk" with an "assigned-clock-rates",

[1] https://patchwork.linuxtv.org/project/linux-media/patch/20220627150453.220292-5-tommaso.merciai@amarulasolutions.com/

Let me know if you need more details.

Regards,
Tommaso

> 
> As a note to myself:
> 
> -static const struct ov4689_mode supported_modes[]
> +static const struct ov4689_mode supported_modes[OV4689_NUM_MODES]
> 
> Overall, thanks for the review!
> 
> > Regards,
> > Tommaso
> >
> --
> Best regards,
> Mikhail Rudenko

-- 
Tommaso Merciai
Embedded Linux Engineer
tommaso.merciai@amarulasolutions.com
__________________________________

Amarula Solutions SRL
Via Le Canevare 30, 31100 Treviso, Veneto, IT
T. +39 042 243 5310
info@amarulasolutions.com
www.amarulasolutions.com

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

* Re: [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings
  2022-09-16 13:15       ` Tommaso Merciai
@ 2022-09-16 13:42         ` Mikhail Rudenko
  2022-09-19 13:16           ` Laurent Pinchart
  0 siblings, 1 reply; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-16 13:42 UTC (permalink / raw)
  To: Tommaso Merciai
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Christian Hemp, Arec Kao, Arnd Bergmann, Laurent Pinchart,
	Daniel Scally, Jimmy Su, linux-media, devicetree, linux-kernel


On 2022-09-16 at 15:15 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
> Hi Mikhail,
>
> On Thu, Sep 15, 2022 at 11:11:57PM +0300, Mikhail Rudenko wrote:
>>
>> Hi Tommaso,
>>
>> On 2022-09-13 at 16:05 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
>> > Hi Mikhail,
>> >
>> > On Sun, Sep 11, 2022 at 11:01:34PM +0300, Mikhail Rudenko wrote:
>> >> Add device-tree binding documentation for OV4689 image sensor driver,
>> >> and the relevant MAINTAINERS entries.
>> >>
>> >> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
>> >> ---
>> >>  .../bindings/media/i2c/ovti,ov4689.yaml       | 141 ++++++++++++++++++
>> >>  MAINTAINERS                                   |   7 +
>> >>  2 files changed, 148 insertions(+)
>> >>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>> >>
>> >> diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>> >> new file mode 100644
>> >> index 000000000000..376330b5572a
>> >> --- /dev/null
>> >> +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>> >> @@ -0,0 +1,141 @@
>> >> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> >> +%YAML 1.2
>> >> +---
>> >> +$id: http://devicetree.org/schemas/media/i2c/ovti,ov4689.yaml#
>> >> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> >> +
>> >> +title: Omnivision OV4689 CMOS
>> >> +
>> >> +maintainers:
>> >> +  - Mikhail Rudenko <mike.rudenko@gmail.com>
>> >> +
>> >> +description: |
>> >> +  The Omnivision OV4689 is a high performance, 1/3-inch, 4 megapixel
>> >> +  image sensor. Ihis chip supports high frame rate speeds up to 90 fps
>> >> +  at 2688x1520 resolution. It is programmable through an I2C
>> >> +  interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
>> >> +  connection.
>> >> +
>> >> +allOf:
>> >> +  - $ref: /schemas/media/video-interface-devices.yaml#
>> >> +
>> >> +properties:
>> >> +  compatible:
>> >> +    const: ovti,ov4689
>> >> +
>> >> +  reg:
>> >> +    maxItems: 1
>> >> +
>> >> +  clocks:
>> >> +    description:
>> >> +      External clock (XVCLK) for the sensor, 6-64 MHz
>> >> +    maxItems: 1
>> >> +
>> >> +  clock-names: true
>> >> +
>> >> +  dovdd-supply:
>> >> +    description:
>> >> +      Digital I/O voltage supply, 1.7-3.0 V
>> >> +
>> >> +  avdd-supply:
>> >> +    description:
>> >> +      Analog voltage supply, 2.6-3.0 V
>> >> +
>> >> +  dvdd-supply:
>> >> +    description:
>> >> +      Digital core voltage supply, 1.1-1.3 V
>> >> +
>> >> +  powerdown-gpios:
>> >> +    maxItems: 1
>> >> +    description:
>> >> +      GPIO connected to the powerdown pin (active low)
>> >> +
>> >> +  reset-gpios:
>> >> +    maxItems: 1
>> >> +    description:
>> >> +      GPIO connected to the reset pin (active low)
>> >> +
>> >> +  orientation: true
>> >> +
>> >> +  rotation: true
>> >> +
>> >> +  port:
>> >> +    $ref: /schemas/graph.yaml#/$defs/port-base
>> >> +    additionalProperties: false
>> >> +    description:
>> >> +      Output port node, single endpoint describing the CSI-2 transmitter
>> >> +
>> >> +    properties:
>> >> +      endpoint:
>> >> +        $ref: /schemas/media/video-interfaces.yaml#
>> >> +        unevaluatedProperties: false
>> >> +
>> >> +        properties:
>> >> +          data-lanes:
>> >> +            oneOf:
>> >> +              - items:
>> >> +                  - const: 1
>> >> +                  - const: 2
>> >> +                  - const: 3
>> >> +                  - const: 4
>> >> +              - items:
>> >> +                  - const: 1
>> >> +                  - const: 2
>> >> +              - items:
>> >> +                  - const: 1
>> >> +          link-frequencies: true
>> >> +
>> >> +        required:
>> >> +          - data-lanes
>> >> +          - link-frequencies
>> >> +
>> >> +required:
>> >> +  - compatible
>> >> +  - reg
>> >> +  - clocks
>> >> +  - clock-names
>> >> +  - dovdd-supply
>> >> +  - avdd-supply
>> >> +  - dvdd-supply
>> >> +  - powerdown-gpios
>> >> +  - reset-gpios
>> >> +  - port
>> >
>> > I think we don't need all of these entries as required.
>> > The only let me say "really" required are:
>> >
>> > - compatible
>> > - reg
>> > - clocks
>> > - port
>>
>> Thanks for the review! I agree that the driver may be modified to work
>> without powerdown and reset gpios and they are not required for sensor
>> operation. On contrary, supplies are obviously required. Of course, linux
>> provides dummy regulators if supplies are missing from device tree, but
>> I though the intention was to document hardware, not implementation
>> details. What do think of this?
>
> We have already discuss on this on the following thread sometimes ago :)
>
> https://www.patchwork.linux-fancy.com/project/linux-fancy/patch/20220630134835.592521-6-tommaso.merciai@amarulasolutions.com/
>
> Take a look and let me know.

Okay, if there already is a consensus regarding this matter, I'll make
the regulators optional in v3.

--
Best regards,
Mikhail Rudenko

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-16 13:34       ` Tommaso Merciai
@ 2022-09-16 13:44         ` Mikhail Rudenko
  2022-09-19  7:08           ` Tommaso Merciai
  2022-09-19  6:33         ` Sakari Ailus
  1 sibling, 1 reply; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-16 13:44 UTC (permalink / raw)
  To: Tommaso Merciai
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel


On 2022-09-16 at 15:34 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
> Hi Mikhail,
>
> On Thu, Sep 15, 2022 at 11:50:23PM +0300, Mikhail Rudenko wrote:
>>
>> Hi Tommaso,
>>
>> On 2022-09-14 at 17:51 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
>> > Hi Mikhail,
>> > I do a first round on reviewing your driver :)
>> >
>> > On Sun, Sep 11, 2022 at 11:01:35PM +0300, Mikhail Rudenko wrote:

<snip>

>> >> +
>> >> +	ov4689->xvclk = devm_clk_get(dev, "xvclk");
>> >> +	if (IS_ERR(ov4689->xvclk)) {
>> >> +		dev_err(dev, "Failed to get xvclk\n");
>> >> +		return -EINVAL;
>> >> +	}
>> >
>> > ^ I think is better to use devm_clk_get_optional instead of clck_get.
>> > clck_get can fail in CPU's that use ACPI
>> >
>> >> +
>> >> +	ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
>> >> +	if (ret < 0) {
>> >> +		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
>> >> +		return ret;
>> >> +	}
>> >> +	if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
>> >> +		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
>> >
>> >
>> > What do you think about?
>> > Thanks.
>>
>> Unfortunately, I have no experience with ACPI-based devices. :(
>>
>> Do you mean that in the case of an ACPI device and devm_clk_get_optional
>> returning NULL we should assume that the clock is already enabled and
>> will stay enabled during sensor operation? How should we distinguish it
>> from the case of an OF-based system and clock just missing from device
>> tree?
>
> Not exaclty :)
>
> I copy comment from [1]
>
> if you use ov5693->xvclk to identify the ACPI vs OF use case shouldn't
> you use the get_optionl() version ? Otherwise in the ACPI case you will have
> -ENOENT if there's not 'xvclk' property and bail out.
>
> Unless my understanding is wrong on ACPI we have "clock-frequency" and
> on OF "xvclk" with an "assigned-clock-rates",
>
> [1] https://patchwork.linuxtv.org/project/linux-media/patch/20220627150453.220292-5-tommaso.merciai@amarulasolutions.com/
>
> Let me know if you need more details.

Thanks for the pointer! I'll try to implement something along the lines
of your ov5693 series.

But I'm not sure that will be enough to support ACPI systems
correctly. What about lanes number and link frequency checks? Should
they be made conditional on CONFIG_OF? Anything else I don't know?

>
> Regards,
> Tommaso
>
--
Best regards,
Mikhail Rudenko

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-16  9:43       ` Krzysztof Kozlowski
@ 2022-09-16 21:51         ` Sakari Ailus
  0 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2022-09-16 21:51 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Mikhail Rudenko, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Randy Dunlap, Daniel Scally, Christian Hemp, Laurent Pinchart,
	Marek Vasut, linux-media, devicetree, linux-kernel

Hi Krzysztof, Mikhail,

On Fri, Sep 16, 2022 at 10:43:11AM +0100, Krzysztof Kozlowski wrote:
> On 15/09/2022 21:40, Mikhail Rudenko wrote:
> > 
> > Hi Krzysztof,
> > 
> > On 2022-09-12 at 12:56 +02, Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> wrote:
> >> On 11/09/2022 22:01, Mikhail Rudenko wrote:
> >>> +static const struct i2c_device_id ov4689_id[] = {
> >>> +	{ "ov4689", 0 },
> >>> +	{},
> >>> +};
> >>> +MODULE_DEVICE_TABLE(i2c, ov4689_id);
> >>> +
> >>> +static const struct of_device_id ov4689_of_match[] = {
> >>> +	{ .compatible = "ovti,ov4689" },
> >>> +	{},
> >>> +};
> >>> +MODULE_DEVICE_TABLE(of, ov4689_of_match);
> >>> +
> >>> +static struct i2c_driver ov4689_i2c_driver = {
> >>> +	.driver = {
> >>> +		.name = "ov4689",
> >>> +		.pm = &ov4689_pm_ops,
> >>> +		.of_match_table = of_match_ptr(ov4689_of_match),
> >>
> >> of_match_ptr is usually paired with maybe_unused, otherwise you will
> >> have compile test warnings.
> > 
> > I see. I think we could also use `#if IS_ENABLED(CONFIG_OF)` around
> > `ov4689_of_match` and the corresponding `MODULE_DEVICE_TABLE`. Is it
> > appropriate here?
> 
> Would work, but ifdefs are not nice. Just use maybe_unused. Warnings
> should disappear.

Neither is needed. Just drop of_match_ptr().

Apart from clock handling, it would probably work on ACPI, too. No need to
address that though.

-- 
Regards,

Sakari Ailus

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-16 13:34       ` Tommaso Merciai
  2022-09-16 13:44         ` Mikhail Rudenko
@ 2022-09-19  6:33         ` Sakari Ailus
  2022-09-19  7:11           ` Tommaso Merciai
  1 sibling, 1 reply; 41+ messages in thread
From: Sakari Ailus @ 2022-09-19  6:33 UTC (permalink / raw)
  To: Tommaso Merciai
  Cc: Mikhail Rudenko, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Randy Dunlap, Daniel Scally, Christian Hemp, Laurent Pinchart,
	Marek Vasut, linux-media, devicetree, linux-kernel

Hi Tommaso,

On Fri, Sep 16, 2022 at 03:34:01PM +0200, Tommaso Merciai wrote:
> > >> +	ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
> > >> +	if (ret < 0) {
> > >> +		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
> > >> +		return ret;
> > >> +	}
> > >> +	if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
> > >> +		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
> > >
> > >
> > > What do you think about?
> > > Thanks.
> > 
> > Unfortunately, I have no experience with ACPI-based devices. :(
> > 
> > Do you mean that in the case of an ACPI device and devm_clk_get_optional
> > returning NULL we should assume that the clock is already enabled and
> > will stay enabled during sensor operation? How should we distinguish it
> > from the case of an OF-based system and clock just missing from device
> > tree?
> 
> Not exaclty :)
> 
> I copy comment from [1]
> 
> if you use ov5693->xvclk to identify the ACPI vs OF use case shouldn't
> you use the get_optionl() version ? Otherwise in the ACPI case you will have
> -ENOENT if there's not 'xvclk' property and bail out.
> 
> Unless my understanding is wrong on ACPI we have "clock-frequency" and
> on OF "xvclk" with an "assigned-clock-rates",

Generally yes. It's also possible to have a clock in ACPI based system
although those clocks do not come from ACPI. See e.g.
drivers/platform/x86/intel/int3472/clk_and_regulator.c .

-- 
Sakari Ailus

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-15 21:27   ` Mikhail Rudenko
@ 2022-09-19  6:40     ` Sakari Ailus
  2022-09-19  7:01       ` Mikhail Rudenko
  0 siblings, 1 reply; 41+ messages in thread
From: Sakari Ailus @ 2022-09-19  6:40 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Dave Stevenson, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Jimmy Su, Arnd Bergmann, Arec Kao, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail,

On Fri, Sep 16, 2022 at 12:27:42AM +0300, Mikhail Rudenko wrote:
> 
> Hi Dave,
> 
> On 2022-09-14 at 10:58 +01, Dave Stevenson <dave.stevenson@raspberrypi.com> wrote:
> > Hi Mikhail
> >
> > On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko <mike.rudenko@gmail.com> wrote:
> >>
> >> Hello,
> >>
> >> this series implements support for Omnivision OV4689 image
> >> sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
> >> megapixel image sensor. Ihis chip supports high frame rate speeds up
> >> to 90 fps at 2688x1520 resolution. It is programmable through an I2C
> >> interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> >> connection.
> >>
> >> The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
> >> and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
> >> 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera module.
> >>
> >> While porting the driver, I stumbled upon two issues:
> >>
> >> (1) In the original driver, horizontal total size (HTS) was set to a
> >> value (2584) lower then the frame width (2688), resulting in negative
> >> hblank. In this driver, I increased HTS to 2688, but fps dropped from
> >> 29.88 to 28.73. What is the preferred way to handle this?
> >
> > This is one of the joys of sensors - they don't all work in the same way.
> >
> > I don't have an official datasheet for OV4689 from Omnivision, but
> > found one on the internet [1]. That should allow you to reverse the
> > PLL configuration to confirm that the pixel rate is the value you've
> > computed based on link frequency (they aren't necessarily related). Do
> > the frame rate calculations work using width + HBLANK, height +
> > VBLANK, and pixel rate?
> > The datasheet claims the sensor supports 2688x1520 @ 90 fps, so
> > something doesn't hold true between 4 data lanes at 500MHz/1Gbit/s per
> > lane when your default hts/vts is 2688x1554 and it only gives
> > 28.73fps.
> 
> Seems like those 90 fps is about CSI throughput, not actual sensor
> performance. I've checked the datasheet and the register values, and it
> seems like the pixel clock is 126 Mhz in this configuration (the maximum
> is 150 MHz according to the datasheet). This corresponds to a
> theoretical fps of 30.16 at hts=2688 and vts=1554. At the same time the
> observed fps is 28.73. I'm not sure where those 1.43 frames are lost,
> hope to do more experimentation with VTS and HTS over the weekend.
> 
> > I have seen modes in sensors where the HTS register is in units of 2
> > pixels, so what range of HTS (and VTS) values actually works on this
> > sensor? (I don't see it documented, but I'm not surprised).
> >
> > [1] https://cdn.hackaday.io/files/19354828041536/OV4689-OmniVision.pdf
> >
> >> (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
> >> gain is not linear across that range. Instead, it is piecewise linear
> >> (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
> >> 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
> >> with more linear segments in between. Rockchip's camera engine code
> >> chooses one of the above segments depenging on the desired gain
> >> value. The question is, how should we proceed keeping in mind
> >> libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
> >> libcamera will do the mapping, or the driver will do the mapping
> >> itself and expose some logical gain units not tied to the actual gain
> >> register value? Meanwhile, this driver conservatively exposes only
> >> 0x0-0xf8 gain register range.
> >
> > The datasheet linked above says "for the gain formula, please contact
> > your local OmniVision FAE" :-(
> > I would assume that the range is from 1x rather than 0x - people
> > rarely want a totally black image that 0x would give. Or is it ranges
> > of 1x - 2x, 2x - 4x, 4x - 8x, and 8x - 16x?
> 
> A picture is worth a thousand words, so I've attached the results of my
> experimentation with the gain register. They were obtained with Rockchip
> 3399, with AEC, AGC and black level subtraction disabled. The image was
> converted from 10-bit RGGB to 8-bit YUV 4:2:0 by the Rockchip ISP.

Based on that it looks like their medication may have been a little too
strong.

Could this be implemented so that the control value would be linear linear
but its range would correspond 1x--16x values?

libcamera will be able to cope with that.

> 
> > Other sensors expose the full range of the register via
> > V4L2_CID_ANALOGUE_GAIN, and require userspace (mainly libcamera now)
> > to know how to convert a gain into the register value. If the gain
> > range goes up to x16, then exposing that would be useful. I'd advocate
> > just exposing the full range of 0x000 - 0x7ff, as then you can have
> > the accuracy of 256 values between x1 to x2, but also the full range.
> 
> I also like this approach, although libcamera's CameraSensorHelper
> doesn't support piecewise-linear gain code mapping yet. Nevertheless,
> I believe exposing the full range is a good idea and will do so in v3.
> 
> > I might see if I can pick up one of these sensors and see if I can get
> > it running on a Raspberry Pi. Thanks for trying to upstream this -
> > it's nice to have such a range of sensor drivers to choose from.
> >
> >   Dave
> >
> 
> Thanks for your elucidating tips!
> 
> --
> Best regards,
> Mikhail Rudenko
> 



-- 
Sakari Ailus

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-19  6:40     ` Sakari Ailus
@ 2022-09-19  7:01       ` Mikhail Rudenko
  2022-09-19 10:31         ` Sakari Ailus
  0 siblings, 1 reply; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-19  7:01 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Dave Stevenson, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Jimmy Su, Arnd Bergmann, Arec Kao, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel


Hi Sakari,

On 2022-09-19 at 06:40 GMT, Sakari Ailus <sakari.ailus@linux.intel.com> wrote:

> Hi Mikhail,
>
> On Fri, Sep 16, 2022 at 12:27:42AM +0300, Mikhail Rudenko wrote:
>>
>> Hi Dave,
>>
>> On 2022-09-14 at 10:58 +01, Dave Stevenson <dave.stevenson@raspberrypi.com> wrote:
>> > Hi Mikhail
>> >
>> > On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko <mike.rudenko@gmail.com> wrote:
>> >>
>> >> Hello,
>> >>
>> >> this series implements support for Omnivision OV4689 image
>> >> sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
>> >> megapixel image sensor. Ihis chip supports high frame rate speeds up
>> >> to 90 fps at 2688x1520 resolution. It is programmable through an I2C
>> >> interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
>> >> connection.
>> >>
>> >> The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
>> >> and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
>> >> 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera
>> >> module.
>> >> While porting the driver, I stumbled upon two issues:
[snip]
>> >> (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
>> >> gain is not linear across that range. Instead, it is piecewise linear
>> >> (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
>> >> 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
>> >> with more linear segments in between. Rockchip's camera engine code
>> >> chooses one of the above segments depenging on the desired gain
>> >> value. The question is, how should we proceed keeping in mind
>> >> libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
>> >> libcamera will do the mapping, or the driver will do the mapping
>> >> itself and expose some logical gain units not tied to the actual gain
>> >> register value? Meanwhile, this driver conservatively exposes only
>> >> 0x0-0xf8 gain register range.
>> >
>> > The datasheet linked above says "for the gain formula, please contact
>> > your local OmniVision FAE" :-(
>> > I would assume that the range is from 1x rather than 0x - people
>> > rarely want a totally black image that 0x would give. Or is it ranges
>> > of 1x - 2x, 2x - 4x, 4x - 8x, and 8x - 16x?
>>
>> A picture is worth a thousand words, so I've attached the results of my
>> experimentation with the gain register. They were obtained with Rockchip
>> 3399, with AEC, AGC and black level subtraction disabled. The image was
>> converted from 10-bit RGGB to 8-bit YUV 4:2:0 by the Rockchip ISP.
>
> Based on that it looks like their medication may have been a little too
> strong.
>
> Could this be implemented so that the control value would be linear linear
> but its range would correspond 1x--16x values?
>
> libcamera will be able to cope with that.
>

According to the following fragment of the Rockchip camera engine sensor
configuration file for ov4689 [1]

    <Linear index="1" type="double" size="[4 7]">
       [1 2 128 0 1 128 255
        2 4 64 -248 1 376 504
        4 8 32 -756 1 884 1012
        8 16 16 -1784 1 1912 2040]
    </Linear>,

it uses gain register value range 128-255 for gain 1x-2x, 376-504 for
gain 2x-4x, 884-1024 for 4x-8x, and 1912-2040 for 8x-16x. Do you suggest
to implement this calculation in the sensor driver and expose some
linear "logical" gain to userspace (ranging, e.g., 128-2048 for gains
1x-16x)?


[1] https://github.com/aosp-rockchip/android_external_camera_engine_rkaiq/blob/quartz64/iqfiles/ov4689_JSD3425-C1_JSD3425-C1-36IRC-4M-F20.xml

--
Best regards,
Mikhail

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-16 13:44         ` Mikhail Rudenko
@ 2022-09-19  7:08           ` Tommaso Merciai
  0 siblings, 0 replies; 41+ messages in thread
From: Tommaso Merciai @ 2022-09-19  7:08 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail,

On Fri, Sep 16, 2022 at 04:44:31PM +0300, Mikhail Rudenko wrote:
> 
> On 2022-09-16 at 15:34 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
> > Hi Mikhail,
> >
> > On Thu, Sep 15, 2022 at 11:50:23PM +0300, Mikhail Rudenko wrote:
> >>
> >> Hi Tommaso,
> >>
> >> On 2022-09-14 at 17:51 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
> >> > Hi Mikhail,
> >> > I do a first round on reviewing your driver :)
> >> >
> >> > On Sun, Sep 11, 2022 at 11:01:35PM +0300, Mikhail Rudenko wrote:
> 
> <snip>
> 
> >> >> +
> >> >> +	ov4689->xvclk = devm_clk_get(dev, "xvclk");
> >> >> +	if (IS_ERR(ov4689->xvclk)) {
> >> >> +		dev_err(dev, "Failed to get xvclk\n");
> >> >> +		return -EINVAL;
> >> >> +	}
> >> >
> >> > ^ I think is better to use devm_clk_get_optional instead of clck_get.
> >> > clck_get can fail in CPU's that use ACPI
> >> >
> >> >> +
> >> >> +	ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
> >> >> +	if (ret < 0) {
> >> >> +		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
> >> >> +		return ret;
> >> >> +	}
> >> >> +	if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
> >> >> +		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
> >> >
> >> >
> >> > What do you think about?
> >> > Thanks.
> >>
> >> Unfortunately, I have no experience with ACPI-based devices. :(
> >>
> >> Do you mean that in the case of an ACPI device and devm_clk_get_optional
> >> returning NULL we should assume that the clock is already enabled and
> >> will stay enabled during sensor operation? How should we distinguish it
> >> from the case of an OF-based system and clock just missing from device
> >> tree?
> >
> > Not exaclty :)
> >
> > I copy comment from [1]
> >
> > if you use ov5693->xvclk to identify the ACPI vs OF use case shouldn't
> > you use the get_optionl() version ? Otherwise in the ACPI case you will have
> > -ENOENT if there's not 'xvclk' property and bail out.
> >
> > Unless my understanding is wrong on ACPI we have "clock-frequency" and
> > on OF "xvclk" with an "assigned-clock-rates",
> >
> > [1] https://patchwork.linuxtv.org/project/linux-media/patch/20220627150453.220292-5-tommaso.merciai@amarulasolutions.com/
> >
> > Let me know if you need more details.
> 
> Thanks for the pointer! I'll try to implement something along the lines
> of your ov5693 series.
> 
> But I'm not sure that will be enough to support ACPI systems
> correctly. What about lanes number and link frequency checks? Should
> they be made conditional on CONFIG_OF? Anything else I don't know?

In my opinion, lanes number and link frequency checks are ok :)
We don't need conditional CONFIG_OF.

fwnode* function support both ACPI and dts.

Thanks,
Tommaso

> 
> >
> > Regards,
> > Tommaso
> >
> --
> Best regards,
> Mikhail Rudenko

-- 
Tommaso Merciai
Embedded Linux Engineer
tommaso.merciai@amarulasolutions.com
__________________________________

Amarula Solutions SRL
Via Le Canevare 30, 31100 Treviso, Veneto, IT
T. +39 042 243 5310
info@amarulasolutions.com
www.amarulasolutions.com

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-19  6:33         ` Sakari Ailus
@ 2022-09-19  7:11           ` Tommaso Merciai
  0 siblings, 0 replies; 41+ messages in thread
From: Tommaso Merciai @ 2022-09-19  7:11 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Mikhail Rudenko, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Randy Dunlap, Daniel Scally, Christian Hemp, Laurent Pinchart,
	Marek Vasut, linux-media, devicetree, linux-kernel

Hi Sakari,

On Mon, Sep 19, 2022 at 06:33:12AM +0000, Sakari Ailus wrote:
> Hi Tommaso,
> 
> On Fri, Sep 16, 2022 at 03:34:01PM +0200, Tommaso Merciai wrote:
> > > >> +	ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
> > > >> +	if (ret < 0) {
> > > >> +		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
> > > >> +		return ret;
> > > >> +	}
> > > >> +	if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
> > > >> +		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
> > > >
> > > >
> > > > What do you think about?
> > > > Thanks.
> > > 
> > > Unfortunately, I have no experience with ACPI-based devices. :(
> > > 
> > > Do you mean that in the case of an ACPI device and devm_clk_get_optional
> > > returning NULL we should assume that the clock is already enabled and
> > > will stay enabled during sensor operation? How should we distinguish it
> > > from the case of an OF-based system and clock just missing from device
> > > tree?
> > 
> > Not exaclty :)
> > 
> > I copy comment from [1]
> > 
> > if you use ov5693->xvclk to identify the ACPI vs OF use case shouldn't
> > you use the get_optionl() version ? Otherwise in the ACPI case you will have
> > -ENOENT if there's not 'xvclk' property and bail out.
> > 
> > Unless my understanding is wrong on ACPI we have "clock-frequency" and
> > on OF "xvclk" with an "assigned-clock-rates",
> 
> Generally yes. It's also possible to have a clock in ACPI based system
> although those clocks do not come from ACPI. See e.g.
> drivers/platform/x86/intel/int3472/clk_and_regulator.c .

I save this :)
Thanks for sharing.

Regards,
Tommaso

> 
> -- 
> Sakari Ailus

-- 
Tommaso Merciai
Embedded Linux Engineer
tommaso.merciai@amarulasolutions.com
__________________________________

Amarula Solutions SRL
Via Le Canevare 30, 31100 Treviso, Veneto, IT
T. +39 042 243 5310
info@amarulasolutions.com
www.amarulasolutions.com

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-19  7:01       ` Mikhail Rudenko
@ 2022-09-19 10:31         ` Sakari Ailus
  2022-09-19 13:49           ` Laurent Pinchart
  0 siblings, 1 reply; 41+ messages in thread
From: Sakari Ailus @ 2022-09-19 10:31 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Dave Stevenson, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Jimmy Su, Arnd Bergmann, Arec Kao, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail,

On Mon, Sep 19, 2022 at 10:01:06AM +0300, Mikhail Rudenko wrote:
> 
> Hi Sakari,
> 
> On 2022-09-19 at 06:40 GMT, Sakari Ailus <sakari.ailus@linux.intel.com> wrote:
> 
> > Hi Mikhail,
> >
> > On Fri, Sep 16, 2022 at 12:27:42AM +0300, Mikhail Rudenko wrote:
> >>
> >> Hi Dave,
> >>
> >> On 2022-09-14 at 10:58 +01, Dave Stevenson <dave.stevenson@raspberrypi.com> wrote:
> >> > Hi Mikhail
> >> >
> >> > On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko <mike.rudenko@gmail.com> wrote:
> >> >>
> >> >> Hello,
> >> >>
> >> >> this series implements support for Omnivision OV4689 image
> >> >> sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
> >> >> megapixel image sensor. Ihis chip supports high frame rate speeds up
> >> >> to 90 fps at 2688x1520 resolution. It is programmable through an I2C
> >> >> interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> >> >> connection.
> >> >>
> >> >> The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
> >> >> and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
> >> >> 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera
> >> >> module.
> >> >> While porting the driver, I stumbled upon two issues:
> [snip]
> >> >> (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
> >> >> gain is not linear across that range. Instead, it is piecewise linear
> >> >> (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
> >> >> 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
> >> >> with more linear segments in between. Rockchip's camera engine code
> >> >> chooses one of the above segments depenging on the desired gain
> >> >> value. The question is, how should we proceed keeping in mind
> >> >> libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
> >> >> libcamera will do the mapping, or the driver will do the mapping
> >> >> itself and expose some logical gain units not tied to the actual gain
> >> >> register value? Meanwhile, this driver conservatively exposes only
> >> >> 0x0-0xf8 gain register range.
> >> >
> >> > The datasheet linked above says "for the gain formula, please contact
> >> > your local OmniVision FAE" :-(
> >> > I would assume that the range is from 1x rather than 0x - people
> >> > rarely want a totally black image that 0x would give. Or is it ranges
> >> > of 1x - 2x, 2x - 4x, 4x - 8x, and 8x - 16x?
> >>
> >> A picture is worth a thousand words, so I've attached the results of my
> >> experimentation with the gain register. They were obtained with Rockchip
> >> 3399, with AEC, AGC and black level subtraction disabled. The image was
> >> converted from 10-bit RGGB to 8-bit YUV 4:2:0 by the Rockchip ISP.
> >
> > Based on that it looks like their medication may have been a little too
> > strong.
> >
> > Could this be implemented so that the control value would be linear linear
> > but its range would correspond 1x--16x values?
> >
> > libcamera will be able to cope with that.
> >
> 
> According to the following fragment of the Rockchip camera engine sensor
> configuration file for ov4689 [1]
> 
>     <Linear index="1" type="double" size="[4 7]">
>        [1 2 128 0 1 128 255
>         2 4 64 -248 1 376 504
>         4 8 32 -756 1 884 1012
>         8 16 16 -1784 1 1912 2040]
>     </Linear>,
> 
> it uses gain register value range 128-255 for gain 1x-2x, 376-504 for
> gain 2x-4x, 884-1024 for 4x-8x, and 1912-2040 for 8x-16x. Do you suggest
> to implement this calculation in the sensor driver and expose some
> linear "logical" gain to userspace (ranging, e.g., 128-2048 for gains
> 1x-16x)?

Yes. This way the user space can somehow work without knowing this special
implementation, even though the granularity changes over the range. I guess
the granularity would need to be known in libcamera but that's a separate
issue.

-- 
Sakari Ailus

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

* Re: [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings
  2022-09-16 13:42         ` Mikhail Rudenko
@ 2022-09-19 13:16           ` Laurent Pinchart
  0 siblings, 0 replies; 41+ messages in thread
From: Laurent Pinchart @ 2022-09-19 13:16 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Tommaso Merciai, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Sakari Ailus, Hans Verkuil, Jacopo Mondi,
	Shawn Tu, Christian Hemp, Arec Kao, Arnd Bergmann, Daniel Scally,
	Jimmy Su, linux-media, devicetree, linux-kernel, Liam Girdwood,
	Mark Brown

CC'ing Liam and Mark.

On Fri, Sep 16, 2022 at 04:42:25PM +0300, Mikhail Rudenko wrote:
> On 2022-09-16 at 15:15 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
> > On Thu, Sep 15, 2022 at 11:11:57PM +0300, Mikhail Rudenko wrote:
> >> On 2022-09-13 at 16:05 +02, Tommaso Merciai <tommaso.merciai@amarulasolutions.com> wrote:
> >> > On Sun, Sep 11, 2022 at 11:01:34PM +0300, Mikhail Rudenko wrote:
> >> >> Add device-tree binding documentation for OV4689 image sensor driver,
> >> >> and the relevant MAINTAINERS entries.
> >> >>
> >> >> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
> >> >> ---
> >> >>  .../bindings/media/i2c/ovti,ov4689.yaml       | 141 ++++++++++++++++++
> >> >>  MAINTAINERS                                   |   7 +
> >> >>  2 files changed, 148 insertions(+)
> >> >>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> >> >>
> >> >> diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> >> >> new file mode 100644
> >> >> index 000000000000..376330b5572a
> >> >> --- /dev/null
> >> >> +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> >> >> @@ -0,0 +1,141 @@
> >> >> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> >> >> +%YAML 1.2
> >> >> +---
> >> >> +$id: http://devicetree.org/schemas/media/i2c/ovti,ov4689.yaml#
> >> >> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> >> >> +
> >> >> +title: Omnivision OV4689 CMOS
> >> >> +
> >> >> +maintainers:
> >> >> +  - Mikhail Rudenko <mike.rudenko@gmail.com>
> >> >> +
> >> >> +description: |
> >> >> +  The Omnivision OV4689 is a high performance, 1/3-inch, 4 megapixel
> >> >> +  image sensor. Ihis chip supports high frame rate speeds up to 90 fps
> >> >> +  at 2688x1520 resolution. It is programmable through an I2C
> >> >> +  interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> >> >> +  connection.
> >> >> +
> >> >> +allOf:
> >> >> +  - $ref: /schemas/media/video-interface-devices.yaml#
> >> >> +
> >> >> +properties:
> >> >> +  compatible:
> >> >> +    const: ovti,ov4689
> >> >> +
> >> >> +  reg:
> >> >> +    maxItems: 1
> >> >> +
> >> >> +  clocks:
> >> >> +    description:
> >> >> +      External clock (XVCLK) for the sensor, 6-64 MHz
> >> >> +    maxItems: 1
> >> >> +
> >> >> +  clock-names: true
> >> >> +
> >> >> +  dovdd-supply:
> >> >> +    description:
> >> >> +      Digital I/O voltage supply, 1.7-3.0 V
> >> >> +
> >> >> +  avdd-supply:
> >> >> +    description:
> >> >> +      Analog voltage supply, 2.6-3.0 V
> >> >> +
> >> >> +  dvdd-supply:
> >> >> +    description:
> >> >> +      Digital core voltage supply, 1.1-1.3 V
> >> >> +
> >> >> +  powerdown-gpios:
> >> >> +    maxItems: 1
> >> >> +    description:
> >> >> +      GPIO connected to the powerdown pin (active low)
> >> >> +
> >> >> +  reset-gpios:
> >> >> +    maxItems: 1
> >> >> +    description:
> >> >> +      GPIO connected to the reset pin (active low)
> >> >> +
> >> >> +  orientation: true
> >> >> +
> >> >> +  rotation: true
> >> >> +
> >> >> +  port:
> >> >> +    $ref: /schemas/graph.yaml#/$defs/port-base
> >> >> +    additionalProperties: false
> >> >> +    description:
> >> >> +      Output port node, single endpoint describing the CSI-2 transmitter
> >> >> +
> >> >> +    properties:
> >> >> +      endpoint:
> >> >> +        $ref: /schemas/media/video-interfaces.yaml#
> >> >> +        unevaluatedProperties: false
> >> >> +
> >> >> +        properties:
> >> >> +          data-lanes:
> >> >> +            oneOf:
> >> >> +              - items:
> >> >> +                  - const: 1
> >> >> +                  - const: 2
> >> >> +                  - const: 3
> >> >> +                  - const: 4
> >> >> +              - items:
> >> >> +                  - const: 1
> >> >> +                  - const: 2
> >> >> +              - items:
> >> >> +                  - const: 1
> >> >> +          link-frequencies: true
> >> >> +
> >> >> +        required:
> >> >> +          - data-lanes
> >> >> +          - link-frequencies
> >> >> +
> >> >> +required:
> >> >> +  - compatible
> >> >> +  - reg
> >> >> +  - clocks
> >> >> +  - clock-names
> >> >> +  - dovdd-supply
> >> >> +  - avdd-supply
> >> >> +  - dvdd-supply
> >> >> +  - powerdown-gpios
> >> >> +  - reset-gpios
> >> >> +  - port
> >> >
> >> > I think we don't need all of these entries as required.
> >> > The only let me say "really" required are:
> >> >
> >> > - compatible
> >> > - reg
> >> > - clocks
> >> > - port
> >>
> >> Thanks for the review! I agree that the driver may be modified to work
> >> without powerdown and reset gpios and they are not required for sensor
> >> operation. On contrary, supplies are obviously required. Of course, linux
> >> provides dummy regulators if supplies are missing from device tree, but
> >> I though the intention was to document hardware, not implementation
> >> details. What do think of this?
> >
> > We have already discuss on this on the following thread sometimes ago :)
> >
> > https://www.patchwork.linux-fancy.com/project/linux-fancy/patch/20220630134835.592521-6-tommaso.merciai@amarulasolutions.com/
> >
> > Take a look and let me know.
> 
> Okay, if there already is a consensus regarding this matter, I'll make
> the regulators optional in v3.

I always request the opposite during reviews :-)

Could we get an authoritative answer from the maintainers of the
regulator framework on this question, and document it somewhere ?

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-19 10:31         ` Sakari Ailus
@ 2022-09-19 13:49           ` Laurent Pinchart
  2022-09-20 15:55             ` Mikhail Rudenko
  2022-09-20 20:31             ` Mikhail Rudenko
  0 siblings, 2 replies; 41+ messages in thread
From: Laurent Pinchart @ 2022-09-19 13:49 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Mikhail Rudenko, Dave Stevenson, Mauro Carvalho Chehab,
	Rob Herring, Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi,
	Shawn Tu, Jimmy Su, Arnd Bergmann, Arec Kao, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hello,

On Mon, Sep 19, 2022 at 10:31:02AM +0000, Sakari Ailus wrote:
> On Mon, Sep 19, 2022 at 10:01:06AM +0300, Mikhail Rudenko wrote:
> > On 2022-09-19 at 06:40 GMT, Sakari Ailus wrote:
> > > On Fri, Sep 16, 2022 at 12:27:42AM +0300, Mikhail Rudenko wrote:
> > >> On 2022-09-14 at 10:58 +01, Dave Stevenson wrote:
> > >> > On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko wrote:
> > >> >>
> > >> >> Hello,
> > >> >>
> > >> >> this series implements support for Omnivision OV4689 image
> > >> >> sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
> > >> >> megapixel image sensor. Ihis chip supports high frame rate speeds up
> > >> >> to 90 fps at 2688x1520 resolution. It is programmable through an I2C
> > >> >> interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> > >> >> connection.
> > >> >>
> > >> >> The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
> > >> >> and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
> > >> >> 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera
> > >> >> module.
> > >> >> While porting the driver, I stumbled upon two issues:
> > 
> > [snip]
> > 
> > >> >> (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
> > >> >> gain is not linear across that range. Instead, it is piecewise linear
> > >> >> (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
> > >> >> 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
> > >> >> with more linear segments in between. Rockchip's camera engine code
> > >> >> chooses one of the above segments depenging on the desired gain
> > >> >> value. The question is, how should we proceed keeping in mind
> > >> >> libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
> > >> >> libcamera will do the mapping, or the driver will do the mapping
> > >> >> itself and expose some logical gain units not tied to the actual gain
> > >> >> register value? Meanwhile, this driver conservatively exposes only
> > >> >> 0x0-0xf8 gain register range.
> > >> >
> > >> > The datasheet linked above says "for the gain formula, please contact
> > >> > your local OmniVision FAE" :-(
> > >> > I would assume that the range is from 1x rather than 0x - people
> > >> > rarely want a totally black image that 0x would give. Or is it ranges
> > >> > of 1x - 2x, 2x - 4x, 4x - 8x, and 8x - 16x?
> > >>
> > >> A picture is worth a thousand words, so I've attached the results of my
> > >> experimentation with the gain register. They were obtained with Rockchip
> > >> 3399, with AEC, AGC and black level subtraction disabled. The image was
> > >> converted from 10-bit RGGB to 8-bit YUV 4:2:0 by the Rockchip ISP.

Is that full or limited range YUV ?

> > > Based on that it looks like their medication may have been a little too
> > > strong.
> > >
> > > Could this be implemented so that the control value would be linear linear
> > > but its range would correspond 1x--16x values?
> > >
> > > libcamera will be able to cope with that.
> > 
> > According to the following fragment of the Rockchip camera engine sensor
> > configuration file for ov4689 [1]
> > 
> >     <Linear index="1" type="double" size="[4 7]">
> >        [1 2 128 0 1 128 255
> >         2 4 64 -248 1 376 504
> >         4 8 32 -756 1 884 1012
> >         8 16 16 -1784 1 1912 2040]
> >     </Linear>,
> > 
> > it uses gain register value range 128-255 for gain 1x-2x, 376-504 for
> > gain 2x-4x, 884-1024 for 4x-8x, and 1912-2040 for 8x-16x. Do you suggest

That looks *really* weird. I would have understood [384, 511], [896,
1023] and [1920, 2047], but not those intervals.

The driver hardcodes bit 0x3503[2] to 1, which means "sensor gain
format". Maybe setting it to 0 ("real gain format") would produce saner
results ?

> > to implement this calculation in the sensor driver and expose some
> > linear "logical" gain to userspace (ranging, e.g., 128-2048 for gains
> > 1x-16x)?
> 
> Yes. This way the user space can somehow work without knowing this special
> implementation, even though the granularity changes over the range. I guess
> the granularity would need to be known in libcamera but that's a separate
> issue.

I can live with that.

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-19 13:49           ` Laurent Pinchart
@ 2022-09-20 15:55             ` Mikhail Rudenko
  2022-09-20 20:31             ` Mikhail Rudenko
  1 sibling, 0 replies; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-20 15:55 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Sakari Ailus, Dave Stevenson, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Jimmy Su, Arnd Bergmann, Arec Kao, Marek Vasut, linux-media,
	devicetree, linux-kernel

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


Hi Laurent,

On 2022-09-19 at 16:49 +03, Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hello,
>
> On Mon, Sep 19, 2022 at 10:31:02AM +0000, Sakari Ailus wrote:
>> On Mon, Sep 19, 2022 at 10:01:06AM +0300, Mikhail Rudenko wrote:
>> > On 2022-09-19 at 06:40 GMT, Sakari Ailus wrote:
>> > > On Fri, Sep 16, 2022 at 12:27:42AM +0300, Mikhail Rudenko wrote:
>> > >> On 2022-09-14 at 10:58 +01, Dave Stevenson wrote:
>> > >> > On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko wrote:
>> > >> >>
>> > >> >> Hello,
>> > >> >>
>> > >> >> this series implements support for Omnivision OV4689 image
>> > >> >> sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
>> > >> >> megapixel image sensor. Ihis chip supports high frame rate speeds up
>> > >> >> to 90 fps at 2688x1520 resolution. It is programmable through an I2C
>> > >> >> interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
>> > >> >> connection.
>> > >> >>
>> > >> >> The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
>> > >> >> and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
>> > >> >> 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera
>> > >> >> module.
>> > >> >> While porting the driver, I stumbled upon two issues:
>> >
>> > [snip]
>> >
>> > >> >> (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
>> > >> >> gain is not linear across that range. Instead, it is piecewise linear
>> > >> >> (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
>> > >> >> 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
>> > >> >> with more linear segments in between. Rockchip's camera engine code
>> > >> >> chooses one of the above segments depenging on the desired gain
>> > >> >> value. The question is, how should we proceed keeping in mind
>> > >> >> libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
>> > >> >> libcamera will do the mapping, or the driver will do the mapping
>> > >> >> itself and expose some logical gain units not tied to the actual gain
>> > >> >> register value? Meanwhile, this driver conservatively exposes only
>> > >> >> 0x0-0xf8 gain register range.
>> > >> >
>> > >> > The datasheet linked above says "for the gain formula, please contact
>> > >> > your local OmniVision FAE" :-(
>> > >> > I would assume that the range is from 1x rather than 0x - people
>> > >> > rarely want a totally black image that 0x would give. Or is it ranges
>> > >> > of 1x - 2x, 2x - 4x, 4x - 8x, and 8x - 16x?
>> > >>
>> > >> A picture is worth a thousand words, so I've attached the results of my
>> > >> experimentation with the gain register. They were obtained with Rockchip
>> > >> 3399, with AEC, AGC and black level subtraction disabled. The image was
>> > >> converted from 10-bit RGGB to 8-bit YUV 4:2:0 by the Rockchip ISP.
>
> Is that full or limited range YUV ?
>

That is default rkisp1 setting, supposedly limited range. This is
consistent with the fact that I have not seen brightness less than 19
and more than 235.

>> > > Based on that it looks like their medication may have been a
>> > > little too strong.
>> > >
>> > > Could this be implemented so that the control value would be linear linear
>> > > but its range would correspond 1x--16x values?
>> > >
>> > > libcamera will be able to cope with that.
>> >
>> > According to the following fragment of the Rockchip camera engine sensor
>> > configuration file for ov4689 [1]
>> >
>> >     <Linear index="1" type="double" size="[4 7]">
>> >        [1 2 128 0 1 128 255
>> >         2 4 64 -248 1 376 504
>> >         4 8 32 -756 1 884 1012
>> >         8 16 16 -1784 1 1912 2040]
>> >     </Linear>,
>> >
>> > it uses gain register value range 128-255 for gain 1x-2x, 376-504 for
>> > gain 2x-4x, 884-1024 for 4x-8x, and 1912-2040 for 8x-16x. Do you suggest
>
> That looks *really* weird. I would have understood [384, 511], [896,
> 1023] and [1920, 2047], but not those intervals.
>
> The driver hardcodes bit 0x3503[2] to 1, which means "sensor gain
> format". Maybe setting it to 0 ("real gain format") would produce saner
> results ?

I've also found this bit when running the tests. I'll attach high
quality plots and raw data for both formats to this message. Overall,
"real gain format" looks a bit saner, though it probably needs gain
codes > 2048 to achieve 16x gain (not tested).

I decided to use "sensor gain format" only because it was one used in
the BSP driver. Now it looks like "real gain format" may be a better
choice. I'll rerun the tests with gain code range 0-4096. If we'll have
0-16x gain over the range 2048-4096 in "real gain format" (this is
expected from existing data), I'll just expose that for
CID_ANALOGUE_GAIN, no need to use piecewise mapping.

>> > to implement this calculation in the sensor driver and expose some
>> > linear "logical" gain to userspace (ranging, e.g., 128-2048 for gains
>> > 1x-16x)?
>>
>> Yes. This way the user space can somehow work without knowing this special
>> implementation, even though the granularity changes over the range. I guess
>> the granularity would need to be known in libcamera but that's a separate
>> issue.
>
> I can live with that.

--
Best regards,
Mikhail Rudenko

[-- Attachment #2: gain-test.3503_00.pdf --]
[-- Type: application/pdf, Size: 17518 bytes --]

[-- Attachment #3: gain-test.3503_04.pdf --]
[-- Type: application/pdf, Size: 18767 bytes --]

[-- Attachment #4: gain-test.3503_00.log --]
[-- Type: text/plain, Size: 21416 bytes --]

16 19.18
17 19.21
18 19.24
19 19.28
20 19.31
21 19.34
22 19.36
23 19.38
24 19.38
25 19.41
26 19.46
27 19.48
28 19.51
29 19.53
30 19.57
31 19.58
32 19.63
33 19.66
34 19.70
35 19.75
36 19.77
37 19.81
38 19.83
39 19.90
40 19.94
41 19.96
42 19.98
43 20.04
44 20.06
45 20.11
46 20.13
47 20.17
48 20.23
49 20.26
50 20.29
51 20.34
52 20.38
53 20.43
54 20.47
55 20.54
56 20.58
57 20.63
58 20.68
59 20.73
60 20.77
61 20.83
62 20.87
63 20.95
64 21.02
65 21.06
66 21.10
67 21.15
68 21.22
69 21.27
70 21.33
71 21.38
72 21.44
73 21.50
74 21.65
75 21.62
76 21.67
77 21.78
78 21.83
79 21.89
80 21.95
81 22.00
82 22.06
83 22.12
84 22.19
85 22.24
86 22.31
87 22.37
88 22.44
89 22.51
90 22.58
91 22.63
92 22.70
93 22.77
94 22.83
95 22.89
96 23.00
97 23.05
98 23.12
99 23.18
100 23.25
101 23.31
102 23.38
103 23.45
104 23.52
105 23.60
106 23.67
107 23.73
108 23.80
109 23.87
110 23.94
111 24.00
112 24.08
113 24.14
114 24.21
115 24.28
116 24.35
117 24.42
118 24.49
119 24.56
120 24.64
121 24.71
122 24.77
123 24.85
124 24.92
125 24.99
126 25.06
127 25.11
128 20.73
129 21.03
130 20.95
131 20.86
132 20.90
133 21.19
134 20.98
135 21.02
136 21.32
137 21.37
138 21.35
139 21.21
140 21.27
141 21.56
142 21.61
143 21.41
144 21.71
145 21.78
146 21.67
147 21.84
148 21.89
149 21.79
150 22.07
151 21.92
152 22.21
153 22.05
154 22.35
155 22.41
156 22.26
157 22.57
158 22.56
159 22.47
160 22.56
161 22.81
162 22.72
163 22.79
164 23.09
165 23.05
166 23.18
167 23.26
168 23.19
169 23.28
170 23.37
171 23.44
172 23.53
173 23.62
174 23.70
175 23.79
176 23.88
177 23.96
178 24.04
179 24.13
180 24.22
181 24.34
182 24.41
183 24.49
184 24.59
185 24.69
186 24.78
187 24.87
188 24.96
189 25.06
190 25.14
191 25.23
192 25.34
193 25.44
194 25.54
195 25.63
196 25.72
197 25.83
198 25.93
199 26.02
200 26.13
201 26.23
202 26.32
203 26.42
204 26.51
205 26.62
206 26.91
207 26.81
208 26.91
209 27.02
210 27.12
211 27.21
212 27.30
213 27.41
214 27.50
215 27.60
216 27.71
217 27.81
218 27.91
219 28.01
220 28.10
221 28.21
222 28.30
223 28.39
224 28.49
225 28.59
226 28.69
227 28.79
228 28.89
229 28.99
230 29.08
231 29.18
232 29.29
233 29.39
234 29.49
235 29.58
236 29.68
237 29.78
238 29.87
239 29.96
240 30.07
241 30.17
242 30.27
243 30.37
244 30.46
245 30.56
246 30.66
247 30.75
248 30.85
249 30.96
250 31.05
251 31.15
252 31.24
253 31.35
254 31.44
255 31.54
256 22.33
257 22.33
258 22.40
259 22.40
260 22.47
261 22.47
262 22.54
263 22.54
264 22.61
265 22.62
266 22.71
267 22.71
268 22.79
269 22.79
270 22.87
271 22.87
272 22.99
273 22.99
274 23.08
275 23.08
276 23.18
277 23.18
278 23.27
279 23.27
280 23.37
281 23.38
282 23.50
283 23.50
284 23.60
285 23.60
286 23.72
287 23.72
288 23.86
289 23.86
290 23.99
291 23.99
292 24.11
293 24.11
294 24.24
295 24.24
296 24.38
297 24.38
298 24.53
299 24.52
300 24.67
301 24.67
302 24.81
303 24.81
304 24.97
305 24.97
306 25.12
307 25.12
308 25.27
309 25.27
310 25.42
311 25.42
312 25.58
313 25.58
314 25.76
315 25.76
316 25.91
317 25.91
318 26.07
319 25.99
320 26.25
321 26.25
322 26.43
323 26.43
324 26.60
325 26.60
326 26.77
327 26.77
328 26.87
329 26.94
330 27.12
331 27.13
332 27.27
333 27.22
334 27.47
335 27.47
336 27.67
337 27.67
338 27.86
339 27.86
340 28.03
341 28.03
342 28.20
343 28.20
344 28.37
345 28.37
346 28.56
347 28.56
348 28.74
349 28.75
350 28.93
351 28.93
352 29.13
353 29.13
354 29.33
355 29.33
356 29.51
357 29.51
358 29.70
359 29.69
360 29.88
361 29.88
362 30.09
363 30.09
364 30.28
365 30.27
366 30.46
367 30.46
368 30.66
369 30.65
370 30.86
371 30.86
372 30.97
373 30.98
374 31.17
375 31.24
376 31.44
377 31.44
378 31.56
379 31.65
380 31.83
381 31.83
382 32.03
383 32.03
384 32.26
385 32.26
386 32.47
387 32.47
388 32.62
389 32.67
390 32.86
391 32.86
392 33.06
393 33.05
394 33.27
395 33.27
396 33.47
397 33.47
398 33.67
399 33.59
400 33.88
401 33.88
402 34.07
403 34.01
404 34.22
405 34.21
406 34.48
407 34.49
408 34.68
409 34.68
410 34.81
411 34.81
412 35.00
413 35.09
414 35.28
415 35.07
416 35.50
417 35.29
418 35.57
419 35.72
420 35.93
421 35.85
422 36.13
423 35.91
424 36.30
425 36.12
426 36.46
427 36.33
428 36.67
429 36.67
430 36.94
431 36.73
432 37.07
433 37.07
434 37.28
435 37.22
436 37.35
437 37.35
438 37.76
439 37.55
440 37.87
441 37.74
442 38.02
443 38.02
444 38.29
445 38.29
446 38.36
447 38.36
448 38.57
449 38.57
450 38.79
451 38.79
452 38.99
453 38.99
454 39.19
455 39.19
456 39.39
457 39.39
458 39.60
459 39.60
460 39.80
461 39.80
462 39.99
463 40.00
464 40.21
465 40.21
466 40.49
467 40.42
468 40.63
469 40.63
470 40.83
471 40.83
472 41.03
473 41.03
474 41.25
475 41.24
476 41.44
477 41.44
478 41.64
479 41.64
480 41.85
481 41.85
482 42.06
483 42.06
484 42.27
485 42.27
486 42.46
487 42.46
488 42.66
489 42.66
490 42.88
491 42.88
492 43.08
493 43.08
494 43.28
495 43.28
496 43.49
497 43.48
498 43.70
499 43.70
500 43.90
501 43.90
502 44.10
503 44.10
504 44.29
505 44.29
506 44.51
507 44.50
508 44.70
509 44.71
510 44.90
511 44.90
512 24.07
513 24.07
514 24.07
515 24.07
516 24.22
517 24.21
518 24.21
519 24.21
520 24.36
521 24.36
522 24.36
523 24.36
524 24.51
525 24.51
526 24.51
527 24.51
528 24.68
529 24.68
530 24.68
531 24.68
532 24.86
533 24.86
534 24.86
535 24.86
536 25.04
537 25.05
538 25.04
539 25.04
540 25.25
541 25.25
542 25.25
543 25.25
544 25.50
545 25.50
546 25.50
547 25.50
548 25.75
549 25.75
550 25.75
551 25.75
552 25.97
553 25.97
554 25.97
555 25.97
556 26.21
557 26.21
558 26.21
559 26.14
560 26.46
561 26.46
562 26.46
563 26.46
564 26.75
565 26.68
566 26.75
567 26.75
568 27.02
569 27.02
570 27.02
571 27.02
572 27.31
573 27.31
574 27.31
575 27.24
576 27.57
577 27.43
578 27.43
579 27.64
580 27.96
581 27.96
582 27.96
583 27.96
584 28.27
585 28.28
586 28.27
587 28.27
588 28.59
589 28.38
590 28.59
591 28.59
592 28.92
593 28.85
594 28.92
595 28.92
596 29.28
597 29.28
598 29.28
599 29.23
600 29.62
601 29.40
602 29.40
603 29.54
604 29.96
605 29.96
606 29.96
607 29.96
608 30.34
609 30.34
610 30.34
611 30.34
612 30.73
613 30.65
614 30.73
615 30.73
616 31.03
617 31.03
618 31.10
619 31.03
620 31.39
621 31.39
622 31.39
623 31.26
624 31.84
625 31.76
626 31.63
627 31.63
628 32.16
629 32.16
630 32.16
631 32.17
632 32.54
633 32.55
634 32.57
635 32.55
636 32.97
637 32.92
638 32.97
639 32.93
640 33.33
641 33.20
642 33.20
643 33.21
644 33.75
645 33.75
646 33.75
647 33.76
648 34.02
649 34.02
650 34.23
651 34.16
652 34.48
653 34.55
654 34.41
655 34.63
656 34.94
657 34.94
658 34.94
659 35.03
660 35.38
661 35.37
662 35.37
663 35.44
664 35.78
665 35.65
666 35.65
667 35.66
668 36.11
669 36.05
670 36.18
671 36.06
672 36.55
673 36.48
674 36.49
675 36.63
676 36.98
677 37.05
678 36.91
679 36.92
680 37.33
681 37.46
682 37.33
683 37.35
684 37.72
685 37.86
686 37.73
687 37.80
688 38.26
689 38.13
690 38.13
691 38.14
692 38.78
693 38.69
694 38.56
695 38.64
696 38.97
697 38.97
698 38.97
699 39.05
700 39.37
701 39.37
702 39.37
703 39.39
704 40.01
705 39.80
706 39.93
707 39.81
708 40.23
709 40.24
710 40.24
711 40.25
712 40.79
713 40.66
714 40.66
715 40.78
716 41.20
717 41.28
718 41.20
719 41.16
720 41.61
721 41.61
722 41.62
723 41.63
724 42.05
725 42.05
726 42.06
727 41.94
728 42.53
729 42.47
730 42.34
731 42.49
732 42.88
733 42.75
734 42.89
735 42.98
736 43.25
737 43.31
738 43.19
739 43.34
740 43.63
741 43.63
742 43.77
743 43.65
744 44.19
745 44.06
746 44.20
747 44.30
748 44.46
749 44.46
750 44.60
751 44.48
752 44.93
753 45.00
754 45.00
755 45.02
756 45.45
757 45.31
758 45.32
759 45.47
760 45.86
761 45.73
762 45.74
763 45.89
764 46.27
765 46.20
766 46.15
767 46.17
768 46.62
769 46.68
770 46.62
771 46.65
772 47.07
773 47.07
774 47.08
775 47.11
776 47.51
777 47.51
778 47.64
779 47.54
780 47.92
781 47.92
782 47.93
783 48.09
784 48.33
785 48.34
786 48.34
787 48.37
788 48.91
789 48.78
790 48.79
791 48.82
792 49.21
793 49.21
794 49.22
795 49.25
796 49.61
797 49.62
798 49.63
799 49.66
800 50.06
801 50.06
802 49.99
803 50.11
804 50.51
805 50.51
806 50.44
807 50.56
808 50.95
809 50.95
810 50.96
811 50.99
812 51.37
813 51.37
814 51.38
815 51.42
816 51.78
817 51.78
818 51.80
819 51.83
820 52.23
821 52.23
822 52.24
823 52.18
824 52.56
825 52.62
826 52.67
827 52.68
828 53.06
829 52.97
830 52.98
831 53.10
832 53.42
833 53.50
834 53.52
835 53.55
836 53.95
837 53.87
838 53.88
839 54.00
840 54.39
841 54.39
842 54.32
843 54.37
844 54.71
845 54.71
846 54.73
847 54.85
848 55.13
849 55.06
850 55.14
851 55.26
852 55.67
853 55.51
854 55.68
855 55.71
856 56.00
857 56.00
858 56.02
859 56.05
860 56.28
861 56.41
862 56.43
863 56.46
864 56.84
865 56.84
866 56.86
867 56.90
868 57.29
869 57.29
870 57.25
871 57.34
872 57.72
873 57.72
874 57.80
875 57.78
876 58.13
877 58.13
878 58.15
879 58.19
880 58.54
881 58.54
882 58.57
883 58.60
884 58.86
885 58.93
886 59.01
887 59.06
888 59.41
889 59.41
890 59.44
891 59.47
892 59.69
893 59.69
894 59.72
895 59.76
896 60.12
897 60.26
898 60.15
899 60.31
900 60.71
901 60.70
902 60.60
903 60.77
904 61.07
905 61.13
906 61.16
907 61.14
908 61.42
909 61.55
910 61.45
911 61.55
912 61.90
913 61.96
914 61.97
915 61.91
916 62.40
917 62.28
918 62.43
919 62.47
920 62.70
921 62.83
922 62.84
923 62.89
924 63.24
925 63.11
926 63.12
927 63.30
928 63.55
929 63.55
930 63.70
931 63.62
932 64.13
933 64.12
934 64.01
935 64.06
936 64.43
937 64.43
938 64.45
939 64.50
940 64.83
941 64.84
942 64.86
943 64.91
944 65.37
945 65.32
946 65.27
947 65.45
948 65.69
949 65.69
950 65.87
951 65.88
952 66.10
953 66.10
954 66.12
955 66.29
956 66.51
957 66.51
958 66.53
959 66.57
960 66.93
961 66.93
962 67.02
963 66.99
964 67.37
965 67.50
966 67.40
967 67.44
968 67.80
969 67.80
970 67.83
971 68.00
972 68.21
973 68.28
974 68.24
975 68.28
976 68.63
977 68.76
978 68.65
979 68.82
980 69.21
981 69.07
982 69.23
983 69.15
984 69.49
985 69.47
986 69.52
987 69.69
988 69.91
989 69.90
990 69.93
991 69.98
992 70.33
993 70.33
994 70.36
995 70.40
996 70.77
997 70.79
998 70.78
999 70.85
1000 71.20
1001 71.20
1002 71.23
1003 71.27
1004 71.61
1005 71.52
1006 71.54
1007 71.67
1008 72.01
1009 72.02
1010 72.04
1011 72.08
1012 72.45
1013 72.57
1014 72.48
1015 72.52
1016 72.86
1017 72.99
1018 72.80
1019 72.93
1020 73.19
1021 73.27
1022 73.29
1023 73.35
1024 26.32
1025 26.54
1026 26.54
1027 26.47
1028 26.43
1029 26.48
1030 26.49
1031 26.54
1032 26.87
1033 26.87
1034 26.87
1035 26.87
1036 26.77
1037 26.77
1038 26.88
1039 26.81
1040 27.20
1041 27.21
1042 27.14
1043 27.21
1044 27.14
1045 27.14
1046 27.21
1047 27.21
1048 27.42
1049 27.50
1050 27.57
1051 27.50
1052 27.36
1053 27.57
1054 27.57
1055 27.53
1056 27.89
1057 27.89
1058 27.96
1059 27.75
1060 27.89
1061 27.89
1062 27.78
1063 27.89
1064 28.19
1065 28.33
1066 28.41
1067 28.33
1068 28.19
1069 28.41
1070 28.41
1071 28.41
1072 28.75
1073 28.80
1074 28.83
1075 28.75
1076 28.83
1077 28.78
1078 28.61
1079 28.61
1080 29.28
1081 29.28
1082 29.28
1083 29.20
1084 29.17
1085 29.21
1086 29.21
1087 29.29
1088 29.76
1089 29.76
1090 29.76
1091 29.76
1092 29.76
1093 29.76
1094 29.63
1095 29.76
1096 30.39
1097 30.39
1098 30.39
1099 30.39
1100 30.32
1101 30.32
1102 30.39
1103 30.40
1104 30.94
1105 30.86
1106 30.72
1107 30.86
1108 30.82
1109 30.82
1110 30.72
1111 30.73
1112 31.41
1113 31.41
1114 31.41
1115 31.28
1116 31.28
1117 31.41
1118 31.28
1119 31.29
1120 32.06
1121 31.99
1122 31.85
1123 31.99
1124 31.85
1125 31.85
1126 31.99
1127 31.93
1128 32.63
1129 32.71
1130 32.63
1131 32.68
1132 32.56
1133 32.71
1134 32.71
1135 32.64
1136 33.20
1137 33.17
1138 33.24
1139 33.10
1140 33.20
1141 33.11
1142 33.25
1143 33.25
1144 33.87
1145 33.89
1146 33.73
1147 33.86
1148 33.86
1149 33.87
1150 33.96
1151 33.75
1152 34.53
1153 34.60
1154 34.60
1155 34.60
1156 34.46
1157 34.47
1158 34.61
1159 34.62
1160 35.32
1161 35.19
1162 35.26
1163 35.32
1164 35.32
1165 35.29
1166 35.21
1167 35.42
1168 36.08
1169 36.03
1170 35.89
1171 35.89
1172 36.03
1173 35.90
1174 35.98
1175 36.05
1176 36.80
1177 36.59
1178 36.72
1179 36.59
1180 36.60
1181 36.60
1182 36.61
1183 36.62
1184 37.29
1185 37.30
1186 37.43
1187 37.29
1188 37.43
1189 37.44
1190 37.44
1191 37.46
1192 38.20
1193 38.19
1194 38.13
1195 38.06
1196 38.26
1197 38.08
1198 38.22
1199 38.23
1200 38.79
1201 38.93
1202 38.79
1203 39.00
1204 38.93
1205 38.81
1206 38.94
1207 38.90
1208 39.52
1209 39.52
1210 39.52
1211 39.52
1212 39.66
1213 39.72
1214 39.67
1215 39.56
1216 40.52
1217 40.44
1218 40.44
1219 40.44
1220 40.53
1221 40.32
1222 40.47
1223 40.48
1224 41.11
1225 41.25
1226 41.11
1227 41.12
1228 41.26
1229 41.35
1230 41.28
1231 41.38
1232 42.03
1233 42.03
1234 42.03
1235 41.90
1236 42.04
1237 41.92
1238 42.07
1239 42.09
1240 42.80
1241 42.80
1242 42.67
1243 42.80
1244 42.68
1245 42.82
1246 42.83
1247 42.85
1248 43.43
1249 43.56
1250 43.50
1251 43.56
1252 43.58
1253 43.58
1254 43.48
1255 43.48
1256 44.27
1257 44.27
1258 44.27
1259 44.27
1260 44.41
1261 44.29
1262 44.31
1263 44.32
1264 45.18
1265 45.05
1266 45.05
1267 45.12
1268 45.19
1269 45.21
1270 45.10
1271 45.18
1272 45.96
1273 45.82
1274 45.96
1275 45.96
1276 45.83
1277 45.86
1278 45.87
1279 45.88
1280 46.69
1281 46.82
1282 46.69
1283 46.70
1284 46.70
1285 46.72
1286 46.75
1287 46.88
1288 47.55
1289 47.56
1290 47.68
1291 47.56
1292 47.57
1293 47.59
1294 47.62
1295 47.62
1296 48.38
1297 48.38
1298 48.38
1299 48.52
1300 48.40
1301 48.55
1302 48.43
1303 48.46
1304 49.17
1305 49.18
1306 49.17
1307 49.19
1308 49.20
1309 49.22
1310 49.23
1311 49.25
1312 49.88
1313 49.97
1314 49.97
1315 49.98
1316 49.99
1317 50.02
1318 50.03
1319 50.05
1320 50.82
1321 50.84
1322 50.84
1323 50.85
1324 50.86
1325 50.86
1326 50.90
1327 50.93
1328 51.66
1329 51.66
1330 51.67
1331 51.59
1332 51.69
1333 51.72
1334 51.72
1335 51.75
1336 52.39
1337 52.47
1338 52.47
1339 52.49
1340 52.44
1341 52.53
1342 52.54
1343 52.48
1344 53.28
1345 53.37
1346 53.37
1347 53.30
1348 53.40
1349 53.34
1350 53.43
1351 53.47
1352 54.25
1353 54.16
1354 54.25
1355 54.18
1356 54.27
1357 54.22
1358 54.32
1359 54.26
1360 55.09
1361 55.00
1362 55.00
1363 55.03
1364 55.03
1365 55.06
1366 55.08
1367 55.11
1368 55.80
1369 55.90
1370 55.90
1371 55.82
1372 55.91
1373 55.86
1374 55.88
1375 55.90
1376 56.61
1377 56.62
1378 56.62
1379 56.63
1380 56.65
1381 56.76
1382 56.70
1383 56.71
1384 57.49
1385 57.49
1386 57.49
1387 57.37
1388 57.53
1389 57.55
1390 57.58
1391 57.47
1392 58.25
1393 58.32
1394 58.32
1395 58.32
1396 58.37
1397 58.46
1398 58.40
1399 58.43
1400 59.00
1401 59.00
1402 59.13
1403 59.14
1404 59.04
1405 59.18
1406 59.16
1407 59.23
1408 59.98
1409 59.99
1410 59.98
1411 59.93
1412 59.97
1413 60.04
1414 60.07
1415 60.10
1416 60.87
1417 60.74
1418 60.88
1419 60.88
1420 60.86
1421 60.81
1422 60.97
1423 60.99
1424 61.72
1425 61.60
1426 61.73
1427 61.61
1428 61.64
1429 61.66
1430 61.82
1431 61.84
1432 62.42
1433 62.41
1434 62.41
1435 62.43
1436 62.46
1437 62.48
1438 62.64
1439 62.66
1440 63.22
1441 63.23
1442 63.36
1443 63.24
1444 63.41
1445 63.30
1446 63.33
1447 63.35
1448 64.12
1449 64.11
1450 64.12
1451 64.14
1452 64.29
1453 64.18
1454 64.21
1455 64.24
1456 64.91
1457 64.94
1458 64.94
1459 65.08
1460 65.13
1461 65.01
1462 65.16
1463 65.07
1464 65.81
1465 65.75
1466 65.75
1467 65.76
1468 65.80
1469 65.82
1470 65.84
1471 65.88
1472 66.60
1473 66.59
1474 66.60
1475 66.75
1476 66.66
1477 66.67
1478 66.69
1479 66.74
1480 67.48
1481 67.47
1482 67.48
1483 67.50
1484 67.53
1485 67.56
1486 67.55
1487 67.62
1488 68.39
1489 68.32
1490 68.33
1491 68.34
1492 68.38
1493 68.39
1494 68.42
1495 68.47
1496 69.04
1497 69.13
1498 69.05
1499 69.15
1500 69.09
1501 69.34
1502 69.15
1503 69.29
1504 69.92
1505 69.94
1506 69.95
1507 69.92
1508 70.12
1509 69.94
1510 69.97
1511 70.01
1512 70.81
1513 70.79
1514 70.83
1515 70.85
1516 70.87
1517 70.91
1518 70.94
1519 70.87
1520 71.62
1521 71.64
1522 71.58
1523 71.67
1524 71.69
1525 71.73
1526 71.66
1527 71.78
1528 72.44
1529 72.42
1530 72.46
1531 72.47
1532 72.50
1533 72.54
1534 72.57
1535 72.57
1536 73.40
1537 73.39
1538 73.31
1539 73.33
1540 73.36
1541 73.49
1542 73.51
1543 73.52
1544 74.27
1545 74.26
1546 74.20
1547 74.29
1548 74.32
1549 74.30
1550 74.39
1551 74.33
1552 75.09
1553 75.01
1554 75.11
1555 75.05
1556 75.07
1557 75.11
1558 75.22
1559 75.26
1560 75.80
1561 75.90
1562 75.90
1563 75.92
1564 75.86
1565 75.90
1566 75.94
1567 75.95
1568 76.59
1569 76.67
1570 76.60
1571 76.62
1572 76.64
1573 76.69
1574 76.78
1575 76.74
1576 77.51
1577 77.50
1578 77.55
1579 77.47
1580 77.50
1581 77.52
1582 77.57
1583 77.60
1584 78.23
1585 78.23
1586 78.25
1587 78.26
1588 78.31
1589 78.30
1590 78.35
1591 78.38
1592 78.99
1593 78.98
1594 79.00
1595 79.02
1596 79.04
1597 79.06
1598 79.13
1599 79.15
1600 79.83
1601 79.83
1602 79.84
1603 79.96
1604 79.89
1605 79.91
1606 79.96
1607 79.99
1608 80.65
1609 80.65
1610 80.72
1611 80.68
1612 80.70
1613 80.73
1614 80.78
1615 80.75
1616 81.41
1617 81.41
1618 81.44
1619 81.46
1620 81.48
1621 81.52
1622 81.56
1623 81.58
1624 82.17
1625 82.15
1626 82.17
1627 82.20
1628 82.22
1629 82.25
1630 82.30
1631 82.32
1632 82.91
1633 82.89
1634 82.91
1635 82.94
1636 82.96
1637 82.98
1638 83.04
1639 83.07
1640 83.69
1641 83.68
1642 83.77
1643 83.73
1644 83.76
1645 83.77
1646 83.80
1647 83.87
1648 84.42
1649 84.43
1650 84.43
1651 84.47
1652 84.58
1653 84.51
1654 84.54
1655 84.59
1656 85.08
1657 85.15
1658 85.15
1659 85.19
1660 85.21
1661 85.24
1662 85.27
1663 85.32
1664 85.94
1665 85.94
1666 85.93
1667 85.98
1668 86.01
1669 86.02
1670 86.15
1671 85.98
1672 86.71
1673 86.72
1674 86.72
1675 86.76
1676 86.67
1677 86.81
1678 86.84
1679 86.91
1680 87.44
1681 87.45
1682 87.45
1683 87.50
1684 87.52
1685 87.49
1686 87.57
1687 87.64
1688 88.09
1689 88.15
1690 88.16
1691 88.20
1692 88.22
1693 88.26
1694 88.22
1695 88.31
1696 88.85
1697 88.85
1698 88.94
1699 88.69
1700 88.92
1701 88.95
1702 88.97
1703 89.01
1704 89.60
1705 89.48
1706 89.48
1707 89.65
1708 89.67
1709 89.70
1710 89.74
1711 89.71
1712 90.30
1713 90.24
1714 90.31
1715 90.35
1716 90.26
1717 90.40
1718 90.31
1719 90.46
1720 90.99
1721 90.93
1722 90.99
1723 91.04
1724 91.06
1725 90.96
1726 91.12
1727 91.14
1728 91.69
1729 91.70
1730 91.57
1731 91.74
1732 91.77
1733 91.74
1734 91.83
1735 91.85
1736 92.42
1737 92.42
1738 92.30
1739 92.47
1740 92.49
1741 92.52
1742 92.43
1743 92.58
1744 93.06
1745 92.97
1746 93.05
1747 93.04
1748 93.19
1749 93.23
1750 93.25
1751 93.28
1752 93.80
1753 93.78
1754 93.78
1755 93.84
1756 93.79
1757 93.77
1758 93.93
1759 93.94
1760 94.45
1761 94.44
1762 94.44
1763 94.36
1764 94.40
1765 94.55
1766 94.58
1767 94.61
1768 95.03
1769 95.04
1770 95.15
1771 94.98
1772 95.20
1773 95.14
1774 95.16
1775 95.32
1776 95.81
1777 95.81
1778 95.82
1779 95.84
1780 95.78
1781 95.73
1782 95.95
1783 95.98
1784 96.46
1785 96.45
1786 96.46
1787 96.49
1788 96.53
1789 96.45
1790 96.59
1791 96.62
1792 97.03
1793 97.13
1794 97.14
1795 97.18
1796 97.22
1797 97.24
1798 97.16
1799 97.30
1800 97.72
1801 97.78
1802 97.71
1803 97.86
1804 97.71
1805 97.94
1806 97.98
1807 97.99
1808 98.29
1809 98.38
1810 98.50
1811 98.53
1812 98.58
1813 98.62
1814 98.73
1815 98.68
1816 99.13
1817 99.13
1818 99.14
1819 99.04
1820 99.21
1821 99.25
1822 99.28
1823 99.30
1824 99.64
1825 99.77
1826 99.57
1827 99.59
1828 99.62
1829 99.77
1830 99.80
1831 99.93
1832 100.44
1833 100.46
1834 100.45
1835 100.49
1836 100.50
1837 100.44
1838 100.60
1839 100.55
1840 101.07
1841 100.88
1842 101.08
1843 101.11
1844 101.13
1845 101.20
1846 101.22
1847 101.27
1848 101.70
1849 101.70
1850 101.69
1851 101.70
1852 101.63
1853 101.81
1854 101.81
1855 101.71
1856 102.36
1857 102.24
1858 102.38
1859 102.40
1860 102.41
1861 102.47
1862 102.44
1863 102.53
1864 103.02
1865 102.90
1866 102.82
1867 102.93
1868 103.08
1869 103.10
1870 103.17
1871 103.18
1872 103.44
1873 103.58
1874 103.64
1875 103.68
1876 103.67
1877 103.55
1878 103.73
1879 103.82
1880 104.04
1881 104.18
1882 104.13
1883 104.27
1884 104.30
1885 104.13
1886 104.38
1887 104.29
1888 104.71
1889 104.71
1890 104.80
1891 104.82
1892 104.83
1893 104.73
1894 104.98
1895 104.90
1896 105.27
1897 105.47
1898 105.47
1899 105.42
1900 105.45
1901 105.48
1902 105.58
1903 105.64
1904 105.95
1905 106.06
1906 105.99
1907 105.90
1908 105.96
1909 106.03
1910 106.05
1911 106.15
1912 106.57
1913 106.63
1914 106.43
1915 106.48
1916 106.61
1917 106.62
1918 106.55
1919 106.58
1920 107.13
1921 107.23
1922 107.09
1923 107.15
1924 107.21
1925 107.32
1926 107.23
1927 107.27
1928 107.66
1929 107.74
1930 107.66
1931 107.69
1932 107.80
1933 107.87
1934 107.77
1935 107.90
1936 108.43
1937 108.45
1938 108.32
1939 108.33
1940 108.46
1941 108.40
1942 108.56
1943 108.47
1944 108.81
1945 108.84
1946 109.00
1947 109.03
1948 108.86
1949 108.89
1950 109.12
1951 109.14
1952 109.38
1953 109.55
1954 109.42
1955 109.40
1956 109.41
1957 109.46
1958 109.48
1959 109.53
1960 110.15
1961 109.96
1962 109.96
1963 110.05
1964 110.06
1965 110.05
1966 110.08
1967 110.25
1968 110.51
1969 110.52
1970 110.52
1971 110.74
1972 110.76
1973 110.60
1974 110.82
1975 110.72
1976 111.05
1977 111.17
1978 111.05
1979 111.08
1980 111.11
1981 111.14
1982 111.17
1983 111.40
1984 111.61
1985 111.61
1986 111.61
1987 111.65
1988 111.67
1989 111.71
1990 111.75
1991 111.77
1992 112.20
1993 112.21
1994 112.19
1995 112.23
1996 112.25
1997 112.28
1998 112.50
1999 112.33
2000 112.73
2001 112.83
2002 112.74
2003 112.78
2004 112.81
2005 112.91
2006 112.85
2007 112.90
2008 113.25
2009 113.25
2010 113.26
2011 113.29
2012 113.31
2013 113.39
2014 113.37
2015 113.40
2016 113.77
2017 113.77
2018 113.77
2019 113.81
2020 113.82
2021 113.86
2022 113.88
2023 113.91
2024 114.32
2025 114.32
2026 114.31
2027 114.36
2028 114.39
2029 114.40
2030 114.43
2031 114.48
2032 114.93
2033 114.82
2034 114.84
2035 114.86
2036 114.88
2037 114.93
2038 114.94
2039 114.98
2040 115.34

[-- Attachment #5: gain-test.3503_04.log --]
[-- Type: text/plain, Size: 21457 bytes --]

16 21.69
17 21.76
18 21.81
19 21.65
20 21.94
21 21.99
22 22.05
23 22.04
24 21.96
25 22.20
26 22.33
27 22.41
28 22.39
29 22.55
30 22.38
31 22.62
32 22.77
33 22.80
34 22.87
35 23.01
36 23.08
37 23.09
38 23.01
39 23.19
40 23.41
41 23.27
42 23.36
43 23.43
44 23.52
45 23.61
46 23.70
47 23.78
48 23.87
49 23.95
50 24.03
51 24.27
52 24.21
53 24.31
54 24.40
55 24.48
56 24.58
57 24.68
58 24.77
59 24.87
60 24.95
61 25.05
62 25.13
63 25.22
64 25.33
65 25.44
66 25.53
67 25.63
68 25.72
69 25.82
70 25.92
71 26.02
72 26.12
73 26.22
74 26.32
75 26.41
76 26.51
77 26.62
78 26.71
79 26.80
80 26.91
81 27.01
82 27.11
83 27.21
84 27.30
85 27.41
86 27.50
87 27.60
88 27.71
89 27.81
90 27.91
91 28.01
92 28.11
93 28.21
94 28.30
95 28.39
96 28.49
97 28.59
98 28.69
99 28.79
100 28.89
101 28.99
102 29.09
103 29.18
104 29.29
105 29.40
106 29.49
107 29.59
108 29.68
109 29.78
110 29.87
111 29.97
112 30.07
113 30.17
114 30.27
115 30.37
116 30.47
117 30.57
118 30.66
119 30.75
120 30.86
121 30.96
122 31.06
123 31.15
124 31.25
125 31.35
126 31.45
127 31.55
128 31.65
129 31.75
130 31.85
131 31.94
132 32.04
133 32.14
134 32.23
135 32.33
136 32.43
137 32.54
138 32.64
139 32.73
140 32.82
141 32.92
142 33.15
143 33.11
144 33.21
145 33.31
146 33.41
147 33.51
148 33.61
149 33.71
150 33.87
151 33.89
152 33.99
153 34.10
154 34.20
155 34.42
156 34.38
157 34.48
158 34.58
159 34.67
160 34.99
161 34.88
162 34.97
163 35.20
164 35.16
165 35.40
166 35.50
167 35.59
168 35.70
169 35.67
170 35.99
171 35.93
172 36.09
173 36.19
174 36.29
175 36.46
176 36.49
177 36.59
178 36.69
179 36.79
180 36.89
181 37.07
182 36.96
183 37.26
184 37.28
185 37.47
186 37.49
187 37.58
188 37.76
189 37.65
190 37.75
191 38.06
192 38.09
193 38.13
194 38.16
195 38.26
196 38.42
197 38.68
198 38.57
199 38.73
200 38.77
201 38.87
202 39.10
203 39.20
204 39.17
205 39.41
206 39.51
207 39.47
208 39.58
209 39.68
210 39.78
211 39.88
212 39.98
213 40.09
214 40.19
215 40.29
216 40.39
217 40.50
218 40.60
219 40.70
220 40.89
221 40.90
222 41.00
223 41.10
224 41.20
225 41.31
226 41.41
227 41.51
228 41.61
229 41.71
230 41.81
231 41.91
232 42.01
233 42.12
234 42.22
235 42.32
236 42.42
237 42.52
238 42.62
239 42.72
240 42.82
241 42.92
242 43.03
243 43.12
244 43.22
245 43.24
246 43.42
247 43.52
248 43.62
249 43.72
250 43.82
251 43.83
252 44.01
253 44.11
254 44.21
255 44.22
256 22.33
257 22.40
258 22.47
259 22.55
260 22.62
261 22.71
262 22.79
263 22.87
264 22.99
265 23.09
266 23.19
267 23.28
268 23.38
269 23.50
270 23.61
271 23.72
272 23.87
273 23.99
274 24.12
275 24.25
276 24.36
277 24.53
278 24.67
279 24.82
280 24.98
281 25.13
282 25.28
283 25.43
284 25.59
285 25.76
286 25.92
287 26.08
288 26.26
289 26.44
290 26.61
291 26.78
292 26.95
293 27.14
294 27.31
295 27.48
296 27.68
297 27.87
298 28.05
299 28.22
300 28.39
301 28.58
302 28.76
303 28.94
304 29.14
305 29.34
306 29.53
307 29.71
308 29.90
309 30.10
310 30.29
311 30.48
312 30.68
313 30.88
314 31.08
315 31.27
316 31.46
317 31.67
318 31.80
319 32.05
320 32.29
321 32.49
322 32.69
323 32.89
324 33.08
325 33.29
326 33.49
327 33.69
328 33.91
329 34.12
330 34.29
331 34.46
332 34.70
333 34.84
334 35.11
335 35.23
336 35.45
337 35.74
338 35.87
339 36.08
340 36.14
341 36.57
342 36.63
343 36.89
344 37.10
345 37.31
346 37.51
347 37.71
348 37.78
349 38.05
350 38.19
351 38.39
352 38.60
353 38.82
354 39.03
355 39.22
356 39.42
357 39.64
358 39.84
359 40.03
360 40.25
361 40.47
362 40.67
363 40.87
364 41.07
365 41.28
366 41.49
367 41.68
368 41.88
369 42.10
370 42.31
371 42.50
372 42.70
373 42.92
374 43.13
375 43.33
376 43.53
377 43.74
378 43.95
379 44.14
380 44.34
381 44.56
382 44.75
383 44.94
384 45.18
385 45.39
386 45.60
387 45.80
388 46.00
389 46.21
390 46.41
391 46.60
392 46.81
393 47.02
394 47.22
395 47.42
396 47.62
397 47.83
398 48.03
399 48.23
400 48.44
401 48.66
402 48.86
403 49.06
404 49.25
405 49.46
406 49.57
407 49.77
408 49.98
409 50.28
410 50.39
411 50.68
412 50.80
413 51.08
414 51.28
415 51.46
416 51.60
417 51.90
418 51.88
419 52.21
420 52.50
421 52.62
422 52.90
423 53.09
424 53.31
425 53.52
426 53.72
427 53.92
428 54.11
429 54.32
430 54.52
431 54.71
432 54.86
433 55.14
434 55.33
435 55.53
436 55.73
437 55.84
438 56.04
439 56.32
440 56.53
441 56.74
442 56.95
443 57.14
444 57.33
445 57.54
446 57.74
447 57.93
448 58.15
449 58.36
450 58.70
451 58.77
452 59.09
453 59.17
454 59.37
455 59.57
456 59.78
457 60.00
458 60.20
459 60.39
460 60.59
461 60.81
462 61.00
463 61.19
464 61.42
465 61.63
466 61.84
467 62.16
468 62.23
469 62.44
470 62.64
471 62.84
472 63.05
473 63.26
474 63.47
475 63.66
476 63.86
477 64.07
478 64.27
479 64.38
480 64.59
481 64.89
482 65.10
483 65.29
484 65.49
485 65.61
486 65.81
487 66.00
488 66.22
489 66.45
490 66.64
491 66.84
492 67.04
493 67.25
494 67.46
495 67.59
496 67.88
497 68.08
498 68.36
499 68.47
500 68.67
501 68.88
502 68.96
503 69.28
504 69.49
505 69.70
506 69.99
507 70.10
508 70.23
509 70.44
510 70.57
511 70.78
512 22.11
513 22.17
514 22.22
515 22.27
516 22.33
517 22.40
518 22.46
519 22.53
520 22.60
521 22.67
522 22.73
523 22.81
524 22.87
525 22.94
526 23.01
527 23.08
528 23.17
529 23.25
530 23.33
531 23.41
532 23.49
533 23.58
534 23.66
535 23.73
536 23.83
537 23.92
538 24.01
539 24.10
540 24.19
541 24.28
542 24.37
543 24.46
544 24.58
545 24.69
546 24.78
547 24.89
548 24.99
549 25.10
550 25.20
551 25.30
552 25.42
553 25.53
554 25.63
555 25.73
556 25.84
557 25.96
558 26.06
559 26.16
560 26.28
561 26.40
562 26.51
563 26.61
564 26.72
565 26.83
566 26.94
567 27.05
568 27.17
569 27.28
570 27.40
571 27.51
572 27.63
573 27.75
574 27.87
575 27.98
576 28.12
577 28.25
578 28.36
579 28.48
580 28.59
581 28.72
582 28.83
583 28.95
584 29.09
585 29.21
586 29.34
587 29.46
588 29.58
589 29.71
590 29.83
591 29.94
592 30.08
593 30.21
594 30.33
595 30.46
596 30.58
597 30.71
598 30.83
599 30.95
600 31.09
601 31.22
602 31.34
603 31.46
604 31.59
605 31.72
606 31.84
607 31.96
608 32.07
609 32.23
610 32.36
611 32.41
612 32.61
613 32.74
614 32.87
615 33.00
616 33.08
617 33.28
618 33.32
619 33.53
620 33.65
621 33.71
622 33.91
623 34.04
624 34.17
625 34.31
626 34.44
627 34.57
628 34.69
629 34.82
630 34.95
631 35.00
632 35.21
633 35.35
634 35.40
635 35.39
636 35.73
637 35.87
638 36.00
639 36.04
640 36.28
641 36.42
642 36.55
643 36.60
644 36.81
645 36.95
646 37.00
647 37.21
648 37.35
649 37.49
650 37.62
651 37.75
652 37.88
653 37.93
654 38.06
655 38.19
656 38.33
657 38.56
658 38.61
659 38.73
660 38.86
661 39.08
662 39.00
663 39.26
664 39.39
665 39.40
666 39.67
667 39.66
668 39.92
669 40.03
670 40.06
671 40.19
672 40.47
673 40.48
674 40.61
675 40.96
676 40.88
677 41.02
678 41.15
679 41.27
680 41.44
681 41.56
682 41.69
683 41.82
684 41.95
685 42.09
686 42.35
687 42.35
688 42.49
689 42.69
690 42.76
691 42.89
692 43.02
693 43.16
694 43.29
695 43.42
696 43.56
697 43.70
698 43.84
699 43.97
700 44.10
701 44.24
702 44.37
703 44.50
704 44.65
705 44.79
706 44.92
707 45.04
708 45.17
709 45.31
710 45.44
711 45.57
712 45.72
713 45.85
714 45.91
715 46.12
716 46.25
717 46.39
718 46.52
719 46.65
720 46.80
721 46.93
722 47.07
723 47.20
724 47.33
725 47.47
726 47.61
727 47.65
728 47.87
729 48.02
730 48.06
731 48.28
732 48.33
733 48.54
734 48.60
735 48.73
736 48.88
737 49.00
738 49.14
739 49.26
740 49.39
741 49.62
742 49.66
743 49.79
744 49.94
745 50.16
746 50.21
747 50.43
748 50.55
749 50.60
750 50.80
751 50.87
752 51.00
753 51.15
754 51.29
755 51.50
756 51.55
757 51.70
758 51.82
759 51.89
760 51.96
761 52.23
762 52.45
763 52.50
764 52.62
765 52.63
766 52.77
767 52.95
768 24.08
769 24.23
770 24.37
771 24.52
772 24.69
773 24.87
774 25.06
775 25.26
776 25.51
777 25.76
778 25.99
779 26.20
780 26.40
781 26.70
782 27.04
783 27.33
784 27.66
785 27.91
786 28.30
787 28.62
788 28.94
789 29.25
790 29.57
791 29.99
792 30.36
793 30.68
794 31.11
795 31.42
796 31.87
797 32.19
798 32.58
799 32.95
800 33.44
801 33.86
802 34.19
803 34.45
804 34.85
805 35.42
806 35.69
807 36.09
808 36.53
809 36.96
810 37.50
811 37.77
812 38.17
813 38.74
814 39.01
815 39.42
816 39.97
817 40.28
818 40.71
819 41.12
820 41.53
821 41.97
822 42.39
823 42.93
824 43.37
825 43.68
826 44.11
827 44.52
828 44.92
829 45.37
830 45.79
831 46.20
832 46.81
833 47.14
834 47.57
835 47.99
836 48.40
837 48.98
838 49.28
839 49.68
840 50.14
841 50.59
842 51.02
843 51.35
844 51.86
845 52.30
846 52.64
847 53.14
848 53.50
849 53.95
850 54.39
851 54.80
852 55.30
853 55.67
854 56.17
855 56.58
856 57.02
857 57.38
858 57.81
859 58.22
860 58.51
861 59.08
862 59.38
863 59.91
864 60.22
865 60.80
866 61.30
867 61.52
868 61.93
869 62.50
870 62.93
871 63.21
872 63.79
873 64.11
874 64.60
875 64.95
876 65.42
877 65.80
878 66.34
879 66.62
880 67.04
881 67.49
882 67.92
883 68.33
884 68.75
885 69.19
886 69.74
887 70.15
888 70.46
889 70.90
890 71.32
891 71.73
892 72.13
893 72.58
894 73.00
895 73.31
896 73.88
897 74.38
898 74.74
899 75.14
900 75.46
901 75.97
902 76.38
903 76.77
904 77.20
905 77.54
906 77.94
907 78.32
908 78.77
909 79.21
910 79.60
911 79.90
912 80.40
913 80.73
914 81.21
915 81.57
916 81.98
917 82.44
918 82.76
919 83.13
920 83.64
921 83.83
922 84.30
923 84.64
924 85.03
925 85.43
926 85.80
927 86.17
928 86.56
929 86.95
930 87.31
931 87.59
932 87.95
933 88.42
934 88.90
935 89.10
936 89.42
937 89.84
938 90.17
939 90.60
940 90.95
941 91.24
942 91.67
943 92.09
944 92.30
945 92.75
946 93.11
947 93.45
948 93.71
949 94.07
950 94.41
951 94.75
952 95.10
953 95.47
954 95.81
955 96.14
956 96.47
957 96.70
958 97.16
959 97.48
960 97.84
961 98.20
962 98.54
963 98.66
964 99.18
965 99.54
966 99.86
967 100.18
968 100.53
969 100.76
970 101.20
971 101.46
972 101.82
973 102.04
974 102.35
975 102.66
976 102.99
977 103.31
978 103.57
979 103.88
980 104.26
981 104.59
982 104.87
983 105.25
984 105.48
985 105.76
986 106.12
987 106.34
988 106.65
989 107.03
990 107.31
991 107.59
992 107.84
993 108.15
994 108.44
995 108.73
996 109.02
997 109.33
998 109.62
999 109.89
1000 110.19
1001 110.49
1002 110.77
1003 111.05
1004 111.32
1005 111.61
1006 111.88
1007 111.95
1008 112.42
1009 112.73
1010 112.99
1011 113.25
1012 113.52
1013 113.79
1014 114.07
1015 114.31
1016 114.58
1017 114.86
1018 115.11
1019 115.37
1020 115.55
1021 115.77
1022 116.14
1023 116.38
1024 29.68
1025 29.90
1026 29.92
1027 29.94
1028 30.03
1029 30.34
1030 30.21
1031 30.30
1032 30.55
1033 30.59
1034 30.70
1035 30.74
1036 30.85
1037 30.97
1038 31.09
1039 31.20
1040 31.18
1041 31.12
1042 31.09
1043 30.79
1044 30.30
1045 29.58
1046 28.91
1047 28.09
1048 27.34
1049 26.65
1050 26.36
1051 26.28
1052 26.42
1053 26.57
1054 26.71
1055 26.85
1056 27.03
1057 27.18
1058 27.33
1059 27.40
1060 27.62
1061 27.78
1062 27.92
1063 28.07
1064 28.24
1065 28.40
1066 28.55
1067 28.69
1068 28.85
1069 28.94
1070 29.09
1071 29.33
1072 29.50
1073 29.68
1074 29.85
1075 30.02
1076 30.15
1077 30.33
1078 30.52
1079 30.68
1080 30.86
1081 31.04
1082 31.22
1083 31.39
1084 31.56
1085 31.67
1086 31.92
1087 32.09
1088 32.30
1089 32.49
1090 32.67
1091 32.84
1092 33.02
1093 33.21
1094 33.39
1095 33.49
1096 33.76
1097 33.75
1098 34.14
1099 34.32
1100 34.47
1101 34.69
1102 34.86
1103 34.89
1104 35.17
1105 35.45
1106 35.63
1107 35.81
1108 35.97
1109 36.20
1110 36.38
1111 36.56
1112 36.76
1113 36.88
1114 37.06
1115 37.33
1116 37.31
1117 37.63
1118 37.81
1119 37.87
1120 38.20
1121 38.40
1122 38.46
1123 38.77
1124 39.04
1125 39.16
1126 39.34
1127 39.39
1128 39.73
1129 39.80
1130 40.20
1131 40.31
1132 40.50
1133 40.62
1134 40.88
1135 40.94
1136 41.14
1137 41.34
1138 41.54
1139 41.73
1140 41.92
1141 42.12
1142 42.31
1143 42.50
1144 42.83
1145 42.90
1146 43.23
1147 43.28
1148 43.54
1149 43.67
1150 44.00
1151 44.05
1152 44.40
1153 44.48
1154 44.67
1155 44.81
1156 45.05
1157 45.26
1158 45.45
1159 45.63
1160 45.84
1161 46.04
1162 46.23
1163 46.43
1164 46.62
1165 46.82
1166 46.93
1167 47.20
1168 47.40
1169 47.53
1170 47.81
1171 48.00
1172 48.19
1173 48.31
1174 48.58
1175 48.77
1176 48.95
1177 49.17
1178 49.37
1179 49.47
1180 49.75
1181 49.86
1182 50.13
1183 50.24
1184 50.45
1185 50.75
1186 50.86
1187 51.05
1188 51.33
1189 51.45
1190 51.65
1191 51.92
1192 52.05
1193 52.13
1194 52.45
1195 52.64
1196 52.70
1197 53.04
1198 53.22
1199 53.41
1200 53.62
1201 53.70
1202 54.02
1203 54.21
1204 54.40
1205 54.49
1206 54.68
1207 54.99
1208 55.20
1209 55.40
1210 55.60
1211 55.66
1212 55.98
1213 56.05
1214 56.25
1215 56.58
1216 56.66
1217 56.87
1218 57.06
1219 57.26
1220 57.57
1221 57.65
1222 57.85
1223 58.04
1224 58.31
1225 58.45
1226 58.65
1227 58.97
1228 59.03
1229 59.24
1230 59.62
1231 59.61
1232 59.82
1233 60.04
1234 60.23
1235 60.33
1236 60.53
1237 60.95
1238 61.01
1239 61.21
1240 61.47
1241 61.61
1242 61.81
1243 62.13
1244 62.18
1245 62.40
1246 62.57
1247 62.77
1248 62.89
1249 63.19
1250 63.29
1251 63.57
1252 63.76
1253 63.97
1254 64.13
1255 64.26
1256 64.55
1257 64.76
1258 64.88
1259 65.15
1260 65.34
1261 65.55
1262 65.74
1263 65.92
1264 66.13
1265 66.24
1266 66.44
1267 66.64
1268 66.82
1269 67.02
1270 67.21
1271 67.50
1272 67.61
1273 67.81
1274 68.01
1275 68.20
1276 68.38
1277 68.58
1278 68.77
1279 68.97
1280 25.64
1281 26.00
1282 26.08
1283 26.40
1284 26.37
1285 26.58
1286 26.98
1287 27.17
1288 27.30
1289 27.53
1290 27.72
1291 28.10
1292 28.33
1293 28.53
1294 28.62
1295 28.87
1296 29.18
1297 29.26
1298 29.52
1299 29.31
1300 29.45
1301 29.69
1302 29.97
1303 30.26
1304 30.52
1305 30.93
1306 31.21
1307 31.54
1308 31.86
1309 32.20
1310 32.51
1311 32.84
1312 33.01
1313 33.59
1314 33.86
1315 34.20
1316 34.41
1317 35.01
1318 35.27
1319 35.70
1320 36.10
1321 36.40
1322 36.84
1323 36.98
1324 37.34
1325 37.86
1326 38.30
1327 38.59
1328 38.98
1329 39.39
1330 39.77
1331 40.07
1332 40.52
1333 40.79
1334 41.17
1335 41.67
1336 41.93
1337 42.48
1338 42.93
1339 43.11
1340 43.49
1341 44.03
1342 44.34
1343 44.66
1344 45.24
1345 45.53
1346 45.93
1347 46.30
1348 46.82
1349 47.11
1350 47.49
1351 47.87
1352 48.30
1353 48.71
1354 49.11
1355 49.50
1356 49.88
1357 50.31
1358 50.61
1359 51.08
1360 51.43
1361 51.84
1362 52.34
1363 52.64
1364 53.06
1365 53.46
1366 53.85
1367 54.25
1368 54.71
1369 55.16
1370 55.36
1371 55.74
1372 56.13
1373 56.69
1374 56.95
1375 57.46
1376 57.89
1377 58.18
1378 58.59
1379 58.97
1380 59.37
1381 59.92
1382 60.18
1383 60.57
1384 61.01
1385 61.42
1386 61.96
1387 62.23
1388 62.61
1389 62.95
1390 63.43
1391 63.72
1392 64.20
1393 64.66
1394 64.96
1395 65.35
1396 65.84
1397 66.25
1398 66.64
1399 67.04
1400 67.41
1401 67.77
1402 68.18
1403 68.56
1404 68.95
1405 69.38
1406 69.77
1407 70.24
1408 70.62
1409 71.02
1410 71.45
1411 71.83
1412 72.20
1413 72.71
1414 73.09
1415 73.40
1416 73.82
1417 74.12
1418 74.63
1419 75.01
1420 75.39
1421 75.80
1422 76.18
1423 76.54
1424 76.95
1425 77.36
1426 77.80
1427 78.11
1428 78.48
1429 78.81
1430 79.24
1431 79.61
1432 79.99
1433 80.38
1434 80.63
1435 81.20
1436 81.46
1437 81.86
1438 82.14
1439 82.55
1440 82.95
1441 83.32
1442 83.69
1443 84.04
1444 84.27
1445 84.64
1446 85.11
1447 85.34
1448 85.71
1449 86.21
1450 86.56
1451 86.89
1452 87.23
1453 87.48
1454 87.72
1455 88.27
1456 88.49
1457 88.98
1458 89.33
1459 89.53
1460 89.92
1461 90.22
1462 90.67
1463 90.94
1464 91.23
1465 91.57
1466 91.96
1467 92.34
1468 92.66
1469 93.00
1470 93.32
1471 93.43
1472 93.99
1473 94.34
1474 94.46
1475 94.78
1476 95.17
1477 95.59
1478 95.94
1479 96.05
1480 96.47
1481 96.80
1482 97.04
1483 97.35
1484 97.85
1485 98.16
1486 98.48
1487 98.67
1488 98.98
1489 99.37
1490 99.65
1491 100.06
1492 100.15
1493 100.57
1494 100.98
1495 101.07
1496 101.53
1497 101.87
1498 102.00
1499 102.41
1500 102.58
1501 103.09
1502 103.24
1503 103.47
1504 103.90
1505 104.24
1506 104.51
1507 104.67
1508 105.07
1509 105.45
1510 105.68
1511 105.88
1512 106.12
1513 106.42
1514 106.72
1515 107.05
1516 107.34
1517 107.66
1518 107.82
1519 108.29
1520 108.55
1521 108.74
1522 109.05
1523 109.27
1524 109.47
1525 109.80
1526 110.02
1527 110.35
1528 110.55
1529 110.91
1530 111.09
1531 111.37
1532 111.61
1533 111.88
1534 112.14
1535 112.46
1536 29.40
1537 29.60
1538 29.56
1539 29.74
1540 29.82
1541 30.05
1542 30.11
1543 30.34
1544 30.41
1545 30.46
1546 30.79
1547 30.82
1548 30.90
1549 31.19
1550 31.21
1551 31.38
1552 31.59
1553 31.58
1554 31.55
1555 31.16
1556 30.64
1557 29.83
1558 28.96
1559 28.17
1560 27.57
1561 27.71
1562 27.89
1563 28.08
1564 28.27
1565 28.48
1566 28.68
1567 28.86
1568 29.10
1569 29.31
1570 29.52
1571 29.73
1572 29.94
1573 30.17
1574 30.38
1575 30.59
1576 30.83
1577 31.06
1578 31.20
1579 31.42
1580 31.64
1581 31.96
1582 32.11
1583 32.40
1584 32.65
1585 32.90
1586 33.13
1587 33.36
1588 33.57
1589 33.85
1590 34.00
1591 34.29
1592 34.56
1593 34.74
1594 35.06
1595 35.30
1596 35.46
1597 35.80
1598 35.96
1599 36.27
1600 36.49
1601 36.83
1602 36.87
1603 37.24
1604 37.35
1605 37.61
1606 38.07
1607 38.23
1608 38.58
1609 38.81
1610 38.87
1611 39.33
1612 39.49
1613 39.77
1614 39.88
1615 40.26
1616 40.41
1617 40.75
1618 40.95
1619 41.20
1620 41.51
1621 41.72
1622 41.96
1623 42.35
1624 42.48
1625 42.75
1626 43.01
1627 43.26
1628 43.51
1629 43.95
1630 44.04
1631 44.29
1632 44.57
1633 44.84
1634 45.10
1635 45.36
1636 45.61
1637 45.88
1638 46.14
1639 46.39
1640 46.67
1641 46.94
1642 47.20
1643 47.45
1644 47.68
1645 47.98
1646 48.24
1647 48.49
1648 48.76
1649 49.04
1650 49.29
1651 49.46
1652 49.81
1653 49.99
1654 50.34
1655 50.60
1656 50.87
1657 51.14
1658 51.41
1659 51.58
1660 51.84
1661 52.05
1662 52.24
1663 52.62
1664 52.92
1665 53.20
1666 53.34
1667 53.73
1668 53.85
1669 54.26
1670 54.51
1671 54.64
1672 55.06
1673 55.33
1674 55.59
1675 55.86
1676 56.10
1677 56.25
1678 56.52
1679 56.77
1680 57.05
1681 57.34
1682 57.72
1683 57.98
1684 58.11
1685 58.52
1686 58.65
1687 59.03
1688 59.17
1689 59.45
1690 59.71
1691 59.97
1692 60.22
1693 60.51
1694 60.76
1695 61.02
1696 61.30
1697 61.58
1698 61.84
1699 62.10
1700 62.36
1701 62.55
1702 62.81
1703 63.06
1704 63.41
1705 63.71
1706 63.95
1707 64.14
1708 64.48
1709 64.76
1710 65.01
1711 65.27
1712 65.54
1713 65.73
1714 66.08
1715 66.31
1716 66.50
1717 66.78
1718 67.12
1719 67.37
1720 67.55
1721 67.83
1722 68.10
1723 68.35
1724 68.60
1725 68.88
1726 69.13
1727 69.39
1728 69.68
1729 70.04
1730 70.22
1731 70.48
1732 70.74
1733 71.01
1734 71.26
1735 71.52
1736 71.80
1737 72.07
1738 72.33
1739 72.59
1740 72.84
1741 72.99
1742 73.37
1743 73.62
1744 73.77
1745 74.05
1746 74.44
1747 74.68
1748 74.93
1749 75.07
1750 75.46
1751 75.63
1752 75.96
1753 76.02
1754 76.42
1755 76.73
1756 76.98
1757 77.23
1758 77.47
1759 77.60
1760 77.97
1761 78.11
1762 78.49
1763 78.72
1764 78.90
1765 79.24
1766 79.47
1767 79.70
1768 79.85
1769 80.23
1770 80.34
1771 80.65
1772 80.93
1773 81.06
1774 81.42
1775 81.65
1776 81.90
1777 82.15
1778 82.27
1779 82.62
1780 82.85
1781 82.98
1782 83.21
1783 83.51
1784 83.74
1785 83.99
1786 84.18
1787 84.39
1788 84.74
1789 85.00
1790 85.22
1791 85.32
1792 26.55
1793 26.82
1794 27.22
1795 27.51
1796 27.90
1797 28.20
1798 28.77
1799 29.22
1800 29.64
1801 30.20
1802 30.74
1803 31.43
1804 31.88
1805 32.65
1806 33.34
1807 33.89
1808 34.63
1809 35.43
1810 36.06
1811 36.63
1812 37.46
1813 38.23
1814 38.83
1815 39.69
1816 40.35
1817 41.15
1818 42.07
1819 42.71
1820 43.48
1821 44.31
1822 45.10
1823 46.00
1824 46.74
1825 47.61
1826 48.44
1827 49.24
1828 49.95
1829 50.91
1830 51.73
1831 52.46
1832 53.44
1833 54.32
1834 55.17
1835 55.98
1836 56.58
1837 57.58
1838 58.41
1839 59.23
1840 60.01
1841 60.96
1842 61.70
1843 62.51
1844 63.32
1845 64.22
1846 65.03
1847 65.85
1848 66.70
1849 67.59
1850 68.34
1851 69.16
1852 70.06
1853 70.94
1854 71.67
1855 72.57
1856 73.43
1857 74.40
1858 75.14
1859 75.93
1860 76.72
1861 77.57
1862 78.35
1863 79.00
1864 79.96
1865 80.78
1866 81.56
1867 82.31
1868 83.04
1869 83.83
1870 84.45
1871 85.30
1872 86.08
1873 86.73
1874 87.48
1875 88.30
1876 89.01
1877 89.64
1878 90.46
1879 91.14
1880 91.80
1881 92.59
1882 93.17
1883 93.87
1884 94.61
1885 95.33
1886 95.77
1887 96.62
1888 97.30
1889 98.01
1890 98.68
1891 99.25
1892 99.82
1893 100.51
1894 101.13
1895 101.73
1896 102.54
1897 103.15
1898 103.83
1899 104.30
1900 105.02
1901 105.65
1902 106.12
1903 106.63
1904 107.43
1905 108.04
1906 108.44
1907 109.00
1908 109.64
1909 110.30
1910 110.71
1911 111.25
1912 111.81
1913 112.39
1914 112.93
1915 113.46
1916 113.97
1917 114.52
1918 115.00
1919 115.52
1920 116.12
1921 116.65
1922 117.15
1923 117.63
1924 118.05
1925 118.63
1926 118.98
1927 119.50
1928 119.90
1929 120.51
1930 120.86
1931 121.42
1932 121.77
1933 122.33
1934 122.77
1935 123.09
1936 123.58
1937 124.03
1938 124.46
1939 124.89
1940 125.29
1941 125.71
1942 126.17
1943 126.54
1944 126.89
1945 127.41
1946 127.75
1947 128.01
1948 128.57
1949 128.93
1950 129.22
1951 129.69
1952 130.12
1953 130.45
1954 130.78
1955 131.11
1956 131.47
1957 131.86
1958 132.23
1959 132.58
1960 132.96
1961 133.34
1962 133.75
1963 134.05
1964 134.39
1965 134.87
1966 135.21
1967 135.31
1968 135.87
1969 136.15
1970 136.46
1971 136.79
1972 137.14
1973 137.47
1974 137.79
1975 138.12
1976 138.43
1977 138.77
1978 139.16
1979 139.34
1980 139.74
1981 140.04
1982 140.34
1983 140.71
1984 141.00
1985 141.30
1986 141.67
1987 141.93
1988 142.27
1989 142.47
1990 142.76
1991 143.06
1992 143.43
1993 143.61
1994 143.89
1995 144.20
1996 144.56
1997 144.78
1998 145.04
1999 145.25
2000 145.64
2001 145.93
2002 146.25
2003 146.41
2004 146.68
2005 146.96
2006 147.30
2007 147.46
2008 147.85
2009 148.09
2010 148.26
2011 148.51
2012 148.76
2013 149.06
2014 149.26
2015 149.54
2016 149.84
2017 150.10
2018 150.34
2019 150.58
2020 150.68
2021 151.07
2022 151.31
2023 151.47
2024 151.69
2025 151.89
2026 152.16
2027 152.34
2028 152.68
2029 152.81
2030 153.03
2031 153.26
2032 153.36
2033 153.72
2034 153.82
2035 154.14
2036 154.28
2037 154.52
2038 154.85
2039 154.88
2040 155.19

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-19 13:49           ` Laurent Pinchart
  2022-09-20 15:55             ` Mikhail Rudenko
@ 2022-09-20 20:31             ` Mikhail Rudenko
  2022-09-21 13:16               ` Sakari Ailus
  1 sibling, 1 reply; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-20 20:31 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Sakari Ailus, Dave Stevenson, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Jimmy Su, Arnd Bergmann, Arec Kao, Marek Vasut, linux-media,
	devicetree, linux-kernel

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


Hi Laurent, Sakari,

On 2022-09-19 at 16:49 +03, Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hello,
>
> On Mon, Sep 19, 2022 at 10:31:02AM +0000, Sakari Ailus wrote:
>> On Mon, Sep 19, 2022 at 10:01:06AM +0300, Mikhail Rudenko wrote:
>> > On 2022-09-19 at 06:40 GMT, Sakari Ailus wrote:
>> > > On Fri, Sep 16, 2022 at 12:27:42AM +0300, Mikhail Rudenko wrote:
>> > >> On 2022-09-14 at 10:58 +01, Dave Stevenson wrote:
>> > >> > On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko wrote:
>> > >> >>
>> > >> >> Hello,
>> > >> >>
>> > >> >> this series implements support for Omnivision OV4689 image
>> > >> >> sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
>> > >> >> megapixel image sensor. Ihis chip supports high frame rate speeds up
>> > >> >> to 90 fps at 2688x1520 resolution. It is programmable through an I2C
>> > >> >> interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
>> > >> >> connection.
>> > >> >>
>> > >> >> The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
>> > >> >> and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
>> > >> >> 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera
>> > >> >> module.
>> > >> >> While porting the driver, I stumbled upon two issues:
>> >
>> > [snip]
>> >
>> > >> >> (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
>> > >> >> gain is not linear across that range. Instead, it is piecewise linear
>> > >> >> (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
>> > >> >> 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
>> > >> >> with more linear segments in between. Rockchip's camera engine code
>> > >> >> chooses one of the above segments depenging on the desired gain
>> > >> >> value. The question is, how should we proceed keeping in mind
>> > >> >> libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
>> > >> >> libcamera will do the mapping, or the driver will do the mapping
>> > >> >> itself and expose some logical gain units not tied to the actual gain
>> > >> >> register value? Meanwhile, this driver conservatively exposes only
>> > >> >> 0x0-0xf8 gain register range.
>> > >> >
>> > >> > The datasheet linked above says "for the gain formula, please contact
>> > >> > your local OmniVision FAE" :-(
>> > >> > I would assume that the range is from 1x rather than 0x - people
>> > >> > rarely want a totally black image that 0x would give. Or is it ranges
>> > >> > of 1x - 2x, 2x - 4x, 4x - 8x, and 8x - 16x?
>> > >>
>> > >> A picture is worth a thousand words, so I've attached the results of my
>> > >> experimentation with the gain register. They were obtained with Rockchip
>> > >> 3399, with AEC, AGC and black level subtraction disabled. The image was
>> > >> converted from 10-bit RGGB to 8-bit YUV 4:2:0 by the Rockchip ISP.
>
> Is that full or limited range YUV ?
>
>> > > Based on that it looks like their medication may have been a little too
>> > > strong.
>> > >
>> > > Could this be implemented so that the control value would be linear linear
>> > > but its range would correspond 1x--16x values?
>> > >
>> > > libcamera will be able to cope with that.
>> >
>> > According to the following fragment of the Rockchip camera engine sensor
>> > configuration file for ov4689 [1]
>> >
>> >     <Linear index="1" type="double" size="[4 7]">
>> >        [1 2 128 0 1 128 255
>> >         2 4 64 -248 1 376 504
>> >         4 8 32 -756 1 884 1012
>> >         8 16 16 -1784 1 1912 2040]
>> >     </Linear>,
>> >
>> > it uses gain register value range 128-255 for gain 1x-2x, 376-504 for
>> > gain 2x-4x, 884-1024 for 4x-8x, and 1912-2040 for 8x-16x. Do you suggest
>
> That looks *really* weird. I would have understood [384, 511], [896,
> 1023] and [1920, 2047], but not those intervals.
>
> The driver hardcodes bit 0x3503[2] to 1, which means "sensor gain
> format". Maybe setting it to 0 ("real gain format") would produce saner
> results ?
>
>> > to implement this calculation in the sensor driver and expose some
>> > linear "logical" gain to userspace (ranging, e.g., 128-2048 for gains
>> > 1x-16x)?
>>
>> Yes. This way the user space can somehow work without knowing this special
>> implementation, even though the granularity changes over the range. I guess
>> the granularity would need to be known in libcamera but that's a separate
>> issue.
>
> I can live with that.

I got some fresh data regarding gain setting, with gain register value
ranging from 0 to 4096, please check the attached plot. What is the best
way to expose this to userspace in your opinion?

--
Best regards,
Mikhail Rudenko

[-- Attachment #2: gain_2.pdf --]
[-- Type: application/pdf, Size: 44742 bytes --]

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-20 20:31             ` Mikhail Rudenko
@ 2022-09-21 13:16               ` Sakari Ailus
  0 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2022-09-21 13:16 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Laurent Pinchart, Dave Stevenson, Mauro Carvalho Chehab,
	Rob Herring, Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi,
	Shawn Tu, Jimmy Su, Arnd Bergmann, Arec Kao, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail,

On Tue, Sep 20, 2022 at 11:31:01PM +0300, Mikhail Rudenko wrote:
> 
> Hi Laurent, Sakari,
> 
> On 2022-09-19 at 16:49 +03, Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> 
> > Hello,
> >
> > On Mon, Sep 19, 2022 at 10:31:02AM +0000, Sakari Ailus wrote:
> >> On Mon, Sep 19, 2022 at 10:01:06AM +0300, Mikhail Rudenko wrote:
> >> > On 2022-09-19 at 06:40 GMT, Sakari Ailus wrote:
> >> > > On Fri, Sep 16, 2022 at 12:27:42AM +0300, Mikhail Rudenko wrote:
> >> > >> On 2022-09-14 at 10:58 +01, Dave Stevenson wrote:
> >> > >> > On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko wrote:
> >> > >> >>
> >> > >> >> Hello,
> >> > >> >>
> >> > >> >> this series implements support for Omnivision OV4689 image
> >> > >> >> sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
> >> > >> >> megapixel image sensor. Ihis chip supports high frame rate speeds up
> >> > >> >> to 90 fps at 2688x1520 resolution. It is programmable through an I2C
> >> > >> >> interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> >> > >> >> connection.
> >> > >> >>
> >> > >> >> The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
> >> > >> >> and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
> >> > >> >> 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera
> >> > >> >> module.
> >> > >> >> While porting the driver, I stumbled upon two issues:
> >> >
> >> > [snip]
> >> >
> >> > >> >> (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
> >> > >> >> gain is not linear across that range. Instead, it is piecewise linear
> >> > >> >> (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
> >> > >> >> 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
> >> > >> >> with more linear segments in between. Rockchip's camera engine code
> >> > >> >> chooses one of the above segments depenging on the desired gain
> >> > >> >> value. The question is, how should we proceed keeping in mind
> >> > >> >> libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
> >> > >> >> libcamera will do the mapping, or the driver will do the mapping
> >> > >> >> itself and expose some logical gain units not tied to the actual gain
> >> > >> >> register value? Meanwhile, this driver conservatively exposes only
> >> > >> >> 0x0-0xf8 gain register range.
> >> > >> >
> >> > >> > The datasheet linked above says "for the gain formula, please contact
> >> > >> > your local OmniVision FAE" :-(
> >> > >> > I would assume that the range is from 1x rather than 0x - people
> >> > >> > rarely want a totally black image that 0x would give. Or is it ranges
> >> > >> > of 1x - 2x, 2x - 4x, 4x - 8x, and 8x - 16x?
> >> > >>
> >> > >> A picture is worth a thousand words, so I've attached the results of my
> >> > >> experimentation with the gain register. They were obtained with Rockchip
> >> > >> 3399, with AEC, AGC and black level subtraction disabled. The image was
> >> > >> converted from 10-bit RGGB to 8-bit YUV 4:2:0 by the Rockchip ISP.
> >
> > Is that full or limited range YUV ?
> >
> >> > > Based on that it looks like their medication may have been a little too
> >> > > strong.
> >> > >
> >> > > Could this be implemented so that the control value would be linear linear
> >> > > but its range would correspond 1x--16x values?
> >> > >
> >> > > libcamera will be able to cope with that.
> >> >
> >> > According to the following fragment of the Rockchip camera engine sensor
> >> > configuration file for ov4689 [1]
> >> >
> >> >     <Linear index="1" type="double" size="[4 7]">
> >> >        [1 2 128 0 1 128 255
> >> >         2 4 64 -248 1 376 504
> >> >         4 8 32 -756 1 884 1012
> >> >         8 16 16 -1784 1 1912 2040]
> >> >     </Linear>,
> >> >
> >> > it uses gain register value range 128-255 for gain 1x-2x, 376-504 for
> >> > gain 2x-4x, 884-1024 for 4x-8x, and 1912-2040 for 8x-16x. Do you suggest
> >
> > That looks *really* weird. I would have understood [384, 511], [896,
> > 1023] and [1920, 2047], but not those intervals.
> >
> > The driver hardcodes bit 0x3503[2] to 1, which means "sensor gain
> > format". Maybe setting it to 0 ("real gain format") would produce saner
> > results ?
> >
> >> > to implement this calculation in the sensor driver and expose some
> >> > linear "logical" gain to userspace (ranging, e.g., 128-2048 for gains
> >> > 1x-16x)?
> >>
> >> Yes. This way the user space can somehow work without knowing this special
> >> implementation, even though the granularity changes over the range. I guess
> >> the granularity would need to be known in libcamera but that's a separate
> >> issue.
> >
> > I can live with that.
> 
> I got some fresh data regarding gain setting, with gain register value
> ranging from 0 to 4096, please check the attached plot. What is the best
> way to expose this to userspace in your opinion?

I know I requested this to be changed to be as linear as possible, but it
would seem that the gain values do not seem to match exactly what is
documented. So you'd need to experimentally find where you'd need to switch
the range and you might arrive at better values after the initial
implementation. There might be differences between units, too, and if there
were tuning values, you'd find this in sensor EEPROM.

Therefore I think this appears to fit less well for driver implementation.
I'm fine with exposing this to the user space as-is although it doesn't
make a great user space inteface. It's just a poor hardware implementation
but there's nothing we can do about it.

-- 
Kind regards,

Sakari Ailus

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-11 20:01 ` [PATCH v2 2/2] media: i2c: add support for ov4689 Mikhail Rudenko
                     ` (2 preceding siblings ...)
  2022-09-14 15:51   ` Tommaso Merciai
@ 2022-09-22  9:53   ` Sakari Ailus
  2022-09-22 15:23     ` Mikhail Rudenko
  2022-09-22 10:54   ` Dave Stevenson
  4 siblings, 1 reply; 41+ messages in thread
From: Sakari Ailus @ 2022-09-22  9:53 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail,

On Sun, Sep 11, 2022 at 11:01:35PM +0300, Mikhail Rudenko wrote:
> Add a V4L2 sub-device driver for OmniVision OV4689 image sensor. This
> is a 4 Mpx image sensor using the I2C bus for control and the CSI-2
> bus for data.
> 
> This driver supports following features:
> - manual exposure and analog gain control support
> - test pattern support
> - media controller support
> - runtime PM support
> - support following resolutions:
>   + 2688x1520 at 30 fps
> 
> The driver provides all mandatory V4L2 controls for compatibility with
> libcamera. The sensor supports 1/2/4-lane CSI-2 modes, but the driver
> implements 4 lane mode only at this moment.
> 
> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
> ---
>  MAINTAINERS                |   1 +
>  drivers/media/i2c/Kconfig  |  14 +
>  drivers/media/i2c/Makefile |   1 +
>  drivers/media/i2c/ov4689.c | 951 +++++++++++++++++++++++++++++++++++++
>  4 files changed, 967 insertions(+)
>  create mode 100644 drivers/media/i2c/ov4689.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 63c4844f26e6..1857f3864e1b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14529,6 +14529,7 @@ L:	linux-media@vger.kernel.org
>  S:	Maintained
>  T:	git git://linuxtv.org/media_tree.git
>  F:	Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> +F:	drivers/media/i2c/ov5647.c
>  
>  OMNIVISION OV5640 SENSOR DRIVER
>  M:	Steve Longerbeam <slongerbeam@gmail.com>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index fae2baabb773..4993e1ae2ea8 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -429,6 +429,20 @@ config VIDEO_OV2740
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ov2740.
>  
> +config VIDEO_OV4689
> +	tristate "OmniVision OV4689 sensor support"
> +	depends on OF
> +	depends on GPIOLIB && VIDEO_DEV && I2C
> +	select MEDIA_CONTROLLER
> +	select VIDEO_V4L2_SUBDEV_API
> +	select V4L2_FWNODE
> +	help
> +	  This is a Video4Linux2 sensor-level driver for the OmniVision
> +	  OV4689 camera.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called ov4689.
> +
>  config VIDEO_OV5640
>  	tristate "OmniVision OV5640 sensor support"
>  	depends on OF
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 3e1696963e7f..7446c0a1eed0 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
>  obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
>  obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
>  obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
> +obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
>  obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>  obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
>  obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
> diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c
> new file mode 100644
> index 000000000000..9f05e812acf8
> --- /dev/null
> +++ b/drivers/media/i2c/ov4689.c
> @@ -0,0 +1,951 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ov4689 driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-fwnode.h>
> +
> +#define CHIP_ID				0x004688
> +#define OV4689_REG_CHIP_ID		0x300a
> +
> +#define OV4689_XVCLK_FREQ		24000000
> +
> +#define OV4689_REG_CTRL_MODE		0x0100
> +#define OV4689_MODE_SW_STANDBY		0x0
> +#define OV4689_MODE_STREAMING		BIT(0)
> +
> +#define OV4689_REG_EXPOSURE		0x3500
> +#define OV4689_EXPOSURE_MIN		4
> +#define OV4689_EXPOSURE_STEP		1
> +#define OV4689_VTS_MAX			0x7fff
> +
> +#define OV4689_REG_GAIN_H		0x3508
> +#define OV4689_REG_GAIN_L		0x3509
> +#define OV4689_GAIN_H_MASK		0x07
> +#define OV4689_GAIN_H_SHIFT		8
> +#define OV4689_GAIN_L_MASK		0xff
> +#define OV4689_GAIN_MIN			0x10
> +#define OV4689_GAIN_MAX			0xf8
> +#define OV4689_GAIN_STEP		1
> +#define OV4689_GAIN_DEFAULT		0x10
> +
> +#define OV4689_REG_TEST_PATTERN		0x5040
> +#define OV4689_TEST_PATTERN_ENABLE	0x80
> +#define OV4689_TEST_PATTERN_DISABLE	0x0
> +
> +#define OV4689_REG_VTS			0x380e
> +
> +#define REG_NULL			0xFFFF
> +
> +#define OV4689_REG_VALUE_08BIT		1
> +#define OV4689_REG_VALUE_16BIT		2
> +#define OV4689_REG_VALUE_24BIT		3
> +
> +#define OV4689_LANES			4
> +#define OV4689_BITS_PER_SAMPLE		10
> +
> +static const char *const ov4689_supply_names[] = {
> +	"avdd", /* Analog power */
> +	"dovdd", /* Digital I/O power */
> +	"dvdd", /* Digital core power */
> +};
> +
> +#define OV4689_NUM_SUPPLIES ARRAY_SIZE(ov4689_supply_names)

I think it'd be cleaner to use ARRAY_SIZE(ov4689_supply_names) instead.

> +
> +struct regval {
> +	u16 addr;
> +	u8 val;
> +};
> +
> +struct ov4689_mode {
> +	u32 width;
> +	u32 height;
> +	u32 max_fps;
> +	u32 hts_def;
> +	u32 vts_def;
> +	u32 exp_def;
> +	const struct regval *reg_list;
> +};
> +
> +struct ov4689 {
> +	struct i2c_client *client;
> +	struct clk *xvclk;
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *pwdn_gpio;
> +	struct regulator_bulk_data supplies[OV4689_NUM_SUPPLIES];
> +
> +	struct v4l2_subdev subdev;
> +	struct media_pad pad;
> +
> +	struct mutex mutex; /* lock to protect streaming, ctrls and cur_mode */
> +	bool streaming;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_ctrl *exposure;
> +	struct v4l2_ctrl *anal_gain;
> +	struct v4l2_ctrl *digi_gain;
> +	struct v4l2_ctrl *hblank;
> +	struct v4l2_ctrl *vblank;
> +	struct v4l2_ctrl *test_pattern;

Only keep the controls you need elsewhere.

> +
> +	const struct ov4689_mode *cur_mode;
> +};
> +
> +#define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
> +
> +/*
> + * Xclk 24Mhz
> + */
> +static const struct regval ov4689_global_regs[] = {
> +	{ REG_NULL, 0x00 },
> +};
> +
> +/*
> + * Xclk 24Mhz
> + * max_framerate 30fps
> + * mipi_datarate per lane 1008Mbps
> + */
> +static const struct regval ov4689_2688x1520_regs[] = {
> +	{0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
> +	{0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
> +	{0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
> +	{0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
> +	{0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
> +	{0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
> +	{0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
> +	{0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
> +	{0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
> +	{0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
> +	{0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
> +	{0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
> +	{0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
> +	{0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
> +	{0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
> +	{0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
> +	{0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
> +	{0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
> +	{0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
> +	{0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
> +	{0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
> +	{0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
> +	{0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
> +	{0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
> +	{0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
> +	{0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
> +	{0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
> +	{0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
> +	{0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
> +	{0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
> +	{0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
> +	{0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
> +	{0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
> +	{0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
> +	{0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
> +	{0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
> +	{0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
> +	{0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
> +	{0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
> +	{0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
> +	{0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
> +	{0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
> +	{0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
> +	{0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
> +	{0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
> +	{0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
> +	{0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
> +	{0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
> +	{0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
> +	{0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
> +	{0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
> +	{0x380d, 0x80}, {0x380e, 0x06}, {0x380f, 0x12},
> +	{0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
> +	{0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
> +	{0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
> +	{0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
> +	{0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
> +	{0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
> +	{0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
> +	{0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
> +	{0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
> +	{0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
> +	{0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
> +	{0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
> +	{0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
> +	{0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
> +	{0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
> +	{0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
> +	{0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
> +	{0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
> +	{0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
> +	{0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
> +	{0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
> +	{0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
> +	{0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
> +	{0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
> +	{0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
> +	{0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
> +	{0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
> +	{0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
> +	{0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
> +	{0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
> +	{0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
> +	{0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
> +	{REG_NULL, 0x00},
> +};
> +
> +static const struct ov4689_mode supported_modes[] = {
> +	{
> +		.width = 2688,
> +		.height = 1520,
> +		.max_fps = 30,
> +		.exp_def = 0x0600,
> +		.hts_def = 0x0a80,
> +		.vts_def = 0x0612,
> +		.reg_list = ov4689_2688x1520_regs,
> +	},
> +};
> +
> +#define OV4689_LINK_FREQ_500MHZ 500000000

Please use the plain number --- see also comments in probe.

> +static const s64 link_freq_menu_items[] = { OV4689_LINK_FREQ_500MHZ };
> +
> +static const char *const ov4689_test_pattern_menu[] = {
> +	"Disabled",
> +	"Vertical Color Bar Type 1",
> +	"Vertical Color Bar Type 2",
> +	"Vertical Color Bar Type 3",
> +	"Vertical Color Bar Type 4"
> +};
> +
> +/* Write registers up to 4 at a time */
> +static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len,
> +			    u32 val)
> +{
> +	u32 buf_i, val_i;
> +	__be32 val_be;
> +	u8 *val_p;
> +	u8 buf[6];
> +
> +	if (len > 4)
> +		return -EINVAL;
> +
> +	buf[0] = reg >> 8;
> +	buf[1] = reg & 0xff;
> +
> +	val_be = cpu_to_be32(val);
> +	val_p = (u8 *)&val_be;
> +	buf_i = 2;
> +	val_i = 4 - len;
> +
> +	while (val_i < 4)
> +		buf[buf_i++] = val_p[val_i++];
> +
> +	if (i2c_master_send(client, buf, len + 2) != len + 2)
> +		return -EIO;
> +
> +	return 0;
> +}
> +
> +static int ov4689_write_array(struct i2c_client *client,
> +			      const struct regval *regs)
> +{
> +	int ret = 0;
> +	u32 i;
> +
> +	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
> +		ret = ov4689_write_reg(client, regs[i].addr,
> +				       OV4689_REG_VALUE_08BIT, regs[i].val);
> +
> +	return ret;
> +}
> +
> +/* Read registers up to 4 at a time */
> +static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
> +			   u32 *val)
> +{
> +	__be16 reg_addr_be = cpu_to_be16(reg);
> +	struct i2c_msg msgs[2];
> +	__be32 data_be = 0;
> +	u8 *data_be_p;
> +	int ret;
> +
> +	if (len > 4 || !len)
> +		return -EINVAL;
> +
> +	data_be_p = (u8 *)&data_be;
> +	/* Write register address */
> +	msgs[0].addr = client->addr;
> +	msgs[0].flags = 0;
> +	msgs[0].len = 2;
> +	msgs[0].buf = (u8 *)&reg_addr_be;
> +
> +	/* Read data from register */
> +	msgs[1].addr = client->addr;
> +	msgs[1].flags = I2C_M_RD;
> +	msgs[1].len = len;
> +	msgs[1].buf = &data_be_p[4 - len];
> +
> +	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> +	if (ret != ARRAY_SIZE(msgs))
> +		return -EIO;
> +
> +	*val = be32_to_cpu(data_be);
> +
> +	return 0;
> +}
> +
> +static void ov4689_fill_fmt(const struct ov4689_mode *mode,
> +			    struct v4l2_mbus_framefmt *fmt)
> +{
> +	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +	fmt->width = mode->width;
> +	fmt->height = mode->height;
> +	fmt->field = V4L2_FIELD_NONE;
> +}
> +
> +static int ov4689_set_fmt(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_state *sd_state,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +	/* only one mode supported for now */
> +	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
> +
> +	return 0;
> +}
> +
> +static int ov4689_get_fmt(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_state *sd_state,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +	/* only one mode supported for now */
> +	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
> +
> +	return 0;
> +}
> +
> +static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_state *sd_state,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	if (code->index != 0)
> +		return -EINVAL;
> +	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +
> +	return 0;
> +}
> +
> +static int ov4689_enum_frame_sizes(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_state *sd_state,
> +				   struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	if (fse->index >= ARRAY_SIZE(supported_modes))
> +		return -EINVAL;
> +
> +	if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
> +		return -EINVAL;
> +
> +	fse->min_width = supported_modes[fse->index].width;
> +	fse->max_width = supported_modes[fse->index].width;
> +	fse->max_height = supported_modes[fse->index].height;
> +	fse->min_height = supported_modes[fse->index].height;
> +
> +	return 0;
> +}
> +
> +static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
> +{
> +	u32 val;
> +
> +	if (pattern)
> +		val = (pattern - 1) | OV4689_TEST_PATTERN_ENABLE;
> +	else
> +		val = OV4689_TEST_PATTERN_DISABLE;
> +
> +	return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN,
> +				OV4689_REG_VALUE_08BIT, val);
> +}
> +
> +static int ov4689_get_selection(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_state *state,
> +				struct v4l2_subdev_selection *sel)
> +{
> +	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
> +		return -EINVAL;
> +
> +	switch (sel->target) {
> +	case V4L2_SEL_TGT_CROP_BOUNDS:
> +		sel->r.top = 0;
> +		sel->r.left = 0;
> +		sel->r.width = 2720;
> +		sel->r.height = 1536;
> +		return 0;
> +	case V4L2_SEL_TGT_CROP:
> +	case V4L2_SEL_TGT_CROP_DEFAULT:
> +		sel->r.top = 8;
> +		sel->r.left = 16;
> +		sel->r.width = 2688;
> +		sel->r.height = 1520;
> +		return 0;
> +	}
> +	return -EINVAL;
> +}
> +
> +static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
> +{
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +	struct i2c_client *client = ov4689->client;
> +	int ret = 0;
> +
> +	mutex_lock(&ov4689->mutex);
> +
> +	on = !!on;
> +	if (on == ov4689->streaming)
> +		goto unlock_and_return;
> +
> +	if (on) {
> +		ret = pm_runtime_resume_and_get(&client->dev);
> +		if (ret < 0)
> +			goto unlock_and_return;
> +
> +		ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
> +		if (ret) {
> +			pm_runtime_put(&client->dev);
> +			goto unlock_and_return;
> +		}
> +
> +		ret = ov4689_write_array(ov4689->client,
> +					 ov4689->cur_mode->reg_list);
> +		if (ret) {
> +			pm_runtime_put(&client->dev);
> +			goto unlock_and_return;
> +		}
> +
> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
> +				       OV4689_REG_VALUE_08BIT,
> +				       OV4689_MODE_STREAMING);
> +		if (ret) {
> +			pm_runtime_put(&client->dev);
> +			goto unlock_and_return;
> +		}
> +	} else {
> +		ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
> +				 OV4689_REG_VALUE_08BIT,
> +				 OV4689_MODE_SW_STANDBY);
> +		pm_runtime_put(&client->dev);
> +	}
> +
> +	ov4689->streaming = on;
> +
> +unlock_and_return:
> +	mutex_unlock(&ov4689->mutex);
> +
> +	return ret;
> +}
> +
> +/* Calculate the delay in us by clock rate and clock cycles */
> +static inline u32 ov4689_cal_delay(u32 cycles)
> +{
> +	return DIV_ROUND_UP(cycles, OV4689_XVCLK_FREQ / 1000 / 1000);

Please use the actual rate instead.

> +}
> +
> +static int __ov4689_power_on(struct ov4689 *ov4689)
> +{
> +	struct device *dev = &ov4689->client->dev;
> +	u32 delay_us;
> +	int ret;
> +
> +	ret = clk_prepare_enable(ov4689->xvclk);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable xvclk\n");
> +		return ret;
> +	}
> +
> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
> +
> +	ret = regulator_bulk_enable(OV4689_NUM_SUPPLIES, ov4689->supplies);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable regulators\n");
> +		goto disable_clk;
> +	}
> +
> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
> +	usleep_range(500, 1000);
> +	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 0);
> +
> +	/* 8192 cycles prior to first SCCB transaction */
> +	delay_us = ov4689_cal_delay(8192);
> +	usleep_range(delay_us, delay_us * 2);
> +
> +	return 0;
> +
> +disable_clk:
> +	clk_disable_unprepare(ov4689->xvclk);
> +
> +	return ret;
> +}
> +
> +static void __ov4689_power_off(struct ov4689 *ov4689)
> +{
> +	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 1);
> +	clk_disable_unprepare(ov4689->xvclk);
> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
> +	regulator_bulk_disable(OV4689_NUM_SUPPLIES, ov4689->supplies);
> +}

Please merge these two and the wrappers below.

> +
> +static int __maybe_unused ov4689_runtime_resume(struct device *dev)
> +{
> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +	return __ov4689_power_on(ov4689);
> +}
> +
> +static int __maybe_unused ov4689_runtime_suspend(struct device *dev)
> +{
> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +	__ov4689_power_off(ov4689);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +	struct v4l2_mbus_framefmt *try_fmt;
> +
> +	mutex_lock(&ov4689->mutex);
> +
> +	try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0);
> +	/* Initialize try_fmt */
> +	ov4689_fill_fmt(&supported_modes[0], try_fmt);
> +
> +	mutex_unlock(&ov4689->mutex);
> +
> +	return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops ov4689_pm_ops = {
> +	SET_RUNTIME_PM_OPS(ov4689_runtime_suspend, ov4689_runtime_resume, NULL)
> +};
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
> +	.open = ov4689_open,
> +};
> +#endif
> +
> +static const struct v4l2_subdev_video_ops ov4689_video_ops = {
> +	.s_stream = ov4689_s_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
> +	.enum_mbus_code = ov4689_enum_mbus_code,
> +	.enum_frame_size = ov4689_enum_frame_sizes,
> +	.get_fmt = ov4689_get_fmt,
> +	.set_fmt = ov4689_set_fmt,
> +	.get_selection = ov4689_get_selection,
> +};
> +
> +static const struct v4l2_subdev_ops ov4689_subdev_ops = {
> +	.video = &ov4689_video_ops,
> +	.pad = &ov4689_pad_ops,
> +};
> +
> +static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct ov4689 *ov4689 =
> +		container_of(ctrl->handler, struct ov4689, ctrl_handler);
> +	struct i2c_client *client = ov4689->client;
> +	s64 max_expo;
> +	int ret;
> +
> +	/* Propagate change of current control to all related controls */
> +	switch (ctrl->id) {
> +	case V4L2_CID_VBLANK:
> +		/* Update max exposure while meeting expected vblanking */
> +		max_expo = ov4689->cur_mode->height + ctrl->val - 4;
> +		__v4l2_ctrl_modify_range(ov4689->exposure,
> +					 ov4689->exposure->minimum, max_expo,
> +					 ov4689->exposure->step,
> +					 ov4689->exposure->default_value);
> +		break;
> +	}
> +
> +	if (!pm_runtime_get_if_in_use(&client->dev))
> +		return 0;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_EXPOSURE:
> +		/* 4 least significant bits of expsoure are fractional part */
> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE,
> +				       OV4689_REG_VALUE_24BIT, ctrl->val << 4);
> +		break;
> +	case V4L2_CID_ANALOGUE_GAIN:
> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H,
> +				       OV4689_REG_VALUE_08BIT,
> +				       (ctrl->val >> OV4689_GAIN_H_SHIFT) &
> +					       OV4689_GAIN_H_MASK);
> +		ret |= ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L,

ret = ret ?: ...;

> +					OV4689_REG_VALUE_08BIT,
> +					ctrl->val & OV4689_GAIN_L_MASK);
> +		break;
> +	case V4L2_CID_VBLANK:
> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS,
> +				       OV4689_REG_VALUE_16BIT,
> +				       ctrl->val + ov4689->cur_mode->height);
> +		break;
> +	case V4L2_CID_TEST_PATTERN:
> +		ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
> +		break;
> +	default:
> +		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
> +			 __func__, ctrl->id, ctrl->val);
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	pm_runtime_put(&client->dev);
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
> +	.s_ctrl = ov4689_set_ctrl,
> +};
> +
> +static int ov4689_initialize_controls(struct ov4689 *ov4689)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&ov4689->subdev);
> +	struct v4l2_fwnode_device_properties props;
> +	struct v4l2_ctrl_handler *handler;
> +	const struct ov4689_mode *mode;
> +	s64 exposure_max, vblank_def;
> +	struct v4l2_ctrl *ctrl;
> +	u32 h_blank, pixel_rate;
> +	int ret;
> +
> +	handler = &ov4689->ctrl_handler;
> +	mode = ov4689->cur_mode;
> +	ret = v4l2_ctrl_handler_init(handler, 10);
> +	if (ret)
> +		return ret;
> +	handler->lock = &ov4689->mutex;
> +
> +	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
> +				      link_freq_menu_items);
> +	if (ctrl)
> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	pixel_rate = (link_freq_menu_items[0] * 2 * OV4689_LANES) /
> +		     OV4689_BITS_PER_SAMPLE;
> +	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, pixel_rate, 1,
> +			  pixel_rate);
> +
> +	h_blank = mode->hts_def - mode->width;
> +	ov4689->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
> +					   h_blank, h_blank, 1, h_blank);
> +	if (ov4689->hblank)
> +		ov4689->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	vblank_def = mode->vts_def - mode->height;
> +	ov4689->vblank =
> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
> +				  vblank_def, OV4689_VTS_MAX - mode->height, 1,
> +				  vblank_def);
> +
> +	exposure_max = mode->vts_def - 4;
> +	ov4689->exposure =
> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_EXPOSURE,
> +				  OV4689_EXPOSURE_MIN, exposure_max,
> +				  OV4689_EXPOSURE_STEP, mode->exp_def);
> +
> +	ov4689->anal_gain =
> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops,
> +				  V4L2_CID_ANALOGUE_GAIN, OV4689_GAIN_MIN,
> +				  OV4689_GAIN_MAX, OV4689_GAIN_STEP,
> +				  OV4689_GAIN_DEFAULT);
> +
> +	ov4689->test_pattern =
> +		v4l2_ctrl_new_std_menu_items(handler, &ov4689_ctrl_ops,
> +					     V4L2_CID_TEST_PATTERN,
> +					     ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
> +					     0, 0, ov4689_test_pattern_menu);
> +
> +	if (handler->error) {
> +		ret = handler->error;
> +		dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n",
> +			ret);
> +		goto err_free_handler;
> +	}
> +
> +	ret = v4l2_fwnode_device_parse(&client->dev, &props);
> +	if (ret)
> +		goto err_free_handler;
> +
> +	ret = v4l2_ctrl_new_fwnode_properties(handler, &ov4689_ctrl_ops,
> +					      &props);
> +	if (ret)
> +		goto err_free_handler;
> +
> +	ov4689->subdev.ctrl_handler = handler;
> +
> +	return 0;
> +
> +err_free_handler:
> +	v4l2_ctrl_handler_free(handler);
> +
> +	return ret;
> +}
> +
> +static int ov4689_check_sensor_id(struct ov4689 *ov4689,
> +				  struct i2c_client *client)
> +{
> +	struct device *dev = &ov4689->client->dev;
> +	u32 id = 0;
> +	int ret;
> +
> +	ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID,
> +			      OV4689_REG_VALUE_16BIT, &id);
> +	if (id != CHIP_ID) {
> +		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
> +		return -ENODEV;
> +	}
> +
> +	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
> +
> +	return 0;
> +}
> +
> +static int ov4689_configure_regulators(struct ov4689 *ov4689)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < OV4689_NUM_SUPPLIES; i++)
> +		ov4689->supplies[i].supply = ov4689_supply_names[i];
> +
> +	return devm_regulator_bulk_get(&ov4689->client->dev,
> +				       OV4689_NUM_SUPPLIES, ov4689->supplies);
> +}
> +
> +static int ov4689_check_hwcfg(struct device *dev)
> +{
> +	struct fwnode_handle *fwnode = dev_fwnode(dev);
> +	struct v4l2_fwnode_endpoint bus_cfg = {
> +		.bus_type = V4L2_MBUS_CSI2_DPHY,
> +	};
> +	struct fwnode_handle *endpoint;
> +	unsigned int i;
> +	int ret;
> +
> +	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
> +	if (!endpoint)
> +		return -EPROBE_DEFER;
> +
> +	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
> +	fwnode_handle_put(endpoint);
> +	if (ret)
> +		return ret;
> +
> +	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
> +		dev_err(dev, "only a 4-lane CSI2 config is supported");
> +		ret = -EINVAL;
> +		goto out_free_bus_cfg;
> +	}
> +
> +	if (!bus_cfg.nr_of_link_frequencies) {
> +		dev_err(dev, "no link frequencies defined\n");
> +		ret = -EINVAL;
> +		goto out_free_bus_cfg;
> +	}
> +
> +	for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
> +		if (bus_cfg.link_frequencies[i] == OV4689_LINK_FREQ_500MHZ)

Please instead compare with array entries.

> +			break;
> +
> +	if (i == bus_cfg.nr_of_link_frequencies) {
> +		dev_err(dev, "supported link freq %ull not found\n",
> +			OV4689_LINK_FREQ_500MHZ);
> +		ret = -EINVAL;
> +		goto out_free_bus_cfg;
> +	}
> +
> +out_free_bus_cfg:
> +	v4l2_fwnode_endpoint_free(&bus_cfg);
> +
> +	return ret;
> +}
> +
> +static int ov4689_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct device *dev = &client->dev;
> +	struct v4l2_subdev *sd;
> +	struct ov4689 *ov4689;
> +	int ret;
> +
> +	ret = ov4689_check_hwcfg(dev);
> +	if (ret)
> +		return ret;
> +
> +	ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
> +	if (!ov4689)
> +		return -ENOMEM;
> +
> +	ov4689->client = client;
> +	ov4689->cur_mode = &supported_modes[0];
> +
> +	ov4689->xvclk = devm_clk_get(dev, "xvclk");
> +	if (IS_ERR(ov4689->xvclk)) {
> +		dev_err(dev, "Failed to get xvclk\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);

Please see handling clocks in:

<URL:https://hverkuil.home.xs4all.nl/spec/driver-api/camera-sensor.html>

> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
> +		return ret;
> +	}
> +	if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
> +		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
> +
> +	ov4689->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(ov4689->reset_gpio)) {
> +		dev_err(dev, "Failed to get reset-gpios\n");
> +		return -EINVAL;
> +	}
> +
> +	ov4689->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
> +	if (IS_ERR(ov4689->pwdn_gpio)) {
> +		dev_err(dev, "Failed to get pwdn-gpios\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = ov4689_configure_regulators(ov4689);
> +	if (ret) {
> +		dev_err(dev, "Failed to get power regulators\n");
> +		return ret;
> +	}
> +
> +	mutex_init(&ov4689->mutex);
> +
> +	sd = &ov4689->subdev;
> +	v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops);
> +	ret = ov4689_initialize_controls(ov4689);
> +	if (ret)
> +		goto err_destroy_mutex;
> +
> +	ret = __ov4689_power_on(ov4689);
> +	if (ret)
> +		goto err_free_handler;
> +
> +	ret = ov4689_check_sensor_id(ov4689, client);
> +	if (ret)
> +		goto err_power_off;
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +	sd->internal_ops = &ov4689_internal_ops;
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +#endif
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +	ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
> +	if (ret < 0)
> +		goto err_power_off;
> +#endif
> +
> +	ret = v4l2_async_register_subdev_sensor(sd);
> +	if (ret) {
> +		dev_err(dev, "v4l2 async register subdev failed\n");
> +		goto err_clean_entity;
> +	}
> +
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
> +	pm_runtime_idle(dev);
> +
> +	return 0;
> +
> +err_clean_entity:
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	media_entity_cleanup(&sd->entity);
> +#endif
> +err_power_off:
> +	__ov4689_power_off(ov4689);
> +err_free_handler:
> +	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
> +err_destroy_mutex:
> +	mutex_destroy(&ov4689->mutex);
> +
> +	return ret;
> +}
> +
> +static int ov4689_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +	v4l2_async_unregister_subdev(sd);
> +#if defined(CONFIG_MEDIA_CONTROLLER)

No need for #if here, please drop.

> +	media_entity_cleanup(&sd->entity);
> +#endif
> +	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
> +	mutex_destroy(&ov4689->mutex);
> +
> +	pm_runtime_disable(&client->dev);
> +	if (!pm_runtime_status_suspended(&client->dev))
> +		__ov4689_power_off(ov4689);
> +	pm_runtime_set_suspended(&client->dev);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ov4689_id[] = {
> +	{ "ov4689", 0 },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(i2c, ov4689_id);
> +
> +static const struct of_device_id ov4689_of_match[] = {
> +	{ .compatible = "ovti,ov4689" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, ov4689_of_match);
> +
> +static struct i2c_driver ov4689_i2c_driver = {
> +	.driver = {
> +		.name = "ov4689",
> +		.pm = &ov4689_pm_ops,
> +		.of_match_table = of_match_ptr(ov4689_of_match),
> +	},
> +	.probe = ov4689_probe,
> +	.remove	= ov4689_remove,
> +	.id_table = ov4689_id,
> +};
> +
> +module_i2c_driver(ov4689_i2c_driver);
> +
> +MODULE_DESCRIPTION("OmniVision ov4689 sensor driver");
> +MODULE_LICENSE("GPL");
> -- 
> 2.37.3
> 

-- 
Sakari Ailus

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-11 20:01 [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver Mikhail Rudenko
                   ` (2 preceding siblings ...)
  2022-09-14  9:58 ` [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver Dave Stevenson
@ 2022-09-22  9:53 ` Sakari Ailus
  2022-09-22 10:43   ` Dave Stevenson
  3 siblings, 1 reply; 41+ messages in thread
From: Sakari Ailus @ 2022-09-22  9:53 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Hans Verkuil, Jacopo Mondi, Shawn Tu, Jimmy Su, Arnd Bergmann,
	Arec Kao, Laurent Pinchart, Marek Vasut, linux-media, devicetree,
	linux-kernel

Hi Mikhail,

On Sun, Sep 11, 2022 at 11:01:33PM +0300, Mikhail Rudenko wrote:
> Hello,
> 
> this series implements support for Omnivision OV4689 image
> sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
> megapixel image sensor. Ihis chip supports high frame rate speeds up
> to 90 fps at 2688x1520 resolution. It is programmable through an I2C
> interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> connection.
> 
> The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
> and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
> 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera module.
> 
> While porting the driver, I stumbled upon two issues:
> 
> (1) In the original driver, horizontal total size (HTS) was set to a
> value (2584) lower then the frame width (2688), resulting in negative
> hblank. In this driver, I increased HTS to 2688, but fps dropped from
> 29.88 to 28.73. What is the preferred way to handle this?

If horizontal total size is less than the frame width, something is
certainly wrong there. You can't have negative horizontal blanking. Neither
it can be zero.

> 
> (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
> gain is not linear across that range. Instead, it is piecewise linear
> (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
> 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
> with more linear segments in between. Rockchip's camera engine code
> chooses one of the above segments depenging on the desired gain
> value. The question is, how should we proceed keeping in mind
> libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
> libcamera will do the mapping, or the driver will do the mapping
> itself and expose some logical gain units not tied to the actual gain
> register value? Meanwhile, this driver conservatively exposes only
> 0x0-0xf8 gain register range.

-- 
Sakari Ailus

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-22  9:53 ` Sakari Ailus
@ 2022-09-22 10:43   ` Dave Stevenson
  2022-09-22 15:13     ` Mikhail Rudenko
  2022-09-26 10:47     ` Mikhail Rudenko
  0 siblings, 2 replies; 41+ messages in thread
From: Dave Stevenson @ 2022-09-22 10:43 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Mikhail Rudenko, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Jimmy Su, Arnd Bergmann, Arec Kao, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail & Sakari

On Thu, 22 Sept 2022 at 10:55, Sakari Ailus
<sakari.ailus@linux.intel.com> wrote:
>
> Hi Mikhail,
>
> On Sun, Sep 11, 2022 at 11:01:33PM +0300, Mikhail Rudenko wrote:
> > Hello,
> >
> > this series implements support for Omnivision OV4689 image
> > sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
> > megapixel image sensor. Ihis chip supports high frame rate speeds up
> > to 90 fps at 2688x1520 resolution. It is programmable through an I2C
> > interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
> > connection.
> >
> > The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
> > and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
> > 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera module.
> >
> > While porting the driver, I stumbled upon two issues:
> >
> > (1) In the original driver, horizontal total size (HTS) was set to a
> > value (2584) lower then the frame width (2688), resulting in negative
> > hblank. In this driver, I increased HTS to 2688, but fps dropped from
> > 29.88 to 28.73. What is the preferred way to handle this?
>
> If horizontal total size is less than the frame width, something is
> certainly wrong there. You can't have negative horizontal blanking. Neither
> it can be zero.

Something certainly seems odd.

To continue my thoughts from earlier in this patch set, Omnivision's
Product Brief [1] states:
The 1/3-inch OV4689 can capture full-resolution 4-megapixel high
definition (HD) video at 90 frames per second (fps), 1080p HD at 120
fps, and binned 720p HD at 180 fps

The datasheet section 2.1 states:
The OV4689 color image sensor is a high performance, 4 megapixel RAW
image sensor that delivers 2688x1520 at 90 fps using OmniBSI-2™ pixel
technology.

So 4MP 90fps or 1080p120 should be achievable somehow.

2688x1520 @ 90fps is 367.7MPix/s, and that tallies quite nicely with
table 2-9 listing the DAC PLL speed limitation of 360-378MHz. Exactly
how that is then converted into PCLK or SCLK is unclear.
Ideally you'd be able to contact an Omnivision FAE to confirm, but
that means you need to be buying modules directly from them or
otherwise have a business relationship.
I do note that there is an NVidia Tegra driver for OV4689 at [2]. I
wonder if analysis of that would reveal anything.

I have just been looking at the ov9282 driver and the timings don't
tally there either - configure it for 60fps and you get 30fps. The
TIMING_HTS register appears to be in units of 2 pixels. The default is
0x2d8 (728 decimal) on a 1280x720 mode, but consider it as units of 2
pixels and HTS of 1456 (1280 active and hblank of 176) does match up.
It works in the general case too.

Looking at the OV4689 datasheet again, the default for TIMING_HTS
(0x380c/d) is 0x5f8 (1528 decimal) when HOUTPUT_SIZE (0x3808/9) is
0x1200 (4608 decimal). Whilst there are no guarantees that default
register settings will stream in any sensible form, it does imply
TIMING_HTS is not in units of 1 pixel, and potentially 4 pixels.
From the Rockchip BSP driver it plainly does stream at 30 (or 29.88)
fps with TIMING_HTS < HOUTPUT_SIZE, so a quick test of reducing it
further would be worthwhile. What does the default of 0x2d8 give you
as a frame rate, and are the images correct?

Just some thoughts.
  Dave

[1] https://www.ovt.com/wp-content/uploads/2022/01/OV4689-PB-v1.7-WEB.pdf
[2] https://github.com/bogsen/STLinux-Kernel/blob/master/drivers/media/platform/tegra/ov4689.c


> >
> > (2) The original driver exposes analog gain range 0x0 - 0x7ff, but the
> > gain is not linear across that range. Instead, it is piecewise linear
> > (and discontinuous). 0x0-0xff register values result in 0x-2x gain,
> > 0x100-0x1ff to 0x-4x, 0x300-0x3ff to 0x-8x, and 0x700-0x7ff to 0x-16x,
> > with more linear segments in between. Rockchip's camera engine code
> > chooses one of the above segments depenging on the desired gain
> > value. The question is, how should we proceed keeping in mind
> > libcamera use case? Should the whole 0x0-0x7ff be exposed as-is and
> > libcamera will do the mapping, or the driver will do the mapping
> > itself and expose some logical gain units not tied to the actual gain
> > register value? Meanwhile, this driver conservatively exposes only
> > 0x0-0xf8 gain register range.
>
> --
> Sakari Ailus

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-11 20:01 ` [PATCH v2 2/2] media: i2c: add support for ov4689 Mikhail Rudenko
                     ` (3 preceding siblings ...)
  2022-09-22  9:53   ` Sakari Ailus
@ 2022-09-22 10:54   ` Dave Stevenson
  2022-09-22 14:56     ` Mikhail Rudenko
  4 siblings, 1 reply; 41+ messages in thread
From: Dave Stevenson @ 2022-09-22 10:54 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail

On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko <mike.rudenko@gmail.com> wrote:
>
> Add a V4L2 sub-device driver for OmniVision OV4689 image sensor. This
> is a 4 Mpx image sensor using the I2C bus for control and the CSI-2
> bus for data.
>
> This driver supports following features:
> - manual exposure and analog gain control support
> - test pattern support
> - media controller support
> - runtime PM support
> - support following resolutions:
>   + 2688x1520 at 30 fps
>
> The driver provides all mandatory V4L2 controls for compatibility with
> libcamera. The sensor supports 1/2/4-lane CSI-2 modes, but the driver
> implements 4 lane mode only at this moment.
>
> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
> ---
>  MAINTAINERS                |   1 +
>  drivers/media/i2c/Kconfig  |  14 +
>  drivers/media/i2c/Makefile |   1 +
>  drivers/media/i2c/ov4689.c | 951 +++++++++++++++++++++++++++++++++++++
>  4 files changed, 967 insertions(+)
>  create mode 100644 drivers/media/i2c/ov4689.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 63c4844f26e6..1857f3864e1b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14529,6 +14529,7 @@ L:      linux-media@vger.kernel.org
>  S:     Maintained
>  T:     git git://linuxtv.org/media_tree.git
>  F:     Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
> +F:     drivers/media/i2c/ov5647.c
>
>  OMNIVISION OV5640 SENSOR DRIVER
>  M:     Steve Longerbeam <slongerbeam@gmail.com>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index fae2baabb773..4993e1ae2ea8 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -429,6 +429,20 @@ config VIDEO_OV2740
>           To compile this driver as a module, choose M here: the
>           module will be called ov2740.
>
> +config VIDEO_OV4689
> +       tristate "OmniVision OV4689 sensor support"
> +       depends on OF
> +       depends on GPIOLIB && VIDEO_DEV && I2C
> +       select MEDIA_CONTROLLER
> +       select VIDEO_V4L2_SUBDEV_API
> +       select V4L2_FWNODE
> +       help
> +         This is a Video4Linux2 sensor-level driver for the OmniVision
> +         OV4689 camera.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called ov4689.
> +
>  config VIDEO_OV5640
>         tristate "OmniVision OV5640 sensor support"
>         depends on OF
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 3e1696963e7f..7446c0a1eed0 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
>  obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
>  obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
>  obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
> +obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
>  obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>  obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
>  obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
> diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c
> new file mode 100644
> index 000000000000..9f05e812acf8
> --- /dev/null
> +++ b/drivers/media/i2c/ov4689.c
> @@ -0,0 +1,951 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ov4689 driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-fwnode.h>
> +
> +#define CHIP_ID                                0x004688
> +#define OV4689_REG_CHIP_ID             0x300a
> +
> +#define OV4689_XVCLK_FREQ              24000000
> +
> +#define OV4689_REG_CTRL_MODE           0x0100
> +#define OV4689_MODE_SW_STANDBY         0x0
> +#define OV4689_MODE_STREAMING          BIT(0)
> +
> +#define OV4689_REG_EXPOSURE            0x3500
> +#define OV4689_EXPOSURE_MIN            4
> +#define OV4689_EXPOSURE_STEP           1
> +#define OV4689_VTS_MAX                 0x7fff
> +
> +#define OV4689_REG_GAIN_H              0x3508
> +#define OV4689_REG_GAIN_L              0x3509
> +#define OV4689_GAIN_H_MASK             0x07
> +#define OV4689_GAIN_H_SHIFT            8
> +#define OV4689_GAIN_L_MASK             0xff
> +#define OV4689_GAIN_MIN                        0x10
> +#define OV4689_GAIN_MAX                        0xf8
> +#define OV4689_GAIN_STEP               1
> +#define OV4689_GAIN_DEFAULT            0x10
> +
> +#define OV4689_REG_TEST_PATTERN                0x5040
> +#define OV4689_TEST_PATTERN_ENABLE     0x80
> +#define OV4689_TEST_PATTERN_DISABLE    0x0
> +
> +#define OV4689_REG_VTS                 0x380e
> +
> +#define REG_NULL                       0xFFFF
> +
> +#define OV4689_REG_VALUE_08BIT         1
> +#define OV4689_REG_VALUE_16BIT         2
> +#define OV4689_REG_VALUE_24BIT         3
> +
> +#define OV4689_LANES                   4
> +#define OV4689_BITS_PER_SAMPLE         10
> +
> +static const char *const ov4689_supply_names[] = {
> +       "avdd", /* Analog power */
> +       "dovdd", /* Digital I/O power */
> +       "dvdd", /* Digital core power */
> +};
> +
> +#define OV4689_NUM_SUPPLIES ARRAY_SIZE(ov4689_supply_names)
> +
> +struct regval {
> +       u16 addr;
> +       u8 val;
> +};
> +
> +struct ov4689_mode {
> +       u32 width;
> +       u32 height;
> +       u32 max_fps;
> +       u32 hts_def;
> +       u32 vts_def;
> +       u32 exp_def;
> +       const struct regval *reg_list;
> +};
> +
> +struct ov4689 {
> +       struct i2c_client *client;
> +       struct clk *xvclk;
> +       struct gpio_desc *reset_gpio;
> +       struct gpio_desc *pwdn_gpio;
> +       struct regulator_bulk_data supplies[OV4689_NUM_SUPPLIES];
> +
> +       struct v4l2_subdev subdev;
> +       struct media_pad pad;
> +
> +       struct mutex mutex; /* lock to protect streaming, ctrls and cur_mode */
> +       bool streaming;
> +       struct v4l2_ctrl_handler ctrl_handler;
> +       struct v4l2_ctrl *exposure;
> +       struct v4l2_ctrl *anal_gain;
> +       struct v4l2_ctrl *digi_gain;
> +       struct v4l2_ctrl *hblank;
> +       struct v4l2_ctrl *vblank;
> +       struct v4l2_ctrl *test_pattern;
> +
> +       const struct ov4689_mode *cur_mode;
> +};
> +
> +#define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
> +
> +/*
> + * Xclk 24Mhz
> + */
> +static const struct regval ov4689_global_regs[] = {
> +       { REG_NULL, 0x00 },
> +};
> +
> +/*
> + * Xclk 24Mhz
> + * max_framerate 30fps
> + * mipi_datarate per lane 1008Mbps

Data rate stated as 1008Mbps here....

> + */
> +static const struct regval ov4689_2688x1520_regs[] = {
> +       {0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
> +       {0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
> +       {0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
> +       {0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
> +       {0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
> +       {0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
> +       {0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
> +       {0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
> +       {0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
> +       {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
> +       {0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
> +       {0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
> +       {0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
> +       {0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
> +       {0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
> +       {0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
> +       {0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
> +       {0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
> +       {0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
> +       {0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
> +       {0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
> +       {0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
> +       {0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
> +       {0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
> +       {0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
> +       {0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
> +       {0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
> +       {0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
> +       {0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
> +       {0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
> +       {0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
> +       {0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
> +       {0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
> +       {0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
> +       {0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
> +       {0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
> +       {0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
> +       {0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
> +       {0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
> +       {0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
> +       {0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
> +       {0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
> +       {0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
> +       {0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
> +       {0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
> +       {0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
> +       {0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
> +       {0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
> +       {0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
> +       {0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
> +       {0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
> +       {0x380d, 0x80}, {0x380e, 0x06}, {0x380f, 0x12},
> +       {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
> +       {0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
> +       {0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
> +       {0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
> +       {0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
> +       {0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
> +       {0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
> +       {0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
> +       {0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
> +       {0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
> +       {0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
> +       {0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
> +       {0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
> +       {0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
> +       {0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
> +       {0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
> +       {0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
> +       {0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
> +       {0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
> +       {0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
> +       {0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
> +       {0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
> +       {0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
> +       {0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
> +       {0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
> +       {0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
> +       {0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
> +       {0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
> +       {0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
> +       {0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
> +       {0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
> +       {0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
> +       {REG_NULL, 0x00},
> +};
> +
> +static const struct ov4689_mode supported_modes[] = {
> +       {
> +               .width = 2688,
> +               .height = 1520,
> +               .max_fps = 30,
> +               .exp_def = 0x0600,
> +               .hts_def = 0x0a80,
> +               .vts_def = 0x0612,
> +               .reg_list = ov4689_2688x1520_regs,
> +       },
> +};
> +
> +#define OV4689_LINK_FREQ_500MHZ 500000000
> +static const s64 link_freq_menu_items[] = { OV4689_LINK_FREQ_500MHZ };

... but a link frequency of 500MHz (ie 1000Mbit/s) here.
Seeing as you compute the pixel rate based on the link frequency,
that's going to mean that the pixel rate is incorrect.
Link frequency should be 504MHz.

Your PLL settings appear to match the 24MHz configuration in table
2-11 "sample PLL configuration" of the datasheet, so it would confirm
that MIPI_SCLK is 1008MHz and MIPI_PCLK is 126MHz (at 1008/8 seems to
be more byte clock than pixel (10bpp) clock).

  Dave

> +
> +static const char *const ov4689_test_pattern_menu[] = {
> +       "Disabled",
> +       "Vertical Color Bar Type 1",
> +       "Vertical Color Bar Type 2",
> +       "Vertical Color Bar Type 3",
> +       "Vertical Color Bar Type 4"
> +};
> +
> +/* Write registers up to 4 at a time */
> +static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len,
> +                           u32 val)
> +{
> +       u32 buf_i, val_i;
> +       __be32 val_be;
> +       u8 *val_p;
> +       u8 buf[6];
> +
> +       if (len > 4)
> +               return -EINVAL;
> +
> +       buf[0] = reg >> 8;
> +       buf[1] = reg & 0xff;
> +
> +       val_be = cpu_to_be32(val);
> +       val_p = (u8 *)&val_be;
> +       buf_i = 2;
> +       val_i = 4 - len;
> +
> +       while (val_i < 4)
> +               buf[buf_i++] = val_p[val_i++];
> +
> +       if (i2c_master_send(client, buf, len + 2) != len + 2)
> +               return -EIO;
> +
> +       return 0;
> +}
> +
> +static int ov4689_write_array(struct i2c_client *client,
> +                             const struct regval *regs)
> +{
> +       int ret = 0;
> +       u32 i;
> +
> +       for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
> +               ret = ov4689_write_reg(client, regs[i].addr,
> +                                      OV4689_REG_VALUE_08BIT, regs[i].val);
> +
> +       return ret;
> +}
> +
> +/* Read registers up to 4 at a time */
> +static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
> +                          u32 *val)
> +{
> +       __be16 reg_addr_be = cpu_to_be16(reg);
> +       struct i2c_msg msgs[2];
> +       __be32 data_be = 0;
> +       u8 *data_be_p;
> +       int ret;
> +
> +       if (len > 4 || !len)
> +               return -EINVAL;
> +
> +       data_be_p = (u8 *)&data_be;
> +       /* Write register address */
> +       msgs[0].addr = client->addr;
> +       msgs[0].flags = 0;
> +       msgs[0].len = 2;
> +       msgs[0].buf = (u8 *)&reg_addr_be;
> +
> +       /* Read data from register */
> +       msgs[1].addr = client->addr;
> +       msgs[1].flags = I2C_M_RD;
> +       msgs[1].len = len;
> +       msgs[1].buf = &data_be_p[4 - len];
> +
> +       ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> +       if (ret != ARRAY_SIZE(msgs))
> +               return -EIO;
> +
> +       *val = be32_to_cpu(data_be);
> +
> +       return 0;
> +}
> +
> +static void ov4689_fill_fmt(const struct ov4689_mode *mode,
> +                           struct v4l2_mbus_framefmt *fmt)
> +{
> +       fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +       fmt->width = mode->width;
> +       fmt->height = mode->height;
> +       fmt->field = V4L2_FIELD_NONE;
> +}
> +
> +static int ov4689_set_fmt(struct v4l2_subdev *sd,
> +                         struct v4l2_subdev_state *sd_state,
> +                         struct v4l2_subdev_format *fmt)
> +{
> +       struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
> +       struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +       /* only one mode supported for now */
> +       ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
> +
> +       return 0;
> +}
> +
> +static int ov4689_get_fmt(struct v4l2_subdev *sd,
> +                         struct v4l2_subdev_state *sd_state,
> +                         struct v4l2_subdev_format *fmt)
> +{
> +       struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
> +       struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +       /* only one mode supported for now */
> +       ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
> +
> +       return 0;
> +}
> +
> +static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
> +                                struct v4l2_subdev_state *sd_state,
> +                                struct v4l2_subdev_mbus_code_enum *code)
> +{
> +       if (code->index != 0)
> +               return -EINVAL;
> +       code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +
> +       return 0;
> +}
> +
> +static int ov4689_enum_frame_sizes(struct v4l2_subdev *sd,
> +                                  struct v4l2_subdev_state *sd_state,
> +                                  struct v4l2_subdev_frame_size_enum *fse)
> +{
> +       if (fse->index >= ARRAY_SIZE(supported_modes))
> +               return -EINVAL;
> +
> +       if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
> +               return -EINVAL;
> +
> +       fse->min_width = supported_modes[fse->index].width;
> +       fse->max_width = supported_modes[fse->index].width;
> +       fse->max_height = supported_modes[fse->index].height;
> +       fse->min_height = supported_modes[fse->index].height;
> +
> +       return 0;
> +}
> +
> +static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
> +{
> +       u32 val;
> +
> +       if (pattern)
> +               val = (pattern - 1) | OV4689_TEST_PATTERN_ENABLE;
> +       else
> +               val = OV4689_TEST_PATTERN_DISABLE;
> +
> +       return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN,
> +                               OV4689_REG_VALUE_08BIT, val);
> +}
> +
> +static int ov4689_get_selection(struct v4l2_subdev *sd,
> +                               struct v4l2_subdev_state *state,
> +                               struct v4l2_subdev_selection *sel)
> +{
> +       if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
> +               return -EINVAL;
> +
> +       switch (sel->target) {
> +       case V4L2_SEL_TGT_CROP_BOUNDS:
> +               sel->r.top = 0;
> +               sel->r.left = 0;
> +               sel->r.width = 2720;
> +               sel->r.height = 1536;
> +               return 0;
> +       case V4L2_SEL_TGT_CROP:
> +       case V4L2_SEL_TGT_CROP_DEFAULT:
> +               sel->r.top = 8;
> +               sel->r.left = 16;
> +               sel->r.width = 2688;
> +               sel->r.height = 1520;
> +               return 0;
> +       }
> +       return -EINVAL;
> +}
> +
> +static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
> +{
> +       struct ov4689 *ov4689 = to_ov4689(sd);
> +       struct i2c_client *client = ov4689->client;
> +       int ret = 0;
> +
> +       mutex_lock(&ov4689->mutex);
> +
> +       on = !!on;
> +       if (on == ov4689->streaming)
> +               goto unlock_and_return;
> +
> +       if (on) {
> +               ret = pm_runtime_resume_and_get(&client->dev);
> +               if (ret < 0)
> +                       goto unlock_and_return;
> +
> +               ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
> +               if (ret) {
> +                       pm_runtime_put(&client->dev);
> +                       goto unlock_and_return;
> +               }
> +
> +               ret = ov4689_write_array(ov4689->client,
> +                                        ov4689->cur_mode->reg_list);
> +               if (ret) {
> +                       pm_runtime_put(&client->dev);
> +                       goto unlock_and_return;
> +               }
> +
> +               ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
> +                                      OV4689_REG_VALUE_08BIT,
> +                                      OV4689_MODE_STREAMING);
> +               if (ret) {
> +                       pm_runtime_put(&client->dev);
> +                       goto unlock_and_return;
> +               }
> +       } else {
> +               ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
> +                                OV4689_REG_VALUE_08BIT,
> +                                OV4689_MODE_SW_STANDBY);
> +               pm_runtime_put(&client->dev);
> +       }
> +
> +       ov4689->streaming = on;
> +
> +unlock_and_return:
> +       mutex_unlock(&ov4689->mutex);
> +
> +       return ret;
> +}
> +
> +/* Calculate the delay in us by clock rate and clock cycles */
> +static inline u32 ov4689_cal_delay(u32 cycles)
> +{
> +       return DIV_ROUND_UP(cycles, OV4689_XVCLK_FREQ / 1000 / 1000);
> +}
> +
> +static int __ov4689_power_on(struct ov4689 *ov4689)
> +{
> +       struct device *dev = &ov4689->client->dev;
> +       u32 delay_us;
> +       int ret;
> +
> +       ret = clk_prepare_enable(ov4689->xvclk);
> +       if (ret < 0) {
> +               dev_err(dev, "Failed to enable xvclk\n");
> +               return ret;
> +       }
> +
> +       gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
> +
> +       ret = regulator_bulk_enable(OV4689_NUM_SUPPLIES, ov4689->supplies);
> +       if (ret < 0) {
> +               dev_err(dev, "Failed to enable regulators\n");
> +               goto disable_clk;
> +       }
> +
> +       gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
> +       usleep_range(500, 1000);
> +       gpiod_set_value_cansleep(ov4689->pwdn_gpio, 0);
> +
> +       /* 8192 cycles prior to first SCCB transaction */
> +       delay_us = ov4689_cal_delay(8192);
> +       usleep_range(delay_us, delay_us * 2);
> +
> +       return 0;
> +
> +disable_clk:
> +       clk_disable_unprepare(ov4689->xvclk);
> +
> +       return ret;
> +}
> +
> +static void __ov4689_power_off(struct ov4689 *ov4689)
> +{
> +       gpiod_set_value_cansleep(ov4689->pwdn_gpio, 1);
> +       clk_disable_unprepare(ov4689->xvclk);
> +       gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
> +       regulator_bulk_disable(OV4689_NUM_SUPPLIES, ov4689->supplies);
> +}
> +
> +static int __maybe_unused ov4689_runtime_resume(struct device *dev)
> +{
> +       struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +       struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +       return __ov4689_power_on(ov4689);
> +}
> +
> +static int __maybe_unused ov4689_runtime_suspend(struct device *dev)
> +{
> +       struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +       struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +       __ov4689_power_off(ov4689);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +       struct ov4689 *ov4689 = to_ov4689(sd);
> +       struct v4l2_mbus_framefmt *try_fmt;
> +
> +       mutex_lock(&ov4689->mutex);
> +
> +       try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0);
> +       /* Initialize try_fmt */
> +       ov4689_fill_fmt(&supported_modes[0], try_fmt);
> +
> +       mutex_unlock(&ov4689->mutex);
> +
> +       return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops ov4689_pm_ops = {
> +       SET_RUNTIME_PM_OPS(ov4689_runtime_suspend, ov4689_runtime_resume, NULL)
> +};
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
> +       .open = ov4689_open,
> +};
> +#endif
> +
> +static const struct v4l2_subdev_video_ops ov4689_video_ops = {
> +       .s_stream = ov4689_s_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
> +       .enum_mbus_code = ov4689_enum_mbus_code,
> +       .enum_frame_size = ov4689_enum_frame_sizes,
> +       .get_fmt = ov4689_get_fmt,
> +       .set_fmt = ov4689_set_fmt,
> +       .get_selection = ov4689_get_selection,
> +};
> +
> +static const struct v4l2_subdev_ops ov4689_subdev_ops = {
> +       .video = &ov4689_video_ops,
> +       .pad = &ov4689_pad_ops,
> +};
> +
> +static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +       struct ov4689 *ov4689 =
> +               container_of(ctrl->handler, struct ov4689, ctrl_handler);
> +       struct i2c_client *client = ov4689->client;
> +       s64 max_expo;
> +       int ret;
> +
> +       /* Propagate change of current control to all related controls */
> +       switch (ctrl->id) {
> +       case V4L2_CID_VBLANK:
> +               /* Update max exposure while meeting expected vblanking */
> +               max_expo = ov4689->cur_mode->height + ctrl->val - 4;
> +               __v4l2_ctrl_modify_range(ov4689->exposure,
> +                                        ov4689->exposure->minimum, max_expo,
> +                                        ov4689->exposure->step,
> +                                        ov4689->exposure->default_value);
> +               break;
> +       }
> +
> +       if (!pm_runtime_get_if_in_use(&client->dev))
> +               return 0;
> +
> +       switch (ctrl->id) {
> +       case V4L2_CID_EXPOSURE:
> +               /* 4 least significant bits of expsoure are fractional part */
> +               ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE,
> +                                      OV4689_REG_VALUE_24BIT, ctrl->val << 4);
> +               break;
> +       case V4L2_CID_ANALOGUE_GAIN:
> +               ret = ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H,
> +                                      OV4689_REG_VALUE_08BIT,
> +                                      (ctrl->val >> OV4689_GAIN_H_SHIFT) &
> +                                              OV4689_GAIN_H_MASK);
> +               ret |= ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L,
> +                                       OV4689_REG_VALUE_08BIT,
> +                                       ctrl->val & OV4689_GAIN_L_MASK);
> +               break;
> +       case V4L2_CID_VBLANK:
> +               ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS,
> +                                      OV4689_REG_VALUE_16BIT,
> +                                      ctrl->val + ov4689->cur_mode->height);
> +               break;
> +       case V4L2_CID_TEST_PATTERN:
> +               ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
> +               break;
> +       default:
> +               dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
> +                        __func__, ctrl->id, ctrl->val);
> +               ret = -EINVAL;
> +               break;
> +       }
> +
> +       pm_runtime_put(&client->dev);
> +
> +       return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
> +       .s_ctrl = ov4689_set_ctrl,
> +};
> +
> +static int ov4689_initialize_controls(struct ov4689 *ov4689)
> +{
> +       struct i2c_client *client = v4l2_get_subdevdata(&ov4689->subdev);
> +       struct v4l2_fwnode_device_properties props;
> +       struct v4l2_ctrl_handler *handler;
> +       const struct ov4689_mode *mode;
> +       s64 exposure_max, vblank_def;
> +       struct v4l2_ctrl *ctrl;
> +       u32 h_blank, pixel_rate;
> +       int ret;
> +
> +       handler = &ov4689->ctrl_handler;
> +       mode = ov4689->cur_mode;
> +       ret = v4l2_ctrl_handler_init(handler, 10);
> +       if (ret)
> +               return ret;
> +       handler->lock = &ov4689->mutex;
> +
> +       ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
> +                                     link_freq_menu_items);
> +       if (ctrl)
> +               ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +       pixel_rate = (link_freq_menu_items[0] * 2 * OV4689_LANES) /
> +                    OV4689_BITS_PER_SAMPLE;
> +       v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, pixel_rate, 1,
> +                         pixel_rate);
> +
> +       h_blank = mode->hts_def - mode->width;
> +       ov4689->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
> +                                          h_blank, h_blank, 1, h_blank);
> +       if (ov4689->hblank)
> +               ov4689->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +       vblank_def = mode->vts_def - mode->height;
> +       ov4689->vblank =
> +               v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
> +                                 vblank_def, OV4689_VTS_MAX - mode->height, 1,
> +                                 vblank_def);
> +
> +       exposure_max = mode->vts_def - 4;
> +       ov4689->exposure =
> +               v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_EXPOSURE,
> +                                 OV4689_EXPOSURE_MIN, exposure_max,
> +                                 OV4689_EXPOSURE_STEP, mode->exp_def);
> +
> +       ov4689->anal_gain =
> +               v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops,
> +                                 V4L2_CID_ANALOGUE_GAIN, OV4689_GAIN_MIN,
> +                                 OV4689_GAIN_MAX, OV4689_GAIN_STEP,
> +                                 OV4689_GAIN_DEFAULT);
> +
> +       ov4689->test_pattern =
> +               v4l2_ctrl_new_std_menu_items(handler, &ov4689_ctrl_ops,
> +                                            V4L2_CID_TEST_PATTERN,
> +                                            ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
> +                                            0, 0, ov4689_test_pattern_menu);
> +
> +       if (handler->error) {
> +               ret = handler->error;
> +               dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n",
> +                       ret);
> +               goto err_free_handler;
> +       }
> +
> +       ret = v4l2_fwnode_device_parse(&client->dev, &props);
> +       if (ret)
> +               goto err_free_handler;
> +
> +       ret = v4l2_ctrl_new_fwnode_properties(handler, &ov4689_ctrl_ops,
> +                                             &props);
> +       if (ret)
> +               goto err_free_handler;
> +
> +       ov4689->subdev.ctrl_handler = handler;
> +
> +       return 0;
> +
> +err_free_handler:
> +       v4l2_ctrl_handler_free(handler);
> +
> +       return ret;
> +}
> +
> +static int ov4689_check_sensor_id(struct ov4689 *ov4689,
> +                                 struct i2c_client *client)
> +{
> +       struct device *dev = &ov4689->client->dev;
> +       u32 id = 0;
> +       int ret;
> +
> +       ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID,
> +                             OV4689_REG_VALUE_16BIT, &id);
> +       if (id != CHIP_ID) {
> +               dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
> +               return -ENODEV;
> +       }
> +
> +       dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
> +
> +       return 0;
> +}
> +
> +static int ov4689_configure_regulators(struct ov4689 *ov4689)
> +{
> +       unsigned int i;
> +
> +       for (i = 0; i < OV4689_NUM_SUPPLIES; i++)
> +               ov4689->supplies[i].supply = ov4689_supply_names[i];
> +
> +       return devm_regulator_bulk_get(&ov4689->client->dev,
> +                                      OV4689_NUM_SUPPLIES, ov4689->supplies);
> +}
> +
> +static int ov4689_check_hwcfg(struct device *dev)
> +{
> +       struct fwnode_handle *fwnode = dev_fwnode(dev);
> +       struct v4l2_fwnode_endpoint bus_cfg = {
> +               .bus_type = V4L2_MBUS_CSI2_DPHY,
> +       };
> +       struct fwnode_handle *endpoint;
> +       unsigned int i;
> +       int ret;
> +
> +       endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
> +       if (!endpoint)
> +               return -EPROBE_DEFER;
> +
> +       ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
> +       fwnode_handle_put(endpoint);
> +       if (ret)
> +               return ret;
> +
> +       if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
> +               dev_err(dev, "only a 4-lane CSI2 config is supported");
> +               ret = -EINVAL;
> +               goto out_free_bus_cfg;
> +       }
> +
> +       if (!bus_cfg.nr_of_link_frequencies) {
> +               dev_err(dev, "no link frequencies defined\n");
> +               ret = -EINVAL;
> +               goto out_free_bus_cfg;
> +       }
> +
> +       for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
> +               if (bus_cfg.link_frequencies[i] == OV4689_LINK_FREQ_500MHZ)
> +                       break;
> +
> +       if (i == bus_cfg.nr_of_link_frequencies) {
> +               dev_err(dev, "supported link freq %ull not found\n",
> +                       OV4689_LINK_FREQ_500MHZ);
> +               ret = -EINVAL;
> +               goto out_free_bus_cfg;
> +       }
> +
> +out_free_bus_cfg:
> +       v4l2_fwnode_endpoint_free(&bus_cfg);
> +
> +       return ret;
> +}
> +
> +static int ov4689_probe(struct i2c_client *client,
> +                       const struct i2c_device_id *id)
> +{
> +       struct device *dev = &client->dev;
> +       struct v4l2_subdev *sd;
> +       struct ov4689 *ov4689;
> +       int ret;
> +
> +       ret = ov4689_check_hwcfg(dev);
> +       if (ret)
> +               return ret;
> +
> +       ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
> +       if (!ov4689)
> +               return -ENOMEM;
> +
> +       ov4689->client = client;
> +       ov4689->cur_mode = &supported_modes[0];
> +
> +       ov4689->xvclk = devm_clk_get(dev, "xvclk");
> +       if (IS_ERR(ov4689->xvclk)) {
> +               dev_err(dev, "Failed to get xvclk\n");
> +               return -EINVAL;
> +       }
> +
> +       ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
> +       if (ret < 0) {
> +               dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
> +               return ret;
> +       }
> +       if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
> +               dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
> +
> +       ov4689->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +       if (IS_ERR(ov4689->reset_gpio)) {
> +               dev_err(dev, "Failed to get reset-gpios\n");
> +               return -EINVAL;
> +       }
> +
> +       ov4689->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
> +       if (IS_ERR(ov4689->pwdn_gpio)) {
> +               dev_err(dev, "Failed to get pwdn-gpios\n");
> +               return -EINVAL;
> +       }
> +
> +       ret = ov4689_configure_regulators(ov4689);
> +       if (ret) {
> +               dev_err(dev, "Failed to get power regulators\n");
> +               return ret;
> +       }
> +
> +       mutex_init(&ov4689->mutex);
> +
> +       sd = &ov4689->subdev;
> +       v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops);
> +       ret = ov4689_initialize_controls(ov4689);
> +       if (ret)
> +               goto err_destroy_mutex;
> +
> +       ret = __ov4689_power_on(ov4689);
> +       if (ret)
> +               goto err_free_handler;
> +
> +       ret = ov4689_check_sensor_id(ov4689, client);
> +       if (ret)
> +               goto err_power_off;
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +       sd->internal_ops = &ov4689_internal_ops;
> +       sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +#endif
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +       ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
> +       sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +       ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
> +       if (ret < 0)
> +               goto err_power_off;
> +#endif
> +
> +       ret = v4l2_async_register_subdev_sensor(sd);
> +       if (ret) {
> +               dev_err(dev, "v4l2 async register subdev failed\n");
> +               goto err_clean_entity;
> +       }
> +
> +       pm_runtime_set_active(dev);
> +       pm_runtime_enable(dev);
> +       pm_runtime_idle(dev);
> +
> +       return 0;
> +
> +err_clean_entity:
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +       media_entity_cleanup(&sd->entity);
> +#endif
> +err_power_off:
> +       __ov4689_power_off(ov4689);
> +err_free_handler:
> +       v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
> +err_destroy_mutex:
> +       mutex_destroy(&ov4689->mutex);
> +
> +       return ret;
> +}
> +
> +static int ov4689_remove(struct i2c_client *client)
> +{
> +       struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +       struct ov4689 *ov4689 = to_ov4689(sd);
> +
> +       v4l2_async_unregister_subdev(sd);
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +       media_entity_cleanup(&sd->entity);
> +#endif
> +       v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
> +       mutex_destroy(&ov4689->mutex);
> +
> +       pm_runtime_disable(&client->dev);
> +       if (!pm_runtime_status_suspended(&client->dev))
> +               __ov4689_power_off(ov4689);
> +       pm_runtime_set_suspended(&client->dev);
> +
> +       return 0;
> +}
> +
> +static const struct i2c_device_id ov4689_id[] = {
> +       { "ov4689", 0 },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(i2c, ov4689_id);
> +
> +static const struct of_device_id ov4689_of_match[] = {
> +       { .compatible = "ovti,ov4689" },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, ov4689_of_match);
> +
> +static struct i2c_driver ov4689_i2c_driver = {
> +       .driver = {
> +               .name = "ov4689",
> +               .pm = &ov4689_pm_ops,
> +               .of_match_table = of_match_ptr(ov4689_of_match),
> +       },
> +       .probe = ov4689_probe,
> +       .remove = ov4689_remove,
> +       .id_table = ov4689_id,
> +};
> +
> +module_i2c_driver(ov4689_i2c_driver);
> +
> +MODULE_DESCRIPTION("OmniVision ov4689 sensor driver");
> +MODULE_LICENSE("GPL");
> --
> 2.37.3
>

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-22 10:54   ` Dave Stevenson
@ 2022-09-22 14:56     ` Mikhail Rudenko
  0 siblings, 0 replies; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-22 14:56 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Sakari Ailus, Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Dave,

and thanks for reviewing this!

On 2022-09-22 at 11:54 +01, Dave Stevenson <dave.stevenson@raspberrypi.com> wrote:

> Hi Mikhail
>
> On Sun, 11 Sept 2022 at 21:02, Mikhail Rudenko <mike.rudenko@gmail.com> wrote:
>>
>> Add a V4L2 sub-device driver for OmniVision OV4689 image sensor. This
>> is a 4 Mpx image sensor using the I2C bus for control and the CSI-2
>> bus for data.
>>
>> This driver supports following features:
>> - manual exposure and analog gain control support
>> - test pattern support
>> - media controller support
>> - runtime PM support
>> - support following resolutions:
>>   + 2688x1520 at 30 fps
>>
>> The driver provides all mandatory V4L2 controls for compatibility with
>> libcamera. The sensor supports 1/2/4-lane CSI-2 modes, but the driver
>> implements 4 lane mode only at this moment.
>>
>> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
>> ---
>>  MAINTAINERS                |   1 +
>>  drivers/media/i2c/Kconfig  |  14 +
>>  drivers/media/i2c/Makefile |   1 +
>>  drivers/media/i2c/ov4689.c | 951 +++++++++++++++++++++++++++++++++++++
>>  4 files changed, 967 insertions(+)
>>  create mode 100644 drivers/media/i2c/ov4689.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 63c4844f26e6..1857f3864e1b 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -14529,6 +14529,7 @@ L:      linux-media@vger.kernel.org
>>  S:     Maintained
>>  T:     git git://linuxtv.org/media_tree.git
>>  F:     Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>> +F:     drivers/media/i2c/ov5647.c
>>
>>  OMNIVISION OV5640 SENSOR DRIVER
>>  M:     Steve Longerbeam <slongerbeam@gmail.com>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index fae2baabb773..4993e1ae2ea8 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -429,6 +429,20 @@ config VIDEO_OV2740
>>           To compile this driver as a module, choose M here: the
>>           module will be called ov2740.
>>
>> +config VIDEO_OV4689
>> +       tristate "OmniVision OV4689 sensor support"
>> +       depends on OF
>> +       depends on GPIOLIB && VIDEO_DEV && I2C
>> +       select MEDIA_CONTROLLER
>> +       select VIDEO_V4L2_SUBDEV_API
>> +       select V4L2_FWNODE
>> +       help
>> +         This is a Video4Linux2 sensor-level driver for the OmniVision
>> +         OV4689 camera.
>> +
>> +         To compile this driver as a module, choose M here: the
>> +         module will be called ov4689.
>> +
>>  config VIDEO_OV5640
>>         tristate "OmniVision OV5640 sensor support"
>>         depends on OF
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index 3e1696963e7f..7446c0a1eed0 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
>>  obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
>>  obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
>>  obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
>> +obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
>>  obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>>  obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
>>  obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
>> diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c
>> new file mode 100644
>> index 000000000000..9f05e812acf8
>> --- /dev/null
>> +++ b/drivers/media/i2c/ov4689.c
>> @@ -0,0 +1,951 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * ov4689 driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/device.h>
>> +#include <linux/delay.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/i2c.h>
>> +#include <linux/module.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-async.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-subdev.h>
>> +#include <media/v4l2-fwnode.h>
>> +
>> +#define CHIP_ID                                0x004688
>> +#define OV4689_REG_CHIP_ID             0x300a
>> +
>> +#define OV4689_XVCLK_FREQ              24000000
>> +
>> +#define OV4689_REG_CTRL_MODE           0x0100
>> +#define OV4689_MODE_SW_STANDBY         0x0
>> +#define OV4689_MODE_STREAMING          BIT(0)
>> +
>> +#define OV4689_REG_EXPOSURE            0x3500
>> +#define OV4689_EXPOSURE_MIN            4
>> +#define OV4689_EXPOSURE_STEP           1
>> +#define OV4689_VTS_MAX                 0x7fff
>> +
>> +#define OV4689_REG_GAIN_H              0x3508
>> +#define OV4689_REG_GAIN_L              0x3509
>> +#define OV4689_GAIN_H_MASK             0x07
>> +#define OV4689_GAIN_H_SHIFT            8
>> +#define OV4689_GAIN_L_MASK             0xff
>> +#define OV4689_GAIN_MIN                        0x10
>> +#define OV4689_GAIN_MAX                        0xf8
>> +#define OV4689_GAIN_STEP               1
>> +#define OV4689_GAIN_DEFAULT            0x10
>> +
>> +#define OV4689_REG_TEST_PATTERN                0x5040
>> +#define OV4689_TEST_PATTERN_ENABLE     0x80
>> +#define OV4689_TEST_PATTERN_DISABLE    0x0
>> +
>> +#define OV4689_REG_VTS                 0x380e
>> +
>> +#define REG_NULL                       0xFFFF
>> +
>> +#define OV4689_REG_VALUE_08BIT         1
>> +#define OV4689_REG_VALUE_16BIT         2
>> +#define OV4689_REG_VALUE_24BIT         3
>> +
>> +#define OV4689_LANES                   4
>> +#define OV4689_BITS_PER_SAMPLE         10
>> +
>> +static const char *const ov4689_supply_names[] = {
>> +       "avdd", /* Analog power */
>> +       "dovdd", /* Digital I/O power */
>> +       "dvdd", /* Digital core power */
>> +};
>> +
>> +#define OV4689_NUM_SUPPLIES ARRAY_SIZE(ov4689_supply_names)
>> +
>> +struct regval {
>> +       u16 addr;
>> +       u8 val;
>> +};
>> +
>> +struct ov4689_mode {
>> +       u32 width;
>> +       u32 height;
>> +       u32 max_fps;
>> +       u32 hts_def;
>> +       u32 vts_def;
>> +       u32 exp_def;
>> +       const struct regval *reg_list;
>> +};
>> +
>> +struct ov4689 {
>> +       struct i2c_client *client;
>> +       struct clk *xvclk;
>> +       struct gpio_desc *reset_gpio;
>> +       struct gpio_desc *pwdn_gpio;
>> +       struct regulator_bulk_data supplies[OV4689_NUM_SUPPLIES];
>> +
>> +       struct v4l2_subdev subdev;
>> +       struct media_pad pad;
>> +
>> +       struct mutex mutex; /* lock to protect streaming, ctrls and cur_mode */
>> +       bool streaming;
>> +       struct v4l2_ctrl_handler ctrl_handler;
>> +       struct v4l2_ctrl *exposure;
>> +       struct v4l2_ctrl *anal_gain;
>> +       struct v4l2_ctrl *digi_gain;
>> +       struct v4l2_ctrl *hblank;
>> +       struct v4l2_ctrl *vblank;
>> +       struct v4l2_ctrl *test_pattern;
>> +
>> +       const struct ov4689_mode *cur_mode;
>> +};
>> +
>> +#define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
>> +
>> +/*
>> + * Xclk 24Mhz
>> + */
>> +static const struct regval ov4689_global_regs[] = {
>> +       { REG_NULL, 0x00 },
>> +};
>> +
>> +/*
>> + * Xclk 24Mhz
>> + * max_framerate 30fps
>> + * mipi_datarate per lane 1008Mbps
>
> Data rate stated as 1008Mbps here....
>
>> + */
>> +static const struct regval ov4689_2688x1520_regs[] = {
>> +       {0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
>> +       {0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
>> +       {0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
>> +       {0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
>> +       {0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
>> +       {0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
>> +       {0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
>> +       {0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
>> +       {0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
>> +       {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
>> +       {0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
>> +       {0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
>> +       {0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
>> +       {0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
>> +       {0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
>> +       {0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
>> +       {0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
>> +       {0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
>> +       {0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
>> +       {0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
>> +       {0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
>> +       {0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
>> +       {0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
>> +       {0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
>> +       {0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
>> +       {0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
>> +       {0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
>> +       {0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
>> +       {0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
>> +       {0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
>> +       {0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
>> +       {0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
>> +       {0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
>> +       {0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
>> +       {0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
>> +       {0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
>> +       {0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
>> +       {0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
>> +       {0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
>> +       {0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
>> +       {0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
>> +       {0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
>> +       {0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
>> +       {0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
>> +       {0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
>> +       {0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
>> +       {0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
>> +       {0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
>> +       {0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
>> +       {0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
>> +       {0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
>> +       {0x380d, 0x80}, {0x380e, 0x06}, {0x380f, 0x12},
>> +       {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
>> +       {0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
>> +       {0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
>> +       {0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
>> +       {0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
>> +       {0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
>> +       {0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
>> +       {0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
>> +       {0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
>> +       {0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
>> +       {0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
>> +       {0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
>> +       {0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
>> +       {0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
>> +       {0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
>> +       {0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
>> +       {0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
>> +       {0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
>> +       {0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
>> +       {0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
>> +       {0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
>> +       {0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
>> +       {0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
>> +       {0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
>> +       {0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
>> +       {0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
>> +       {0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
>> +       {0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
>> +       {0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
>> +       {0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
>> +       {0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
>> +       {0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
>> +       {REG_NULL, 0x00},
>> +};
>> +
>> +static const struct ov4689_mode supported_modes[] = {
>> +       {
>> +               .width = 2688,
>> +               .height = 1520,
>> +               .max_fps = 30,
>> +               .exp_def = 0x0600,
>> +               .hts_def = 0x0a80,
>> +               .vts_def = 0x0612,
>> +               .reg_list = ov4689_2688x1520_regs,
>> +       },
>> +};
>> +
>> +#define OV4689_LINK_FREQ_500MHZ 500000000
>> +static const s64 link_freq_menu_items[] = { OV4689_LINK_FREQ_500MHZ };
>
> ... but a link frequency of 500MHz (ie 1000Mbit/s) here.
> Seeing as you compute the pixel rate based on the link frequency,
> that's going to mean that the pixel rate is incorrect.
> Link frequency should be 504MHz.
>
> Your PLL settings appear to match the 24MHz configuration in table
> 2-11 "sample PLL configuration" of the datasheet, so it would confirm
> that MIPI_SCLK is 1008MHz and MIPI_PCLK is 126MHz (at 1008/8 seems to
> be more byte clock than pixel (10bpp) clock).

Those are leftovers from the bsp driver, and they will be definitely
changed in v3. Recovering correct pixel clock, hblank and vblank
matching the observed fps needs some experimentation, which I hope to
carry out over the weekend.

>
>   Dave
>

--
Best regards,
Mikhail

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-22 10:43   ` Dave Stevenson
@ 2022-09-22 15:13     ` Mikhail Rudenko
  2022-09-26 10:47     ` Mikhail Rudenko
  1 sibling, 0 replies; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-22 15:13 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Jimmy Su, Arnd Bergmann, Arec Kao, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel


Hi Dave,

On 2022-09-22 at 11:43 +01, Dave Stevenson <dave.stevenson@raspberrypi.com> wrote:

> Hi Mikhail & Sakari
>
> On Thu, 22 Sept 2022 at 10:55, Sakari Ailus
> <sakari.ailus@linux.intel.com> wrote:
>>
>> Hi Mikhail,
>>
>> On Sun, Sep 11, 2022 at 11:01:33PM +0300, Mikhail Rudenko wrote:
>> > Hello,
>> >
>> > this series implements support for Omnivision OV4689 image
>> > sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
>> > megapixel image sensor. Ihis chip supports high frame rate speeds up
>> > to 90 fps at 2688x1520 resolution. It is programmable through an I2C
>> > interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
>> > connection.
>> >
>> > The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
>> > and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
>> > 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera module.
>> >
>> > While porting the driver, I stumbled upon two issues:
>> >
>> > (1) In the original driver, horizontal total size (HTS) was set to a
>> > value (2584) lower then the frame width (2688), resulting in negative
>> > hblank. In this driver, I increased HTS to 2688, but fps dropped from
>> > 29.88 to 28.73. What is the preferred way to handle this?
>>
>> If horizontal total size is less than the frame width, something is
>> certainly wrong there. You can't have negative horizontal blanking. Neither
>> it can be zero.
>
> Something certainly seems odd.
>
> To continue my thoughts from earlier in this patch set, Omnivision's
> Product Brief [1] states:
> The 1/3-inch OV4689 can capture full-resolution 4-megapixel high
> definition (HD) video at 90 frames per second (fps), 1080p HD at 120
> fps, and binned 720p HD at 180 fps
>
> The datasheet section 2.1 states:
> The OV4689 color image sensor is a high performance, 4 megapixel RAW
> image sensor that delivers 2688x1520 at 90 fps using OmniBSI-2™ pixel
> technology.
>
> So 4MP 90fps or 1080p120 should be achievable somehow.
>
> 2688x1520 @ 90fps is 367.7MPix/s, and that tallies quite nicely with
> table 2-9 listing the DAC PLL speed limitation of 360-378MHz. Exactly
> how that is then converted into PCLK or SCLK is unclear.
> Ideally you'd be able to contact an Omnivision FAE to confirm, but
> that means you need to be buying modules directly from them or
> otherwise have a business relationship.
> I do note that there is an NVidia Tegra driver for OV4689 at [2]. I
> wonder if analysis of that would reveal anything.
>
> I have just been looking at the ov9282 driver and the timings don't
> tally there either - configure it for 60fps and you get 30fps. The
> TIMING_HTS register appears to be in units of 2 pixels. The default is
> 0x2d8 (728 decimal) on a 1280x720 mode, but consider it as units of 2
> pixels and HTS of 1456 (1280 active and hblank of 176) does match up.
> It works in the general case too.
>
> Looking at the OV4689 datasheet again, the default for TIMING_HTS
> (0x380c/d) is 0x5f8 (1528 decimal) when HOUTPUT_SIZE (0x3808/9) is
> 0x1200 (4608 decimal). Whilst there are no guarantees that default
> register settings will stream in any sensible form, it does imply
> TIMING_HTS is not in units of 1 pixel, and potentially 4 pixels.
> From the Rockchip BSP driver it plainly does stream at 30 (or 29.88)
> fps with TIMING_HTS < HOUTPUT_SIZE, so a quick test of reducing it
> further would be worthwhile. What does the default of 0x2d8 give you
> as a frame rate, and are the images correct?

Thanks for sharing this! Actually, I'm going to do some experimentation
with these registers next weekend, and post the result here.

> Just some thoughts.
>   Dave

--
Best regards,
Mikhail Rudenko

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-22  9:53   ` Sakari Ailus
@ 2022-09-22 15:23     ` Mikhail Rudenko
  2022-09-22 20:39       ` Sakari Ailus
  0 siblings, 1 reply; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-22 15:23 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel


Hi Sakari,

and thanks for reviewing this!

Please see my comments below:

On 2022-09-22 at 09:53 GMT, Sakari Ailus <sakari.ailus@linux.intel.com> wrote:

> Hi Mikhail,
>
> On Sun, Sep 11, 2022 at 11:01:35PM +0300, Mikhail Rudenko wrote:
>> Add a V4L2 sub-device driver for OmniVision OV4689 image sensor. This
>> is a 4 Mpx image sensor using the I2C bus for control and the CSI-2
>> bus for data.
>>
>> This driver supports following features:
>> - manual exposure and analog gain control support
>> - test pattern support
>> - media controller support
>> - runtime PM support
>> - support following resolutions:
>>   + 2688x1520 at 30 fps
>>
>> The driver provides all mandatory V4L2 controls for compatibility with
>> libcamera. The sensor supports 1/2/4-lane CSI-2 modes, but the driver
>> implements 4 lane mode only at this moment.
>>
>> Signed-off-by: Mikhail Rudenko <mike.rudenko@gmail.com>
>> ---
>>  MAINTAINERS                |   1 +
>>  drivers/media/i2c/Kconfig  |  14 +
>>  drivers/media/i2c/Makefile |   1 +
>>  drivers/media/i2c/ov4689.c | 951 +++++++++++++++++++++++++++++++++++++
>>  4 files changed, 967 insertions(+)
>>  create mode 100644 drivers/media/i2c/ov4689.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 63c4844f26e6..1857f3864e1b 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -14529,6 +14529,7 @@ L:	linux-media@vger.kernel.org
>>  S:	Maintained
>>  T:	git git://linuxtv.org/media_tree.git
>>  F:	Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
>> +F:	drivers/media/i2c/ov5647.c
>>
>>  OMNIVISION OV5640 SENSOR DRIVER
>>  M:	Steve Longerbeam <slongerbeam@gmail.com>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index fae2baabb773..4993e1ae2ea8 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -429,6 +429,20 @@ config VIDEO_OV2740
>>  	  To compile this driver as a module, choose M here: the
>>  	  module will be called ov2740.
>>
>> +config VIDEO_OV4689
>> +	tristate "OmniVision OV4689 sensor support"
>> +	depends on OF
>> +	depends on GPIOLIB && VIDEO_DEV && I2C
>> +	select MEDIA_CONTROLLER
>> +	select VIDEO_V4L2_SUBDEV_API
>> +	select V4L2_FWNODE
>> +	help
>> +	  This is a Video4Linux2 sensor-level driver for the OmniVision
>> +	  OV4689 camera.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called ov4689.
>> +
>>  config VIDEO_OV5640
>>  	tristate "OmniVision OV5640 sensor support"
>>  	depends on OF
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index 3e1696963e7f..7446c0a1eed0 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -78,6 +78,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
>>  obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
>>  obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
>>  obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
>> +obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
>>  obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>>  obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
>>  obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
>> diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c
>> new file mode 100644
>> index 000000000000..9f05e812acf8
>> --- /dev/null
>> +++ b/drivers/media/i2c/ov4689.c
>> @@ -0,0 +1,951 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * ov4689 driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/device.h>
>> +#include <linux/delay.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/i2c.h>
>> +#include <linux/module.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-async.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-subdev.h>
>> +#include <media/v4l2-fwnode.h>
>> +
>> +#define CHIP_ID				0x004688
>> +#define OV4689_REG_CHIP_ID		0x300a
>> +
>> +#define OV4689_XVCLK_FREQ		24000000
>> +
>> +#define OV4689_REG_CTRL_MODE		0x0100
>> +#define OV4689_MODE_SW_STANDBY		0x0
>> +#define OV4689_MODE_STREAMING		BIT(0)
>> +
>> +#define OV4689_REG_EXPOSURE		0x3500
>> +#define OV4689_EXPOSURE_MIN		4
>> +#define OV4689_EXPOSURE_STEP		1
>> +#define OV4689_VTS_MAX			0x7fff
>> +
>> +#define OV4689_REG_GAIN_H		0x3508
>> +#define OV4689_REG_GAIN_L		0x3509
>> +#define OV4689_GAIN_H_MASK		0x07
>> +#define OV4689_GAIN_H_SHIFT		8
>> +#define OV4689_GAIN_L_MASK		0xff
>> +#define OV4689_GAIN_MIN			0x10
>> +#define OV4689_GAIN_MAX			0xf8
>> +#define OV4689_GAIN_STEP		1
>> +#define OV4689_GAIN_DEFAULT		0x10
>> +
>> +#define OV4689_REG_TEST_PATTERN		0x5040
>> +#define OV4689_TEST_PATTERN_ENABLE	0x80
>> +#define OV4689_TEST_PATTERN_DISABLE	0x0
>> +
>> +#define OV4689_REG_VTS			0x380e
>> +
>> +#define REG_NULL			0xFFFF
>> +
>> +#define OV4689_REG_VALUE_08BIT		1
>> +#define OV4689_REG_VALUE_16BIT		2
>> +#define OV4689_REG_VALUE_24BIT		3
>> +
>> +#define OV4689_LANES			4
>> +#define OV4689_BITS_PER_SAMPLE		10
>> +
>> +static const char *const ov4689_supply_names[] = {
>> +	"avdd", /* Analog power */
>> +	"dovdd", /* Digital I/O power */
>> +	"dvdd", /* Digital core power */
>> +};
>> +
>> +#define OV4689_NUM_SUPPLIES ARRAY_SIZE(ov4689_supply_names)
>
> I think it'd be cleaner to use ARRAY_SIZE(ov4689_supply_names) instead.
>

Ack, will fix in v3.


>> +
>> +struct regval {
>> +	u16 addr;
>> +	u8 val;
>> +};
>> +
>> +struct ov4689_mode {
>> +	u32 width;
>> +	u32 height;
>> +	u32 max_fps;
>> +	u32 hts_def;
>> +	u32 vts_def;
>> +	u32 exp_def;
>> +	const struct regval *reg_list;
>> +};
>> +
>> +struct ov4689 {
>> +	struct i2c_client *client;
>> +	struct clk *xvclk;
>> +	struct gpio_desc *reset_gpio;
>> +	struct gpio_desc *pwdn_gpio;
>> +	struct regulator_bulk_data supplies[OV4689_NUM_SUPPLIES];
>> +
>> +	struct v4l2_subdev subdev;
>> +	struct media_pad pad;
>> +
>> +	struct mutex mutex; /* lock to protect streaming, ctrls and cur_mode */
>> +	bool streaming;
>> +	struct v4l2_ctrl_handler ctrl_handler;
>> +	struct v4l2_ctrl *exposure;
>> +	struct v4l2_ctrl *anal_gain;
>> +	struct v4l2_ctrl *digi_gain;
>> +	struct v4l2_ctrl *hblank;
>> +	struct v4l2_ctrl *vblank;
>> +	struct v4l2_ctrl *test_pattern;
>
> Only keep the controls you need elsewhere.
>

Ack, will remove the unused controls.

>> +
>> +	const struct ov4689_mode *cur_mode;
>> +};
>> +
>> +#define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
>> +
>> +/*
>> + * Xclk 24Mhz
>> + */
>> +static const struct regval ov4689_global_regs[] = {
>> +	{ REG_NULL, 0x00 },
>> +};
>> +
>> +/*
>> + * Xclk 24Mhz
>> + * max_framerate 30fps
>> + * mipi_datarate per lane 1008Mbps
>> + */
>> +static const struct regval ov4689_2688x1520_regs[] = {
>> +	{0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
>> +	{0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
>> +	{0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
>> +	{0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
>> +	{0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
>> +	{0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
>> +	{0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
>> +	{0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
>> +	{0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
>> +	{0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
>> +	{0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
>> +	{0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
>> +	{0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
>> +	{0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
>> +	{0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
>> +	{0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
>> +	{0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
>> +	{0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
>> +	{0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
>> +	{0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
>> +	{0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
>> +	{0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
>> +	{0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
>> +	{0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
>> +	{0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
>> +	{0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
>> +	{0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
>> +	{0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
>> +	{0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
>> +	{0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
>> +	{0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
>> +	{0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
>> +	{0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
>> +	{0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
>> +	{0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
>> +	{0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
>> +	{0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
>> +	{0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
>> +	{0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
>> +	{0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
>> +	{0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
>> +	{0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
>> +	{0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
>> +	{0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
>> +	{0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
>> +	{0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
>> +	{0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
>> +	{0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
>> +	{0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
>> +	{0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
>> +	{0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
>> +	{0x380d, 0x80}, {0x380e, 0x06}, {0x380f, 0x12},
>> +	{0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
>> +	{0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
>> +	{0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
>> +	{0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
>> +	{0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
>> +	{0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
>> +	{0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
>> +	{0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
>> +	{0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
>> +	{0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
>> +	{0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
>> +	{0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
>> +	{0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
>> +	{0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
>> +	{0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
>> +	{0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
>> +	{0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
>> +	{0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
>> +	{0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
>> +	{0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
>> +	{0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
>> +	{0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
>> +	{0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
>> +	{0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
>> +	{0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
>> +	{0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
>> +	{0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
>> +	{0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
>> +	{0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
>> +	{0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
>> +	{0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
>> +	{0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
>> +	{REG_NULL, 0x00},
>> +};
>> +
>> +static const struct ov4689_mode supported_modes[] = {
>> +	{
>> +		.width = 2688,
>> +		.height = 1520,
>> +		.max_fps = 30,
>> +		.exp_def = 0x0600,
>> +		.hts_def = 0x0a80,
>> +		.vts_def = 0x0612,
>> +		.reg_list = ov4689_2688x1520_regs,
>> +	},
>> +};
>> +
>> +#define OV4689_LINK_FREQ_500MHZ 500000000
>
> Please use the plain number --- see also comments in probe.
>

Ack.

>> +static const s64 link_freq_menu_items[] = { OV4689_LINK_FREQ_500MHZ };
>> +
>> +static const char *const ov4689_test_pattern_menu[] = {
>> +	"Disabled",
>> +	"Vertical Color Bar Type 1",
>> +	"Vertical Color Bar Type 2",
>> +	"Vertical Color Bar Type 3",
>> +	"Vertical Color Bar Type 4"
>> +};
>> +
>> +/* Write registers up to 4 at a time */
>> +static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len,
>> +			    u32 val)
>> +{
>> +	u32 buf_i, val_i;
>> +	__be32 val_be;
>> +	u8 *val_p;
>> +	u8 buf[6];
>> +
>> +	if (len > 4)
>> +		return -EINVAL;
>> +
>> +	buf[0] = reg >> 8;
>> +	buf[1] = reg & 0xff;
>> +
>> +	val_be = cpu_to_be32(val);
>> +	val_p = (u8 *)&val_be;
>> +	buf_i = 2;
>> +	val_i = 4 - len;
>> +
>> +	while (val_i < 4)
>> +		buf[buf_i++] = val_p[val_i++];
>> +
>> +	if (i2c_master_send(client, buf, len + 2) != len + 2)
>> +		return -EIO;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_write_array(struct i2c_client *client,
>> +			      const struct regval *regs)
>> +{
>> +	int ret = 0;
>> +	u32 i;
>> +
>> +	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
>> +		ret = ov4689_write_reg(client, regs[i].addr,
>> +				       OV4689_REG_VALUE_08BIT, regs[i].val);
>> +
>> +	return ret;
>> +}
>> +
>> +/* Read registers up to 4 at a time */
>> +static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
>> +			   u32 *val)
>> +{
>> +	__be16 reg_addr_be = cpu_to_be16(reg);
>> +	struct i2c_msg msgs[2];
>> +	__be32 data_be = 0;
>> +	u8 *data_be_p;
>> +	int ret;
>> +
>> +	if (len > 4 || !len)
>> +		return -EINVAL;
>> +
>> +	data_be_p = (u8 *)&data_be;
>> +	/* Write register address */
>> +	msgs[0].addr = client->addr;
>> +	msgs[0].flags = 0;
>> +	msgs[0].len = 2;
>> +	msgs[0].buf = (u8 *)&reg_addr_be;
>> +
>> +	/* Read data from register */
>> +	msgs[1].addr = client->addr;
>> +	msgs[1].flags = I2C_M_RD;
>> +	msgs[1].len = len;
>> +	msgs[1].buf = &data_be_p[4 - len];
>> +
>> +	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
>> +	if (ret != ARRAY_SIZE(msgs))
>> +		return -EIO;
>> +
>> +	*val = be32_to_cpu(data_be);
>> +
>> +	return 0;
>> +}
>> +
>> +static void ov4689_fill_fmt(const struct ov4689_mode *mode,
>> +			    struct v4l2_mbus_framefmt *fmt)
>> +{
>> +	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +	fmt->width = mode->width;
>> +	fmt->height = mode->height;
>> +	fmt->field = V4L2_FIELD_NONE;
>> +}
>> +
>> +static int ov4689_set_fmt(struct v4l2_subdev *sd,
>> +			  struct v4l2_subdev_state *sd_state,
>> +			  struct v4l2_subdev_format *fmt)
>> +{
>> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +
>> +	/* only one mode supported for now */
>> +	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_get_fmt(struct v4l2_subdev *sd,
>> +			  struct v4l2_subdev_state *sd_state,
>> +			  struct v4l2_subdev_format *fmt)
>> +{
>> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +
>> +	/* only one mode supported for now */
>> +	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
>> +				 struct v4l2_subdev_state *sd_state,
>> +				 struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +	if (code->index != 0)
>> +		return -EINVAL;
>> +	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_enum_frame_sizes(struct v4l2_subdev *sd,
>> +				   struct v4l2_subdev_state *sd_state,
>> +				   struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> +	if (fse->index >= ARRAY_SIZE(supported_modes))
>> +		return -EINVAL;
>> +
>> +	if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
>> +		return -EINVAL;
>> +
>> +	fse->min_width = supported_modes[fse->index].width;
>> +	fse->max_width = supported_modes[fse->index].width;
>> +	fse->max_height = supported_modes[fse->index].height;
>> +	fse->min_height = supported_modes[fse->index].height;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
>> +{
>> +	u32 val;
>> +
>> +	if (pattern)
>> +		val = (pattern - 1) | OV4689_TEST_PATTERN_ENABLE;
>> +	else
>> +		val = OV4689_TEST_PATTERN_DISABLE;
>> +
>> +	return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN,
>> +				OV4689_REG_VALUE_08BIT, val);
>> +}
>> +
>> +static int ov4689_get_selection(struct v4l2_subdev *sd,
>> +				struct v4l2_subdev_state *state,
>> +				struct v4l2_subdev_selection *sel)
>> +{
>> +	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
>> +		return -EINVAL;
>> +
>> +	switch (sel->target) {
>> +	case V4L2_SEL_TGT_CROP_BOUNDS:
>> +		sel->r.top = 0;
>> +		sel->r.left = 0;
>> +		sel->r.width = 2720;
>> +		sel->r.height = 1536;
>> +		return 0;
>> +	case V4L2_SEL_TGT_CROP:
>> +	case V4L2_SEL_TGT_CROP_DEFAULT:
>> +		sel->r.top = 8;
>> +		sel->r.left = 16;
>> +		sel->r.width = 2688;
>> +		sel->r.height = 1520;
>> +		return 0;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
>> +{
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +	struct i2c_client *client = ov4689->client;
>> +	int ret = 0;
>> +
>> +	mutex_lock(&ov4689->mutex);
>> +
>> +	on = !!on;
>> +	if (on == ov4689->streaming)
>> +		goto unlock_and_return;
>> +
>> +	if (on) {
>> +		ret = pm_runtime_resume_and_get(&client->dev);
>> +		if (ret < 0)
>> +			goto unlock_and_return;
>> +
>> +		ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
>> +		if (ret) {
>> +			pm_runtime_put(&client->dev);
>> +			goto unlock_and_return;
>> +		}
>> +
>> +		ret = ov4689_write_array(ov4689->client,
>> +					 ov4689->cur_mode->reg_list);
>> +		if (ret) {
>> +			pm_runtime_put(&client->dev);
>> +			goto unlock_and_return;
>> +		}
>> +
>> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
>> +				       OV4689_REG_VALUE_08BIT,
>> +				       OV4689_MODE_STREAMING);
>> +		if (ret) {
>> +			pm_runtime_put(&client->dev);
>> +			goto unlock_and_return;
>> +		}
>> +	} else {
>> +		ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
>> +				 OV4689_REG_VALUE_08BIT,
>> +				 OV4689_MODE_SW_STANDBY);
>> +		pm_runtime_put(&client->dev);
>> +	}
>> +
>> +	ov4689->streaming = on;
>> +
>> +unlock_and_return:
>> +	mutex_unlock(&ov4689->mutex);
>> +
>> +	return ret;
>> +}
>> +
>> +/* Calculate the delay in us by clock rate and clock cycles */
>> +static inline u32 ov4689_cal_delay(u32 cycles)
>> +{
>> +	return DIV_ROUND_UP(cycles, OV4689_XVCLK_FREQ / 1000 / 1000);
>
> Please use the actual rate instead.
>

Do you mean clk_get_rate(ov4689->xvclk), right? What if we have an ACPI
system and xvclk is NULL here? Please explain.

>> +}
>> +
>> +static int __ov4689_power_on(struct ov4689 *ov4689)
>> +{
>> +	struct device *dev = &ov4689->client->dev;
>> +	u32 delay_us;
>> +	int ret;
>> +
>> +	ret = clk_prepare_enable(ov4689->xvclk);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable xvclk\n");
>> +		return ret;
>> +	}
>> +
>> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
>> +
>> +	ret = regulator_bulk_enable(OV4689_NUM_SUPPLIES, ov4689->supplies);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable regulators\n");
>> +		goto disable_clk;
>> +	}
>> +
>> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
>> +	usleep_range(500, 1000);
>> +	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 0);
>> +
>> +	/* 8192 cycles prior to first SCCB transaction */
>> +	delay_us = ov4689_cal_delay(8192);
>> +	usleep_range(delay_us, delay_us * 2);
>> +
>> +	return 0;
>> +
>> +disable_clk:
>> +	clk_disable_unprepare(ov4689->xvclk);
>> +
>> +	return ret;
>> +}
>> +
>> +static void __ov4689_power_off(struct ov4689 *ov4689)
>> +{
>> +	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 1);
>> +	clk_disable_unprepare(ov4689->xvclk);
>> +	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
>> +	regulator_bulk_disable(OV4689_NUM_SUPPLIES, ov4689->supplies);
>> +}
>
> Please merge these two and the wrappers below.
>

Ack.

>> +
>> +static int __maybe_unused ov4689_runtime_resume(struct device *dev)
>> +{
>> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +
>> +	return __ov4689_power_on(ov4689);
>> +}
>> +
>> +static int __maybe_unused ov4689_runtime_suspend(struct device *dev)
>> +{
>> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +
>> +	__ov4689_power_off(ov4689);
>> +
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +	struct v4l2_mbus_framefmt *try_fmt;
>> +
>> +	mutex_lock(&ov4689->mutex);
>> +
>> +	try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0);
>> +	/* Initialize try_fmt */
>> +	ov4689_fill_fmt(&supported_modes[0], try_fmt);
>> +
>> +	mutex_unlock(&ov4689->mutex);
>> +
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static const struct dev_pm_ops ov4689_pm_ops = {
>> +	SET_RUNTIME_PM_OPS(ov4689_runtime_suspend, ov4689_runtime_resume, NULL)
>> +};
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
>> +	.open = ov4689_open,
>> +};
>> +#endif
>> +
>> +static const struct v4l2_subdev_video_ops ov4689_video_ops = {
>> +	.s_stream = ov4689_s_stream,
>> +};
>> +
>> +static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
>> +	.enum_mbus_code = ov4689_enum_mbus_code,
>> +	.enum_frame_size = ov4689_enum_frame_sizes,
>> +	.get_fmt = ov4689_get_fmt,
>> +	.set_fmt = ov4689_set_fmt,
>> +	.get_selection = ov4689_get_selection,
>> +};
>> +
>> +static const struct v4l2_subdev_ops ov4689_subdev_ops = {
>> +	.video = &ov4689_video_ops,
>> +	.pad = &ov4689_pad_ops,
>> +};
>> +
>> +static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct ov4689 *ov4689 =
>> +		container_of(ctrl->handler, struct ov4689, ctrl_handler);
>> +	struct i2c_client *client = ov4689->client;
>> +	s64 max_expo;
>> +	int ret;
>> +
>> +	/* Propagate change of current control to all related controls */
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_VBLANK:
>> +		/* Update max exposure while meeting expected vblanking */
>> +		max_expo = ov4689->cur_mode->height + ctrl->val - 4;
>> +		__v4l2_ctrl_modify_range(ov4689->exposure,
>> +					 ov4689->exposure->minimum, max_expo,
>> +					 ov4689->exposure->step,
>> +					 ov4689->exposure->default_value);
>> +		break;
>> +	}
>> +
>> +	if (!pm_runtime_get_if_in_use(&client->dev))
>> +		return 0;
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_EXPOSURE:
>> +		/* 4 least significant bits of expsoure are fractional part */
>> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE,
>> +				       OV4689_REG_VALUE_24BIT, ctrl->val << 4);
>> +		break;
>> +	case V4L2_CID_ANALOGUE_GAIN:
>> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H,
>> +				       OV4689_REG_VALUE_08BIT,
>> +				       (ctrl->val >> OV4689_GAIN_H_SHIFT) &
>> +					       OV4689_GAIN_H_MASK);
>> +		ret |= ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L,
>
> ret = ret ?: ...;
>

Ack

>> +					OV4689_REG_VALUE_08BIT,
>> +					ctrl->val & OV4689_GAIN_L_MASK);
>> +		break;
>> +	case V4L2_CID_VBLANK:
>> +		ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS,
>> +				       OV4689_REG_VALUE_16BIT,
>> +				       ctrl->val + ov4689->cur_mode->height);
>> +		break;
>> +	case V4L2_CID_TEST_PATTERN:
>> +		ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
>> +		break;
>> +	default:
>> +		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
>> +			 __func__, ctrl->id, ctrl->val);
>> +		ret = -EINVAL;
>> +		break;
>> +	}
>> +
>> +	pm_runtime_put(&client->dev);
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
>> +	.s_ctrl = ov4689_set_ctrl,
>> +};
>> +
>> +static int ov4689_initialize_controls(struct ov4689 *ov4689)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(&ov4689->subdev);
>> +	struct v4l2_fwnode_device_properties props;
>> +	struct v4l2_ctrl_handler *handler;
>> +	const struct ov4689_mode *mode;
>> +	s64 exposure_max, vblank_def;
>> +	struct v4l2_ctrl *ctrl;
>> +	u32 h_blank, pixel_rate;
>> +	int ret;
>> +
>> +	handler = &ov4689->ctrl_handler;
>> +	mode = ov4689->cur_mode;
>> +	ret = v4l2_ctrl_handler_init(handler, 10);
>> +	if (ret)
>> +		return ret;
>> +	handler->lock = &ov4689->mutex;
>> +
>> +	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
>> +				      link_freq_menu_items);
>> +	if (ctrl)
>> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> +	pixel_rate = (link_freq_menu_items[0] * 2 * OV4689_LANES) /
>> +		     OV4689_BITS_PER_SAMPLE;
>> +	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, pixel_rate, 1,
>> +			  pixel_rate);
>> +
>> +	h_blank = mode->hts_def - mode->width;
>> +	ov4689->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
>> +					   h_blank, h_blank, 1, h_blank);
>> +	if (ov4689->hblank)
>> +		ov4689->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> +	vblank_def = mode->vts_def - mode->height;
>> +	ov4689->vblank =
>> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
>> +				  vblank_def, OV4689_VTS_MAX - mode->height, 1,
>> +				  vblank_def);
>> +
>> +	exposure_max = mode->vts_def - 4;
>> +	ov4689->exposure =
>> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_EXPOSURE,
>> +				  OV4689_EXPOSURE_MIN, exposure_max,
>> +				  OV4689_EXPOSURE_STEP, mode->exp_def);
>> +
>> +	ov4689->anal_gain =
>> +		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops,
>> +				  V4L2_CID_ANALOGUE_GAIN, OV4689_GAIN_MIN,
>> +				  OV4689_GAIN_MAX, OV4689_GAIN_STEP,
>> +				  OV4689_GAIN_DEFAULT);
>> +
>> +	ov4689->test_pattern =
>> +		v4l2_ctrl_new_std_menu_items(handler, &ov4689_ctrl_ops,
>> +					     V4L2_CID_TEST_PATTERN,
>> +					     ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
>> +					     0, 0, ov4689_test_pattern_menu);
>> +
>> +	if (handler->error) {
>> +		ret = handler->error;
>> +		dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n",
>> +			ret);
>> +		goto err_free_handler;
>> +	}
>> +
>> +	ret = v4l2_fwnode_device_parse(&client->dev, &props);
>> +	if (ret)
>> +		goto err_free_handler;
>> +
>> +	ret = v4l2_ctrl_new_fwnode_properties(handler, &ov4689_ctrl_ops,
>> +					      &props);
>> +	if (ret)
>> +		goto err_free_handler;
>> +
>> +	ov4689->subdev.ctrl_handler = handler;
>> +
>> +	return 0;
>> +
>> +err_free_handler:
>> +	v4l2_ctrl_handler_free(handler);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov4689_check_sensor_id(struct ov4689 *ov4689,
>> +				  struct i2c_client *client)
>> +{
>> +	struct device *dev = &ov4689->client->dev;
>> +	u32 id = 0;
>> +	int ret;
>> +
>> +	ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID,
>> +			      OV4689_REG_VALUE_16BIT, &id);
>> +	if (id != CHIP_ID) {
>> +		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
>> +		return -ENODEV;
>> +	}
>> +
>> +	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov4689_configure_regulators(struct ov4689 *ov4689)
>> +{
>> +	unsigned int i;
>> +
>> +	for (i = 0; i < OV4689_NUM_SUPPLIES; i++)
>> +		ov4689->supplies[i].supply = ov4689_supply_names[i];
>> +
>> +	return devm_regulator_bulk_get(&ov4689->client->dev,
>> +				       OV4689_NUM_SUPPLIES, ov4689->supplies);
>> +}
>> +
>> +static int ov4689_check_hwcfg(struct device *dev)
>> +{
>> +	struct fwnode_handle *fwnode = dev_fwnode(dev);
>> +	struct v4l2_fwnode_endpoint bus_cfg = {
>> +		.bus_type = V4L2_MBUS_CSI2_DPHY,
>> +	};
>> +	struct fwnode_handle *endpoint;
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
>> +	if (!endpoint)
>> +		return -EPROBE_DEFER;
>> +
>> +	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
>> +	fwnode_handle_put(endpoint);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
>> +		dev_err(dev, "only a 4-lane CSI2 config is supported");
>> +		ret = -EINVAL;
>> +		goto out_free_bus_cfg;
>> +	}
>> +
>> +	if (!bus_cfg.nr_of_link_frequencies) {
>> +		dev_err(dev, "no link frequencies defined\n");
>> +		ret = -EINVAL;
>> +		goto out_free_bus_cfg;
>> +	}
>> +
>> +	for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
>> +		if (bus_cfg.link_frequencies[i] == OV4689_LINK_FREQ_500MHZ)
>
> Please instead compare with array entries.
>

Ack

>> +			break;
>> +
>> +	if (i == bus_cfg.nr_of_link_frequencies) {
>> +		dev_err(dev, "supported link freq %ull not found\n",
>> +			OV4689_LINK_FREQ_500MHZ);
>> +		ret = -EINVAL;
>> +		goto out_free_bus_cfg;
>> +	}
>> +
>> +out_free_bus_cfg:
>> +	v4l2_fwnode_endpoint_free(&bus_cfg);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov4689_probe(struct i2c_client *client,
>> +			const struct i2c_device_id *id)
>> +{
>> +	struct device *dev = &client->dev;
>> +	struct v4l2_subdev *sd;
>> +	struct ov4689 *ov4689;
>> +	int ret;
>> +
>> +	ret = ov4689_check_hwcfg(dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
>> +	if (!ov4689)
>> +		return -ENOMEM;
>> +
>> +	ov4689->client = client;
>> +	ov4689->cur_mode = &supported_modes[0];
>> +
>> +	ov4689->xvclk = devm_clk_get(dev, "xvclk");
>> +	if (IS_ERR(ov4689->xvclk)) {
>> +		dev_err(dev, "Failed to get xvclk\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
>
> Please see handling clocks in:
>
> <URL:https://hverkuil.home.xs4all.nl/spec/driver-api/camera-sensor.html>
>

Ack.

>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
>> +		return ret;
>> +	}
>> +	if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
>> +		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
>> +
>> +	ov4689->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +	if (IS_ERR(ov4689->reset_gpio)) {
>> +		dev_err(dev, "Failed to get reset-gpios\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ov4689->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
>> +	if (IS_ERR(ov4689->pwdn_gpio)) {
>> +		dev_err(dev, "Failed to get pwdn-gpios\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = ov4689_configure_regulators(ov4689);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to get power regulators\n");
>> +		return ret;
>> +	}
>> +
>> +	mutex_init(&ov4689->mutex);
>> +
>> +	sd = &ov4689->subdev;
>> +	v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops);
>> +	ret = ov4689_initialize_controls(ov4689);
>> +	if (ret)
>> +		goto err_destroy_mutex;
>> +
>> +	ret = __ov4689_power_on(ov4689);
>> +	if (ret)
>> +		goto err_free_handler;
>> +
>> +	ret = ov4689_check_sensor_id(ov4689, client);
>> +	if (ret)
>> +		goto err_power_off;
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +	sd->internal_ops = &ov4689_internal_ops;
>> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +#endif
>> +#if defined(CONFIG_MEDIA_CONTROLLER)
>> +	ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
>> +	ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
>> +	if (ret < 0)
>> +		goto err_power_off;
>> +#endif
>> +
>> +	ret = v4l2_async_register_subdev_sensor(sd);
>> +	if (ret) {
>> +		dev_err(dev, "v4l2 async register subdev failed\n");
>> +		goto err_clean_entity;
>> +	}
>> +
>> +	pm_runtime_set_active(dev);
>> +	pm_runtime_enable(dev);
>> +	pm_runtime_idle(dev);
>> +
>> +	return 0;
>> +
>> +err_clean_entity:
>> +#if defined(CONFIG_MEDIA_CONTROLLER)
>> +	media_entity_cleanup(&sd->entity);
>> +#endif
>> +err_power_off:
>> +	__ov4689_power_off(ov4689);
>> +err_free_handler:
>> +	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
>> +err_destroy_mutex:
>> +	mutex_destroy(&ov4689->mutex);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov4689_remove(struct i2c_client *client)
>> +{
>> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
>> +	struct ov4689 *ov4689 = to_ov4689(sd);
>> +
>> +	v4l2_async_unregister_subdev(sd);
>> +#if defined(CONFIG_MEDIA_CONTROLLER)
>
> No need for #if here, please drop.
>

Ack

>> +	media_entity_cleanup(&sd->entity);
>> +#endif
>> +	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
>> +	mutex_destroy(&ov4689->mutex);
>> +
>> +	pm_runtime_disable(&client->dev);
>> +	if (!pm_runtime_status_suspended(&client->dev))
>> +		__ov4689_power_off(ov4689);
>> +	pm_runtime_set_suspended(&client->dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct i2c_device_id ov4689_id[] = {
>> +	{ "ov4689", 0 },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(i2c, ov4689_id);
>> +
>> +static const struct of_device_id ov4689_of_match[] = {
>> +	{ .compatible = "ovti,ov4689" },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, ov4689_of_match);
>> +
>> +static struct i2c_driver ov4689_i2c_driver = {
>> +	.driver = {
>> +		.name = "ov4689",
>> +		.pm = &ov4689_pm_ops,
>> +		.of_match_table = of_match_ptr(ov4689_of_match),
>> +	},
>> +	.probe = ov4689_probe,
>> +	.remove	= ov4689_remove,
>> +	.id_table = ov4689_id,
>> +};
>> +
>> +module_i2c_driver(ov4689_i2c_driver);
>> +
>> +MODULE_DESCRIPTION("OmniVision ov4689 sensor driver");
>> +MODULE_LICENSE("GPL");
>> --
>> 2.37.3
>>


--
Best regards,
Mikhail Rudenko

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

* Re: [PATCH v2 2/2] media: i2c: add support for ov4689
  2022-09-22 15:23     ` Mikhail Rudenko
@ 2022-09-22 20:39       ` Sakari Ailus
  0 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2022-09-22 20:39 UTC (permalink / raw)
  To: Mikhail Rudenko
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Hans Verkuil, Jacopo Mondi, Shawn Tu, Randy Dunlap,
	Daniel Scally, Christian Hemp, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel

Hi Mikhail,

On Thu, Sep 22, 2022 at 06:23:05PM +0300, Mikhail Rudenko wrote:
> 
> Hi Sakari,
> 
> and thanks for reviewing this!

You're welcome!

> 
> Please see my comments below:
> 
> On 2022-09-22 at 09:53 GMT, Sakari Ailus <sakari.ailus@linux.intel.com> wrote:
...
> >> +static inline u32 ov4689_cal_delay(u32 cycles)
> >> +{
> >> +	return DIV_ROUND_UP(cycles, OV4689_XVCLK_FREQ / 1000 / 1000);
> >
> > Please use the actual rate instead.
> >
> 
> Do you mean clk_get_rate(ov4689->xvclk), right? What if we have an ACPI

Yes.

> system and xvclk is NULL here? Please explain.

There are a few ways this could work on ACPI based systems but generally
you'd have "clock-frequency" property to indicate the frequency even if
clocks themselves wouldn't be available (when controlled via ACPI)).

-- 
Kind regards,

Sakari Ailus

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

* Re: [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver
  2022-09-22 10:43   ` Dave Stevenson
  2022-09-22 15:13     ` Mikhail Rudenko
@ 2022-09-26 10:47     ` Mikhail Rudenko
  1 sibling, 0 replies; 41+ messages in thread
From: Mikhail Rudenko @ 2022-09-26 10:47 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Hans Verkuil, Jacopo Mondi, Shawn Tu,
	Jimmy Su, Arnd Bergmann, Arec Kao, Laurent Pinchart, Marek Vasut,
	linux-media, devicetree, linux-kernel


Hi Dave,

On 2022-09-22 at 11:43 +01, Dave Stevenson <dave.stevenson@raspberrypi.com> wrote:

> Hi Mikhail & Sakari
>
> On Thu, 22 Sept 2022 at 10:55, Sakari Ailus
> <sakari.ailus@linux.intel.com> wrote:
>>
>> Hi Mikhail,
>>
>> On Sun, Sep 11, 2022 at 11:01:33PM +0300, Mikhail Rudenko wrote:
>> > Hello,
>> >
>> > this series implements support for Omnivision OV4689 image
>> > sensor. The Omnivision OV4689 is a high performance, 1/3-inch, 4
>> > megapixel image sensor. Ihis chip supports high frame rate speeds up
>> > to 90 fps at 2688x1520 resolution. It is programmable through an I2C
>> > interface, and sensor output is sent via 1/2/4 lane MIPI CSI-2
>> > connection.
>> >
>> > The driver is based on Rockchip BSP kernel [1]. It implements 4-lane CSI-2
>> > and single 2688x1520 @ 30 fps mode. The driver was tested on Rockchip
>> > 3399-based FriendlyElec NanoPi M4 board with MCAM400 camera module.
>> >
>> > While porting the driver, I stumbled upon two issues:
>> >
>> > (1) In the original driver, horizontal total size (HTS) was set to a
>> > value (2584) lower then the frame width (2688), resulting in negative
>> > hblank. In this driver, I increased HTS to 2688, but fps dropped from
>> > 29.88 to 28.73. What is the preferred way to handle this?
>>
>> If horizontal total size is less than the frame width, something is
>> certainly wrong there. You can't have negative horizontal blanking. Neither
>> it can be zero.
>
> Something certainly seems odd.
>
> To continue my thoughts from earlier in this patch set, Omnivision's
> Product Brief [1] states:
> The 1/3-inch OV4689 can capture full-resolution 4-megapixel high
> definition (HD) video at 90 frames per second (fps), 1080p HD at 120
> fps, and binned 720p HD at 180 fps
>
> The datasheet section 2.1 states:
> The OV4689 color image sensor is a high performance, 4 megapixel RAW
> image sensor that delivers 2688x1520 at 90 fps using OmniBSI-2™ pixel
> technology.
>
> So 4MP 90fps or 1080p120 should be achievable somehow.
>
> 2688x1520 @ 90fps is 367.7MPix/s, and that tallies quite nicely with
> table 2-9 listing the DAC PLL speed limitation of 360-378MHz. Exactly
> how that is then converted into PCLK or SCLK is unclear.
> Ideally you'd be able to contact an Omnivision FAE to confirm, but
> that means you need to be buying modules directly from them or
> otherwise have a business relationship.
> I do note that there is an NVidia Tegra driver for OV4689 at [2]. I
> wonder if analysis of that would reveal anything.
>
> I have just been looking at the ov9282 driver and the timings don't
> tally there either - configure it for 60fps and you get 30fps. The
> TIMING_HTS register appears to be in units of 2 pixels. The default is
> 0x2d8 (728 decimal) on a 1280x720 mode, but consider it as units of 2
> pixels and HTS of 1456 (1280 active and hblank of 176) does match up.
> It works in the general case too.
>
> Looking at the OV4689 datasheet again, the default for TIMING_HTS
> (0x380c/d) is 0x5f8 (1528 decimal) when HOUTPUT_SIZE (0x3808/9) is
> 0x1200 (4608 decimal). Whilst there are no guarantees that default
> register settings will stream in any sensible form, it does imply
> TIMING_HTS is not in units of 1 pixel, and potentially 4 pixels.
> From the Rockchip BSP driver it plainly does stream at 30 (or 29.88)
> fps with TIMING_HTS < HOUTPUT_SIZE, so a quick test of reducing it
> further would be worthwhile. What does the default of 0x2d8 give you
> as a frame rate, and are the images correct?
>

I've done some experimentation with timing registers and here is what I
found. First of all, there are two tables with default values of timing
registers (table 4-2 and table 6-9) in publicly available datasheet, and
they (values) are completely different. I'll just leave relevant parts
here for reference:

    Defaults (table 4-2):
    H_OUT_SIZE: 4608 (0x1200)
    V_OUT_SIZE: 3456 (0x0d80)
    HTS: 1528 (0x05f8)
    VTS: 3492 (0x0da4)

    Defaults (table 6-9):
    H_OUT_SIZE: 2688 (0xa80)
    V_OUT_SIZE: 1520 (0x5f0)
    HTS: 860 (0x35c)
    VTS: 1552 (0x610)

    Driver v2:
    H_OUT_SIZE: 2688
    V_OUT_SIZE: 1520
    HTS(0x380c/0d): 2688 (0x0a80)
    VTS(0x380e/0f): 1554 (0x0612)

Then I tried decreasing hts/vts and seeing what happens. The lowest
possible values before frame dropping started were hts=886 and vts=1552,
and the frame rate was 87.27 fps. Multiplying these three numbers yields
pixel rate of 120.0025e6.

So it looks like you are right, and the unit of HTS register is at least
4 pixels. Hence, in order to allow libcamera do correct timing
calculations, we should report pixel rate of 4*120e6 == 480e6, and
HBLANK of (4*HTS - H_OUT_SIZE). For 30 fps and VTS of 1554, this yields
HTS=2574 and HBLANK = (4*2574 - 2688) = 7608.

In fact, the above is what I'm going to implement in v3. Comments,
anyone?

> Just some thoughts.
>   Dave
>
> [1] https://www.ovt.com/wp-content/uploads/2022/01/OV4689-PB-v1.7-WEB.pdf
> [2] https://github.com/bogsen/STLinux-Kernel/blob/master/drivers/media/platform/tegra/ov4689.c
>

--
Best regards,
Mikhail Rudenko

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

end of thread, other threads:[~2022-09-26 13:41 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-11 20:01 [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver Mikhail Rudenko
2022-09-11 20:01 ` [PATCH v2 1/2] media: dt-bindings: media: i2c: document OV4689 DT bindings Mikhail Rudenko
2022-09-12 10:55   ` Krzysztof Kozlowski
2022-09-15 12:16     ` Mikhail Rudenko
2022-09-16  9:42       ` Krzysztof Kozlowski
2022-09-13 14:05   ` Tommaso Merciai
2022-09-15 20:11     ` Mikhail Rudenko
2022-09-16 13:15       ` Tommaso Merciai
2022-09-16 13:42         ` Mikhail Rudenko
2022-09-19 13:16           ` Laurent Pinchart
2022-09-11 20:01 ` [PATCH v2 2/2] media: i2c: add support for ov4689 Mikhail Rudenko
2022-09-11 22:52   ` kernel test robot
2022-09-12 10:56   ` Krzysztof Kozlowski
2022-09-15 20:40     ` Mikhail Rudenko
2022-09-16  9:43       ` Krzysztof Kozlowski
2022-09-16 21:51         ` Sakari Ailus
2022-09-14 15:51   ` Tommaso Merciai
2022-09-15 20:50     ` Mikhail Rudenko
2022-09-16 13:34       ` Tommaso Merciai
2022-09-16 13:44         ` Mikhail Rudenko
2022-09-19  7:08           ` Tommaso Merciai
2022-09-19  6:33         ` Sakari Ailus
2022-09-19  7:11           ` Tommaso Merciai
2022-09-22  9:53   ` Sakari Ailus
2022-09-22 15:23     ` Mikhail Rudenko
2022-09-22 20:39       ` Sakari Ailus
2022-09-22 10:54   ` Dave Stevenson
2022-09-22 14:56     ` Mikhail Rudenko
2022-09-14  9:58 ` [PATCH v2 0/2] Add Omnivision OV4689 image sensor driver Dave Stevenson
2022-09-15 21:27   ` Mikhail Rudenko
2022-09-19  6:40     ` Sakari Ailus
2022-09-19  7:01       ` Mikhail Rudenko
2022-09-19 10:31         ` Sakari Ailus
2022-09-19 13:49           ` Laurent Pinchart
2022-09-20 15:55             ` Mikhail Rudenko
2022-09-20 20:31             ` Mikhail Rudenko
2022-09-21 13:16               ` Sakari Ailus
2022-09-22  9:53 ` Sakari Ailus
2022-09-22 10:43   ` Dave Stevenson
2022-09-22 15:13     ` Mikhail Rudenko
2022-09-26 10:47     ` Mikhail Rudenko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.