All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/2] Add IMX219 CMOS image sensor support
@ 2020-01-20  8:15 Andrey Konovalov
  2020-01-20  8:15 ` [PATCH v4 1/2] dt-bindings: media: i2c: Add IMX219 CMOS sensor binding Andrey Konovalov
  2020-01-20  8:15 ` [PATCH v4 2/2] media: i2c: Add driver for Sony IMX219 sensor Andrey Konovalov
  0 siblings, 2 replies; 7+ messages in thread
From: Andrey Konovalov @ 2020-01-20  8:15 UTC (permalink / raw)
  To: mchehab, robh+dt
  Cc: linux-media, devicetree, sakari.ailus, peter.griffin,
	dave.stevenson, ezequiel, Andrey Konovalov

This patchset adds support for IMX219 CMOS image sensor from Sony.
Sony IMX219 is an 8MPix, 1/4.0-inch CMOS active pixel digital image sensor
with an active array size of 3280H x 2464V. It is programmable through
I2C interface. Image data are sent through MIPI CSI-2, which can be configured
as either 2 or 4 data lanes, but this driver currently only supports 2 lanes.
The currently supported resolutions are 3280x2464 @ 15fps, 1920x1080 @ 30fps
(cropped FOV), and 1640x1232 (2x2 binned) @ 30fps.

The driver has been tested with Raspberry Pi Camera Module v2 connected to
Raspberry Pi Zero W.

Changes since v3 [1]:

dt-bindings:
  - "link-frequencies" property added

imx219 sensor driver:
  - "depends on MEDIA_CAMERA_SUPPORT" removed from the Kconfig as all the
    sensor drivers are under "if MEDIA_CAMERA_SUPPORT" now
  - in imx219_get_pad_format(), when 'which' argument is V4L2_SUBDEV_FORMAT_TRY
    update the code field in v4l2_mbus_framefmt structure, as the format code
    could change due to vflip or hflip
  - in imx219_power_on(), the delay after de-asserting the reset is changed
    (reduced from 10 mS to 6.2 mS) to fit the startup timings better. Also
    switched from using msleep() to usleep_range(), as otherwise the change
    from 10 mS to 6.2 mS wouldn't have an effect (see
    https://www.kernel.org/doc/Documentation/timers/timers-howto.txt).
    Added the comment which explains how the value of the delay was calculated,
    and why it is fixed value
  - empty lines added before "return x;" statements
  - in imx219_get_regulators() "int i;" changed to "unsigned int i;"
  - the 'dev' field dropped from struct imx219
  - the link frequencies check is added. As the current driver supports only
    single link frequency, the check just compares the link-frequency in DT to
    the predefined value.
    To be able to read link-frequencies from the DT, call to
    v4l2_fwnode_endpoint_parse() was replaced with call to
    v4l2_fwnode_endpoint_alloc_parse(), and the code was rearranged:
    imx219_check_hwcfg() was introduced, and 'ep' field was removed from imx219
    structure, as the parsed DT endpoint info is not used outside
    imx219_check_hwcfg()
  - in imx219_remove(), the code was added to power off the sensor in the case
    when runtime PM is disabled
  - 'probe_new' is used instead of 'probe'

Thanks,
Andrey

[1] https://patchwork.kernel.org/cover/11328425/


Andrey Konovalov (1):
  dt-bindings: media: i2c: Add IMX219 CMOS sensor binding

Dave Stevenson (1):
  media: i2c: Add driver for Sony IMX219 sensor

 .../devicetree/bindings/media/i2c/imx219.yaml |  114 ++
 MAINTAINERS                                   |    8 +
 drivers/media/i2c/Kconfig                     |   11 +
 drivers/media/i2c/Makefile                    |    1 +
 drivers/media/i2c/imx219.c                    | 1312 +++++++++++++++++
 5 files changed, 1446 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.yaml
 create mode 100644 drivers/media/i2c/imx219.c

-- 
2.17.1


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

* [PATCH v4 1/2] dt-bindings: media: i2c: Add IMX219 CMOS sensor binding
  2020-01-20  8:15 [PATCH v4 0/2] Add IMX219 CMOS image sensor support Andrey Konovalov
@ 2020-01-20  8:15 ` Andrey Konovalov
  2020-01-20 11:32   ` Andrey Konovalov
  2020-01-20  8:15 ` [PATCH v4 2/2] media: i2c: Add driver for Sony IMX219 sensor Andrey Konovalov
  1 sibling, 1 reply; 7+ messages in thread
From: Andrey Konovalov @ 2020-01-20  8:15 UTC (permalink / raw)
  To: mchehab, robh+dt
  Cc: linux-media, devicetree, sakari.ailus, peter.griffin,
	dave.stevenson, ezequiel, Andrey Konovalov

Add YAML device tree binding for IMX219 CMOS image sensor, and
the relevant MAINTAINERS entries.

Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
---
 .../devicetree/bindings/media/i2c/imx219.yaml | 114 ++++++++++++++++++
 MAINTAINERS                                   |   8 ++
 2 files changed, 122 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.yaml

diff --git a/Documentation/devicetree/bindings/media/i2c/imx219.yaml b/Documentation/devicetree/bindings/media/i2c/imx219.yaml
new file mode 100644
index 000000000000..32d6b693274f
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/imx219.yaml
@@ -0,0 +1,114 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/imx219.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor
+
+maintainers:
+  - Dave Stevenson <dave.stevenson@raspberrypi.com>
+
+description: |-
+  The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor
+  with an active array size of 3280H x 2464V. It is programmable through
+  I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet.
+  Image data is sent through MIPI CSI-2, which is configured as either 2 or
+  4 data lanes.
+
+properties:
+  compatible:
+    const: sony,imx219
+
+  reg:
+    description: I2C device address
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  VDIG-supply:
+    description:
+      Digital I/O voltage supply, 1.8 volts
+
+  VANA-supply:
+    description:
+      Analog voltage supply, 2.8 volts
+
+  VDDL-supply:
+    description:
+      Digital core voltage supply, 1.2 volts
+
+  reset-gpios:
+    description: |-
+      Reference to the GPIO connected to the xclr pin, if any.
+      Must be released (set high) after all supplies are applied.
+
+  # See ../video-interfaces.txt for more details
+  port:
+    type: object
+    properties:
+      endpoint:
+        type: object
+        properties:
+          data-lanes:
+            description: |-
+              The sensor supports either two-lane, or four-lane operation.
+              If this property is omitted four-lane operation is assumed.
+              For two-lane operation the property must be set to <1 2>.
+            items:
+              - const: 1
+              - const: 2
+
+          clock-noncontinuous:
+            type: boolean
+            description: |-
+              MIPI CSI-2 clock is non-continuous if this property is present,
+              otherwise it's continuous.
+
+          link-frequencies:
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/uint64-array
+            description:
+              Allowed data bus frequencies.
+
+        required:
+          - link-frequencies
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - VANA-supply
+  - VDIG-supply
+  - VDDL-supply
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c0 {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        imx219: sensor@10 {
+            compatible = "sony,imx219";
+            reg = <0x10>;
+            clocks = <&imx219_clk>;
+            VANA-supply = <&imx219_vana>;   /* 2.8v */
+            VDIG-supply = <&imx219_vdig>;   /* 1.8v */
+            VDDL-supply = <&imx219_vddl>;   /* 1.2v */
+
+            port {
+                imx219_0: endpoint {
+                    remote-endpoint = <&csi1_ep>;
+                    data-lanes = <1 2>;
+                    clock-noncontinuous;
+                    link-frequencies = /bits/ 64 <456000000>;
+                };
+            };
+        };
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index cf6ccca6e61c..aae83699e146 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15351,6 +15351,14 @@ S:	Maintained
 F:	drivers/media/i2c/imx214.c
 F:	Documentation/devicetree/bindings/media/i2c/sony,imx214.txt
 
+SONY IMX219 SENSOR DRIVER
+M:	Dave Stevenson <dave.stevenson@raspberrypi.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	drivers/media/i2c/imx219.c
+F:	Documentation/devicetree/bindings/media/i2c/imx219.yaml
+
 SONY IMX258 SENSOR DRIVER
 M:	Sakari Ailus <sakari.ailus@linux.intel.com>
 L:	linux-media@vger.kernel.org
-- 
2.17.1


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

* [PATCH v4 2/2] media: i2c: Add driver for Sony IMX219 sensor
  2020-01-20  8:15 [PATCH v4 0/2] Add IMX219 CMOS image sensor support Andrey Konovalov
  2020-01-20  8:15 ` [PATCH v4 1/2] dt-bindings: media: i2c: Add IMX219 CMOS sensor binding Andrey Konovalov
@ 2020-01-20  8:15 ` Andrey Konovalov
  2020-01-21 16:47   ` Sakari Ailus
  1 sibling, 1 reply; 7+ messages in thread
From: Andrey Konovalov @ 2020-01-20  8:15 UTC (permalink / raw)
  To: mchehab, robh+dt
  Cc: linux-media, devicetree, sakari.ailus, peter.griffin,
	dave.stevenson, ezequiel, Andrey Konovalov

From: Dave Stevenson <dave.stevenson@raspberrypi.com>

Adds a driver for the 8MPix Sony IMX219 CSI2 sensor.
Whilst the sensor supports 2 or 4 CSI2 data lanes, this driver
currently only supports 2 lanes.
8MPix @ 15fps, 1080P @ 30fps (cropped FOV), and 1640x1232 (2x2 binned)
@ 30fps are currently supported.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
---
 drivers/media/i2c/Kconfig  |   11 +
 drivers/media/i2c/Makefile |    1 +
 drivers/media/i2c/imx219.c | 1312 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1324 insertions(+)
 create mode 100644 drivers/media/i2c/imx219.c

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index c68e002d26ea..87fba68542b5 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -591,6 +591,17 @@ config VIDEO_IMX214
 	  To compile this driver as a module, choose M here: the
 	  module will be called imx214.
 
+config VIDEO_IMX219
+	tristate "Sony IMX219 sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	select V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor driver for the Sony
+	  IMX219 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called imx219.
+
 config VIDEO_IMX258
 	tristate "Sony IMX258 sensor support"
 	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index c147bb9d28db..77bf7d0b691f 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -111,6 +111,7 @@ obj-$(CONFIG_VIDEO_OV2659)	+= ov2659.o
 obj-$(CONFIG_VIDEO_TC358743)	+= tc358743.o
 obj-$(CONFIG_VIDEO_HI556)	+= hi556.o
 obj-$(CONFIG_VIDEO_IMX214)	+= imx214.o
+obj-$(CONFIG_VIDEO_IMX219)	+= imx219.o
 obj-$(CONFIG_VIDEO_IMX258)	+= imx258.o
 obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
 obj-$(CONFIG_VIDEO_IMX290)	+= imx290.o
diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
new file mode 100644
index 000000000000..8627a5903039
--- /dev/null
+++ b/drivers/media/i2c/imx219.c
@@ -0,0 +1,1312 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A V4L2 driver for Sony IMX219 cameras.
+ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd
+ *
+ * Based on Sony imx258 camera driver
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver
+ * Copyright 2018 Qtechnology A/S
+ *
+ * Flip handling taken from the Sony IMX319 driver.
+ * Copyright (C) 2018 Intel Corporation
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.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/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mediabus.h>
+#include <asm/unaligned.h>
+
+#define IMX219_REG_VALUE_08BIT		1
+#define IMX219_REG_VALUE_16BIT		2
+
+#define IMX219_REG_MODE_SELECT		0x0100
+#define IMX219_MODE_STANDBY		0x00
+#define IMX219_MODE_STREAMING		0x01
+
+/* Chip ID */
+#define IMX219_REG_CHIP_ID		0x0000
+#define IMX219_CHIP_ID			0x0219
+
+/* External clock frequency is 24.0M */
+#define IMX219_XCLK_FREQ		24000000
+
+/* Pixel rate is fixed at 182.4M for all the modes */
+#define IMX219_PIXEL_RATE		182400000
+
+#define IMX219_DEFAULT_LINK_FREQ	456000000
+
+/* V_TIMING internal */
+#define IMX219_REG_VTS			0x0160
+#define IMX219_VTS_15FPS		0x0dc6
+#define IMX219_VTS_30FPS_1080P		0x06e3
+#define IMX219_VTS_30FPS_BINNED		0x06e3
+#define IMX219_VTS_MAX			0xffff
+
+#define IMX219_VBLANK_MIN		4
+
+/*Frame Length Line*/
+#define IMX219_FLL_MIN			0x08a6
+#define IMX219_FLL_MAX			0xffff
+#define IMX219_FLL_STEP			1
+#define IMX219_FLL_DEFAULT		0x0c98
+
+/* HBLANK control - read only */
+#define IMX219_PPL_DEFAULT		3448
+
+/* Exposure control */
+#define IMX219_REG_EXPOSURE		0x015a
+#define IMX219_EXPOSURE_MIN		4
+#define IMX219_EXPOSURE_STEP		1
+#define IMX219_EXPOSURE_DEFAULT		0x640
+#define IMX219_EXPOSURE_MAX		65535
+
+/* Analog gain control */
+#define IMX219_REG_ANALOG_GAIN		0x0157
+#define IMX219_ANA_GAIN_MIN		0
+#define IMX219_ANA_GAIN_MAX		232
+#define IMX219_ANA_GAIN_STEP		1
+#define IMX219_ANA_GAIN_DEFAULT		0x0
+
+/* Digital gain control */
+#define IMX219_REG_DIGITAL_GAIN		0x0158
+#define IMX219_DGTL_GAIN_MIN		0x0100
+#define IMX219_DGTL_GAIN_MAX		0x0fff
+#define IMX219_DGTL_GAIN_DEFAULT	0x0100
+#define IMX219_DGTL_GAIN_STEP		1
+
+#define IMX219_REG_ORIENTATION		0x0172
+
+/* Test Pattern Control */
+#define IMX219_REG_TEST_PATTERN		0x0600
+#define IMX219_TEST_PATTERN_DISABLE	0
+#define IMX219_TEST_PATTERN_SOLID_COLOR	1
+#define IMX219_TEST_PATTERN_COLOR_BARS	2
+#define IMX219_TEST_PATTERN_GREY_COLOR	3
+#define IMX219_TEST_PATTERN_PN9		4
+
+/* Test pattern colour components */
+#define IMX219_REG_TESTP_RED		0x0602
+#define IMX219_REG_TESTP_GREENR		0x0604
+#define IMX219_REG_TESTP_BLUE		0x0606
+#define IMX219_REG_TESTP_GREENB		0x0608
+#define IMX219_TESTP_COLOUR_MIN		0
+#define IMX219_TESTP_COLOUR_MAX		0x03ff
+#define IMX219_TESTP_COLOUR_STEP	1
+#define IMX219_TESTP_RED_DEFAULT	IMX219_TESTP_COLOUR_MAX
+#define IMX219_TESTP_GREENR_DEFAULT	0
+#define IMX219_TESTP_BLUE_DEFAULT	0
+#define IMX219_TESTP_GREENB_DEFAULT	0
+
+struct imx219_reg {
+	u16 address;
+	u8 val;
+};
+
+struct imx219_reg_list {
+	unsigned int num_of_regs;
+	const struct imx219_reg *regs;
+};
+
+/* Mode : resolution and related config&values */
+struct imx219_mode {
+	/* Frame width */
+	unsigned int width;
+	/* Frame height */
+	unsigned int height;
+
+	/* V-timing */
+	unsigned int vts_def;
+
+	/* Default register values */
+	struct imx219_reg_list reg_list;
+};
+
+/*
+ * Register sets lifted off the i2C interface from the Raspberry Pi firmware
+ * driver.
+ * 3280x2464 = mode 2, 1920x1080 = mode 1, and 1640x1232 = mode 4.
+ */
+static const struct imx219_reg mode_3280x2464_regs[] = {
+	{0x0100, 0x00},
+	{0x30eb, 0x0c},
+	{0x30eb, 0x05},
+	{0x300a, 0xff},
+	{0x300b, 0xff},
+	{0x30eb, 0x05},
+	{0x30eb, 0x09},
+	{0x0114, 0x01},
+	{0x0128, 0x00},
+	{0x012a, 0x18},
+	{0x012b, 0x00},
+	{0x0164, 0x00},
+	{0x0165, 0x00},
+	{0x0166, 0x0c},
+	{0x0167, 0xcf},
+	{0x0168, 0x00},
+	{0x0169, 0x00},
+	{0x016a, 0x09},
+	{0x016b, 0x9f},
+	{0x016c, 0x0c},
+	{0x016d, 0xd0},
+	{0x016e, 0x09},
+	{0x016f, 0xa0},
+	{0x0170, 0x01},
+	{0x0171, 0x01},
+	{0x0174, 0x00},
+	{0x0175, 0x00},
+	{0x018c, 0x0a},
+	{0x018d, 0x0a},
+	{0x0301, 0x05},
+	{0x0303, 0x01},
+	{0x0304, 0x03},
+	{0x0305, 0x03},
+	{0x0306, 0x00},
+	{0x0307, 0x39},
+	{0x0309, 0x0a},
+	{0x030b, 0x01},
+	{0x030c, 0x00},
+	{0x030d, 0x72},
+	{0x0624, 0x0c},
+	{0x0625, 0xd0},
+	{0x0626, 0x09},
+	{0x0627, 0xa0},
+	{0x455e, 0x00},
+	{0x471e, 0x4b},
+	{0x4767, 0x0f},
+	{0x4750, 0x14},
+	{0x4540, 0x00},
+	{0x47b4, 0x14},
+	{0x4713, 0x30},
+	{0x478b, 0x10},
+	{0x478f, 0x10},
+	{0x4793, 0x10},
+	{0x4797, 0x0e},
+	{0x479b, 0x0e},
+	{0x0162, 0x0d},
+	{0x0163, 0x78},
+};
+
+static const struct imx219_reg mode_1920_1080_regs[] = {
+	{0x0100, 0x00},
+	{0x30eb, 0x05},
+	{0x30eb, 0x0c},
+	{0x300a, 0xff},
+	{0x300b, 0xff},
+	{0x30eb, 0x05},
+	{0x30eb, 0x09},
+	{0x0114, 0x01},
+	{0x0128, 0x00},
+	{0x012a, 0x18},
+	{0x012b, 0x00},
+	{0x0162, 0x0d},
+	{0x0163, 0x78},
+	{0x0164, 0x02},
+	{0x0165, 0xa8},
+	{0x0166, 0x0a},
+	{0x0167, 0x27},
+	{0x0168, 0x02},
+	{0x0169, 0xb4},
+	{0x016a, 0x06},
+	{0x016b, 0xeb},
+	{0x016c, 0x07},
+	{0x016d, 0x80},
+	{0x016e, 0x04},
+	{0x016f, 0x38},
+	{0x0170, 0x01},
+	{0x0171, 0x01},
+	{0x0174, 0x00},
+	{0x0175, 0x00},
+	{0x018c, 0x0a},
+	{0x018d, 0x0a},
+	{0x0301, 0x05},
+	{0x0303, 0x01},
+	{0x0304, 0x03},
+	{0x0305, 0x03},
+	{0x0306, 0x00},
+	{0x0307, 0x39},
+	{0x0309, 0x0a},
+	{0x030b, 0x01},
+	{0x030c, 0x00},
+	{0x030d, 0x72},
+	{0x0624, 0x07},
+	{0x0625, 0x80},
+	{0x0626, 0x04},
+	{0x0627, 0x38},
+	{0x455e, 0x00},
+	{0x471e, 0x4b},
+	{0x4767, 0x0f},
+	{0x4750, 0x14},
+	{0x4540, 0x00},
+	{0x47b4, 0x14},
+	{0x4713, 0x30},
+	{0x478b, 0x10},
+	{0x478f, 0x10},
+	{0x4793, 0x10},
+	{0x4797, 0x0e},
+	{0x479b, 0x0e},
+	{0x0162, 0x0d},
+	{0x0163, 0x78},
+};
+
+static const struct imx219_reg mode_1640_1232_regs[] = {
+	{0x0100, 0x00},
+	{0x30eb, 0x0c},
+	{0x30eb, 0x05},
+	{0x300a, 0xff},
+	{0x300b, 0xff},
+	{0x30eb, 0x05},
+	{0x30eb, 0x09},
+	{0x0114, 0x01},
+	{0x0128, 0x00},
+	{0x012a, 0x18},
+	{0x012b, 0x00},
+	{0x0164, 0x00},
+	{0x0165, 0x00},
+	{0x0166, 0x0c},
+	{0x0167, 0xcf},
+	{0x0168, 0x00},
+	{0x0169, 0x00},
+	{0x016a, 0x09},
+	{0x016b, 0x9f},
+	{0x016c, 0x06},
+	{0x016d, 0x68},
+	{0x016e, 0x04},
+	{0x016f, 0xd0},
+	{0x0170, 0x01},
+	{0x0171, 0x01},
+	{0x0174, 0x01},
+	{0x0175, 0x01},
+	{0x018c, 0x0a},
+	{0x018d, 0x0a},
+	{0x0301, 0x05},
+	{0x0303, 0x01},
+	{0x0304, 0x03},
+	{0x0305, 0x03},
+	{0x0306, 0x00},
+	{0x0307, 0x39},
+	{0x0309, 0x0a},
+	{0x030b, 0x01},
+	{0x030c, 0x00},
+	{0x030d, 0x72},
+	{0x0624, 0x06},
+	{0x0625, 0x68},
+	{0x0626, 0x04},
+	{0x0627, 0xd0},
+	{0x455e, 0x00},
+	{0x471e, 0x4b},
+	{0x4767, 0x0f},
+	{0x4750, 0x14},
+	{0x4540, 0x00},
+	{0x47b4, 0x14},
+	{0x4713, 0x30},
+	{0x478b, 0x10},
+	{0x478f, 0x10},
+	{0x4793, 0x10},
+	{0x4797, 0x0e},
+	{0x479b, 0x0e},
+	{0x0162, 0x0d},
+	{0x0163, 0x78},
+};
+
+static const char * const imx219_test_pattern_menu[] = {
+	"Disabled",
+	"Color Bars",
+	"Solid Color",
+	"Grey Color Bars",
+	"PN9"
+};
+
+static const int imx219_test_pattern_val[] = {
+	IMX219_TEST_PATTERN_DISABLE,
+	IMX219_TEST_PATTERN_COLOR_BARS,
+	IMX219_TEST_PATTERN_SOLID_COLOR,
+	IMX219_TEST_PATTERN_GREY_COLOR,
+	IMX219_TEST_PATTERN_PN9,
+};
+
+/* regulator supplies */
+static const char * const imx219_supply_name[] = {
+	/* Supplies can be enabled in any order */
+	"VANA",  /* Analog (2.8V) supply */
+	"VDIG",  /* Digital Core (1.8V) supply */
+	"VDDL",  /* IF (1.2V) supply */
+};
+
+#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name)
+
+/*
+ * Initialisation delay between XCLR low->high and the moment when the sensor
+ * can start capture (i.e. can leave software stanby) must be not less than:
+ *   t4 + max(t5, t6 + <time to initialize the sensor register over I2C>)
+ * where
+ *   t4 is fixed, and is max 200uS,
+ *   t5 is fixed, and is 6000uS,
+ *   t6 depends on the sensor external clock, and is max 32000 clock periods.
+ * As per sensor datasheet, the external clock must be from 6MHz to 27MHz.
+ * So for any acceptable external clock t6 is always within the range of
+ * 1185 to 5333 uS, and is always less than t5.
+ * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then
+ * initialize the sensor over I2C, and then exit the software standby.
+ *
+ * This start-up time can be optimized a bit more, if we start the writes
+ * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor
+ * initialization over I2C may complete before (t4+t5) expires, and we must
+ * ensure that capture is not started before (t4+t5).
+ *
+ * This delay doesn't account for the power supply startup time. If needed,
+ * this should be taken care of via the regulator framework. E.g. in the
+ * case of DT for regulator-fixed one should define the startup-delay-us
+ * property.
+ */
+#define IMX219_XCLR_MIN_DELAY_US	6200
+#define IMX219_XCLR_DELAY_RANGE_US	1000
+
+/* Mode configs */
+static const struct imx219_mode supported_modes[] = {
+	{
+		/* 8MPix 15fps mode */
+		.width = 3280,
+		.height = 2464,
+		.vts_def = IMX219_VTS_15FPS,
+		.reg_list = {
+			.num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
+			.regs = mode_3280x2464_regs,
+		},
+	},
+	{
+		/* 1080P 30fps cropped */
+		.width = 1920,
+		.height = 1080,
+		.vts_def = IMX219_VTS_30FPS_1080P,
+		.reg_list = {
+			.num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
+			.regs = mode_1920_1080_regs,
+		},
+	},
+	{
+		/* 2x2 binned 30fps mode */
+		.width = 1640,
+		.height = 1232,
+		.vts_def = IMX219_VTS_30FPS_BINNED,
+		.reg_list = {
+			.num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
+			.regs = mode_1640_1232_regs,
+		},
+	},
+};
+
+struct imx219 {
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+
+	struct clk *xclk; /* system clock to IMX219 */
+	u32 xclk_freq;
+
+	struct gpio_desc *reset_gpio;
+	struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES];
+
+	struct v4l2_ctrl_handler ctrl_handler;
+	/* V4L2 Controls */
+	struct v4l2_ctrl *pixel_rate;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *vflip;
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *hblank;
+
+	/* Current mode */
+	const struct imx219_mode *mode;
+
+	/*
+	 * Mutex for serialized access:
+	 * Protect sensor module set pad format and start/stop streaming safely.
+	 */
+	struct mutex mutex;
+
+	/* Streaming on/off */
+	bool streaming;
+};
+
+static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
+{
+	return container_of(_sd, struct imx219, sd);
+}
+
+/* Read registers up to 2 at a time */
+static int imx219_read_reg(struct imx219 *imx219, u16 reg, u32 len, u32 *val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+	struct i2c_msg msgs[2];
+	u8 addr_buf[2] = { reg >> 8, reg & 0xff };
+	u8 data_buf[4] = { 0, };
+	int ret;
+
+	if (len > 4)
+		return -EINVAL;
+
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = ARRAY_SIZE(addr_buf);
+	msgs[0].buf = addr_buf;
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = &data_buf[4 - len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	*val = get_unaligned_be32(data_buf);
+
+	return 0;
+}
+
+/* Write registers up to 2 at a time */
+static int imx219_write_reg(struct imx219 *imx219, u16 reg, u32 len, u32 val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+	u8 buf[6];
+
+	if (len > 4)
+		return -EINVAL;
+
+	put_unaligned_be16(reg, buf);
+	put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
+	if (i2c_master_send(client, buf, len + 2) != len + 2)
+		return -EIO;
+
+	return 0;
+}
+
+/* Write a list of registers */
+static int imx219_write_regs(struct imx219 *imx219,
+			     const struct imx219_reg *regs, u32 len)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < len; i++) {
+		ret = imx219_write_reg(imx219, regs[i].address, 1, regs[i].val);
+		if (ret) {
+			dev_err_ratelimited(&client->dev,
+					    "Failed to write reg 0x%4.4x. error = %d\n",
+					    regs[i].address, ret);
+
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/* Get bayer order based on flip setting. */
+static u32 imx219_get_format_code(struct imx219 *imx219)
+{
+	/*
+	 * Only one bayer order is supported.
+	 * It depends on the flip settings.
+	 */
+	static const u32 codes[2][2] = {
+		{ MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, },
+		{ MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, },
+	};
+
+	lockdep_assert_held(&imx219->mutex);
+	return codes[imx219->vflip->val][imx219->hflip->val];
+}
+
+static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct imx219 *imx219 = to_imx219(sd);
+	struct v4l2_mbus_framefmt *try_fmt =
+		v4l2_subdev_get_try_format(sd, fh->pad, 0);
+
+	mutex_lock(&imx219->mutex);
+
+	/* Initialize try_fmt */
+	try_fmt->width = supported_modes[0].width;
+	try_fmt->height = supported_modes[0].height;
+	try_fmt->code = imx219_get_format_code(imx219);
+	try_fmt->field = V4L2_FIELD_NONE;
+
+	mutex_unlock(&imx219->mutex);
+
+	return 0;
+}
+
+static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct imx219 *imx219 =
+		container_of(ctrl->handler, struct imx219, ctrl_handler);
+	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+	int ret;
+
+	if (ctrl->id == V4L2_CID_VBLANK) {
+		int exposure_max, exposure_def;
+
+		/* Update max exposure while meeting expected vblanking */
+		exposure_max = imx219->mode->height + ctrl->val - 4;
+		exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
+			exposure_max : IMX219_EXPOSURE_DEFAULT;
+		__v4l2_ctrl_modify_range(imx219->exposure,
+					 imx219->exposure->minimum,
+					 exposure_max, imx219->exposure->step,
+					 exposure_def);
+	}
+
+	/*
+	 * Applying V4L2 control value only happens
+	 * when power is up for streaming
+	 */
+	if (pm_runtime_get_if_in_use(&client->dev) == 0)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_ANALOGUE_GAIN:
+		ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN,
+				       IMX219_REG_VALUE_08BIT, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE,
+				       IMX219_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_DIGITAL_GAIN:
+		ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN,
+				       IMX219_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = imx219_write_reg(imx219, IMX219_REG_TEST_PATTERN,
+				       IMX219_REG_VALUE_16BIT,
+				       imx219_test_pattern_val[ctrl->val]);
+		break;
+	case V4L2_CID_HFLIP:
+	case V4L2_CID_VFLIP:
+		ret = imx219_write_reg(imx219, IMX219_REG_ORIENTATION, 1,
+				       imx219->hflip->val |
+				       imx219->vflip->val << 1);
+		break;
+	case V4L2_CID_VBLANK:
+		ret = imx219_write_reg(imx219, IMX219_REG_VTS,
+				       IMX219_REG_VALUE_16BIT,
+				       imx219->mode->height + ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN_RED:
+		ret = imx219_write_reg(imx219, IMX219_REG_TESTP_RED,
+				       IMX219_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN_GREENR:
+		ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENR,
+				       IMX219_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN_BLUE:
+		ret = imx219_write_reg(imx219, IMX219_REG_TESTP_BLUE,
+				       IMX219_REG_VALUE_16BIT, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN_GREENB:
+		ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENB,
+				       IMX219_REG_VALUE_16BIT, ctrl->val);
+		break;
+	default:
+		dev_info(&client->dev,
+			 "ctrl(id:0x%x,val:0x%x) is not handled\n",
+			 ctrl->id, ctrl->val);
+		ret = -EINVAL;
+		break;
+	}
+
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops imx219_ctrl_ops = {
+	.s_ctrl = imx219_set_ctrl,
+};
+
+static int imx219_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct imx219 *imx219 = to_imx219(sd);
+
+	/*
+	 * Only one bayer order is supported (though it depends on the flip
+	 * settings)
+	 */
+	if (code->index > 0)
+		return -EINVAL;
+
+	code->code = imx219_get_format_code(imx219);
+
+	return 0;
+}
+
+static int imx219_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct imx219 *imx219 = to_imx219(sd);
+
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != imx219_get_format_code(imx219))
+		return -EINVAL;
+
+	fse->min_width = supported_modes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = supported_modes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
+{
+	fmt->colorspace = V4L2_COLORSPACE_SRGB;
+	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+	fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
+							  fmt->colorspace,
+							  fmt->ycbcr_enc);
+	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+}
+
+static void imx219_update_pad_format(struct imx219 *imx219,
+				     const struct imx219_mode *mode,
+				     struct v4l2_subdev_format *fmt)
+{
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.code = imx219_get_format_code(imx219);
+	fmt->format.field = V4L2_FIELD_NONE;
+
+	imx219_reset_colorspace(&fmt->format);
+}
+
+static int __imx219_get_pad_format(struct imx219 *imx219,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_format *fmt)
+{
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		struct v4l2_mbus_framefmt *try_fmt =
+			v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad);
+		/* update the code which could change due to vflip or hflip: */
+		try_fmt->code = imx219_get_format_code(imx219);
+		fmt->format = *try_fmt;
+	} else {
+		imx219_update_pad_format(imx219, imx219->mode, fmt);
+	}
+
+	return 0;
+}
+
+static int imx219_get_pad_format(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_format *fmt)
+{
+	struct imx219 *imx219 = to_imx219(sd);
+	int ret;
+
+	mutex_lock(&imx219->mutex);
+	ret = __imx219_get_pad_format(imx219, cfg, fmt);
+	mutex_unlock(&imx219->mutex);
+
+	return ret;
+}
+
+static int imx219_set_pad_format(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_format *fmt)
+{
+	struct imx219 *imx219 = to_imx219(sd);
+	const struct imx219_mode *mode;
+	struct v4l2_mbus_framefmt *framefmt;
+	int exposure_max, exposure_def, hblank;
+
+	mutex_lock(&imx219->mutex);
+
+	/* Bayer order varies with flips */
+	fmt->format.code = imx219_get_format_code(imx219);
+
+	mode = v4l2_find_nearest_size(supported_modes,
+				      ARRAY_SIZE(supported_modes),
+				      width, height,
+				      fmt->format.width, fmt->format.height);
+	imx219_update_pad_format(imx219, mode, fmt);
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+		*framefmt = fmt->format;
+	} else if (imx219->mode != mode) {
+		imx219->mode = mode;
+		/* Update limits and set FPS to default */
+		__v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
+					 IMX219_VTS_MAX - mode->height, 1,
+					 mode->vts_def - mode->height);
+		__v4l2_ctrl_s_ctrl(imx219->vblank,
+				   mode->vts_def - mode->height);
+		/* Update max exposure while meeting expected vblanking */
+		exposure_max = mode->vts_def - 4;
+		exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
+			exposure_max : IMX219_EXPOSURE_DEFAULT;
+		__v4l2_ctrl_modify_range(imx219->exposure,
+					 imx219->exposure->minimum,
+					 exposure_max, imx219->exposure->step,
+					 exposure_def);
+		/*
+		 * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
+		 * depends on mode->width only, and is not changeble in any
+		 * way other than changing the mode.
+		 */
+		hblank = IMX219_PPL_DEFAULT - mode->width;
+		__v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
+					 hblank);
+	}
+
+	mutex_unlock(&imx219->mutex);
+
+	return 0;
+}
+
+static int imx219_start_streaming(struct imx219 *imx219)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+	const struct imx219_reg_list *reg_list;
+	int ret;
+
+	/* Apply default values of current mode */
+	reg_list = &imx219->mode->reg_list;
+	ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
+	if (ret) {
+		dev_err(&client->dev, "%s failed to set mode\n", __func__);
+		return ret;
+	}
+
+	/* Apply customized values from user */
+	ret =  __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
+	if (ret)
+		return ret;
+
+	/* set stream on register */
+	return imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
+				IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
+}
+
+static void imx219_stop_streaming(struct imx219 *imx219)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+	int ret;
+
+	/* set stream off register */
+	ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
+			       IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
+	if (ret)
+		dev_err(&client->dev, "%s failed to set stream\n", __func__);
+}
+
+static int imx219_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct imx219 *imx219 = to_imx219(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	mutex_lock(&imx219->mutex);
+	if (imx219->streaming == enable) {
+		mutex_unlock(&imx219->mutex);
+		return 0;
+	}
+
+	if (enable) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto err_unlock;
+		}
+
+		/*
+		 * Apply default & customized values
+		 * and then start streaming.
+		 */
+		ret = imx219_start_streaming(imx219);
+		if (ret)
+			goto err_rpm_put;
+	} else {
+		imx219_stop_streaming(imx219);
+		pm_runtime_put(&client->dev);
+	}
+
+	imx219->streaming = enable;
+
+	/* vflip and hflip cannot change during streaming */
+	__v4l2_ctrl_grab(imx219->vflip, enable);
+	__v4l2_ctrl_grab(imx219->hflip, enable);
+
+	mutex_unlock(&imx219->mutex);
+
+	return ret;
+
+err_rpm_put:
+	pm_runtime_put(&client->dev);
+err_unlock:
+	mutex_unlock(&imx219->mutex);
+
+	return ret;
+}
+
+/* Power/clock management functions */
+static int imx219_power_on(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx219 *imx219 = to_imx219(sd);
+	int ret;
+
+	ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES,
+				    imx219->supplies);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to enable regulators\n",
+			__func__);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(imx219->xclk);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to enable clock\n",
+			__func__);
+		goto reg_off;
+	}
+
+	gpiod_set_value_cansleep(imx219->reset_gpio, 1);
+	usleep_range(IMX219_XCLR_MIN_DELAY_US,
+		     IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
+
+	return 0;
+
+reg_off:
+	regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
+
+	return ret;
+}
+
+static int imx219_power_off(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx219 *imx219 = to_imx219(sd);
+
+	gpiod_set_value_cansleep(imx219->reset_gpio, 0);
+	regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
+	clk_disable_unprepare(imx219->xclk);
+
+	return 0;
+}
+
+static int __maybe_unused imx219_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx219 *imx219 = to_imx219(sd);
+
+	if (imx219->streaming)
+		imx219_stop_streaming(imx219);
+
+	return 0;
+}
+
+static int __maybe_unused imx219_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx219 *imx219 = to_imx219(sd);
+	int ret;
+
+	if (imx219->streaming) {
+		ret = imx219_start_streaming(imx219);
+		if (ret)
+			goto error;
+	}
+
+	return 0;
+
+error:
+	imx219_stop_streaming(imx219);
+	imx219->streaming = 0;
+
+	return ret;
+}
+
+static int imx219_get_regulators(struct imx219 *imx219)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+	unsigned int i;
+
+	for (i = 0; i < IMX219_NUM_SUPPLIES; i++)
+		imx219->supplies[i].supply = imx219_supply_name[i];
+
+	return devm_regulator_bulk_get(&client->dev,
+				       IMX219_NUM_SUPPLIES,
+				       imx219->supplies);
+}
+
+/* Verify chip ID */
+static int imx219_identify_module(struct imx219 *imx219)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+	int ret;
+	u32 val;
+
+	ret = imx219_read_reg(imx219, IMX219_REG_CHIP_ID,
+			      IMX219_REG_VALUE_16BIT, &val);
+	if (ret) {
+		dev_err(&client->dev, "failed to read chip id %x\n",
+			IMX219_CHIP_ID);
+		return ret;
+	}
+
+	if (val != IMX219_CHIP_ID) {
+		dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
+			IMX219_CHIP_ID, val);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops imx219_core_ops = {
+	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops imx219_video_ops = {
+	.s_stream = imx219_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx219_pad_ops = {
+	.enum_mbus_code = imx219_enum_mbus_code,
+	.get_fmt = imx219_get_pad_format,
+	.set_fmt = imx219_set_pad_format,
+	.enum_frame_size = imx219_enum_frame_size,
+};
+
+static const struct v4l2_subdev_ops imx219_subdev_ops = {
+	.core = &imx219_core_ops,
+	.video = &imx219_video_ops,
+	.pad = &imx219_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
+	.open = imx219_open,
+};
+
+/* Initialize control handlers */
+static int imx219_init_controls(struct imx219 *imx219)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+	struct v4l2_ctrl_handler *ctrl_hdlr;
+	unsigned int height = imx219->mode->height;
+	int exposure_max, exposure_def, hblank;
+	int i, ret;
+
+	ctrl_hdlr = &imx219->ctrl_handler;
+	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+	if (ret)
+		return ret;
+
+	mutex_init(&imx219->mutex);
+	ctrl_hdlr->lock = &imx219->mutex;
+
+	/* By default, PIXEL_RATE is read only */
+	imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
+					       V4L2_CID_PIXEL_RATE,
+					       IMX219_PIXEL_RATE,
+					       IMX219_PIXEL_RATE, 1,
+					       IMX219_PIXEL_RATE);
+
+	/* Initial vblank/hblank/exposure parameters based on current mode */
+	imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
+					   V4L2_CID_VBLANK, IMX219_VBLANK_MIN,
+					   IMX219_VTS_MAX - height, 1,
+					   imx219->mode->vts_def - height);
+	hblank = IMX219_PPL_DEFAULT - imx219->mode->width;
+	imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
+					   V4L2_CID_HBLANK, hblank, hblank,
+					   1, hblank);
+	if (imx219->hblank)
+		imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	exposure_max = imx219->mode->vts_def - 4;
+	exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
+		exposure_max : IMX219_EXPOSURE_DEFAULT;
+	imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
+					     V4L2_CID_EXPOSURE,
+					     IMX219_EXPOSURE_MIN, exposure_max,
+					     IMX219_EXPOSURE_STEP,
+					     exposure_def);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+			  IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX,
+			  IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+			  IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX,
+			  IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT);
+
+	imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
+					  V4L2_CID_HFLIP, 0, 1, 1, 0);
+	if (imx219->hflip)
+		imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+	imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
+					  V4L2_CID_VFLIP, 0, 1, 1, 0);
+	if (imx219->vflip)
+		imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops,
+				     V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(imx219_test_pattern_menu) - 1,
+				     0, 0, imx219_test_pattern_menu);
+	for (i = 0; i < 4; i++) {
+		/*
+		 * The assumption is that
+		 * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
+		 * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
+		 * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
+		 */
+		v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
+				  V4L2_CID_TEST_PATTERN_RED + i,
+				  IMX219_TESTP_COLOUR_MIN,
+				  IMX219_TESTP_COLOUR_MAX,
+				  IMX219_TESTP_COLOUR_STEP,
+				  IMX219_TESTP_COLOUR_MAX);
+		/* The "Solid color" pattern is white by default */
+	}
+
+	if (ctrl_hdlr->error) {
+		ret = ctrl_hdlr->error;
+		dev_err(&client->dev, "%s control init failed (%d)\n",
+			__func__, ret);
+		goto error;
+	}
+
+	imx219->sd.ctrl_handler = ctrl_hdlr;
+
+	return 0;
+
+error:
+	v4l2_ctrl_handler_free(ctrl_hdlr);
+	mutex_destroy(&imx219->mutex);
+
+	return ret;
+}
+
+static void imx219_free_controls(struct imx219 *imx219)
+{
+	v4l2_ctrl_handler_free(imx219->sd.ctrl_handler);
+	mutex_destroy(&imx219->mutex);
+}
+
+int imx219_check_hwcfg(struct device *dev)
+{
+	struct fwnode_handle *endpoint;
+	struct v4l2_fwnode_endpoint ep_cfg = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY
+	};
+	int ret = -EINVAL;
+
+	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+	if (!endpoint) {
+		dev_err(dev, "endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
+		dev_err(dev, "could not parse endpoint\n");
+		goto error_out;
+	}
+
+	/* Check the number of MIPI CSI2 data lanes */
+	if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
+		dev_err(dev, "only 2 data lanes are currently supported\n");
+		goto error_out;
+	}
+
+	/* Check the link frequency set in device tree */
+	if (!ep_cfg.nr_of_link_frequencies) {
+		dev_err(dev, "link-frequency property not found in DT\n");
+		goto error_out;
+	}
+
+	if (ep_cfg.nr_of_link_frequencies != 1 ||
+	    ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
+		dev_err(dev, "Link frequency not supported: %lld\n",
+			ep_cfg.link_frequencies[0]);
+		goto error_out;
+	}
+
+	ret = 0;
+
+error_out:
+	v4l2_fwnode_endpoint_free(&ep_cfg);
+	fwnode_handle_put(endpoint);
+
+	return ret;
+}
+
+static int imx219_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct imx219 *imx219;
+	int ret;
+
+	imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
+	if (!imx219)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
+
+	/* Check the hardware configuration in device tree */
+	if (imx219_check_hwcfg(dev))
+		return -EINVAL;
+
+	/* Get system clock (xclk) */
+	imx219->xclk = devm_clk_get(dev, NULL);
+	if (IS_ERR(imx219->xclk)) {
+		dev_err(dev, "failed to get xclk\n");
+		return PTR_ERR(imx219->xclk);
+	}
+
+	imx219->xclk_freq = clk_get_rate(imx219->xclk);
+	if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
+		dev_err(dev, "xclk frequency not supported: %d Hz\n",
+			imx219->xclk_freq);
+		return -EINVAL;
+	}
+
+	ret = imx219_get_regulators(imx219);
+	if (ret) {
+		dev_err(dev, "failed to get regulators\n");
+		return ret;
+	}
+
+	/* Request optional enable pin */
+	imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+						     GPIOD_OUT_HIGH);
+
+	/*
+	 * The sensor must be powered for imx219_identify_module()
+	 * to be able to read the CHIP_ID register
+	 */
+	ret = imx219_power_on(dev);
+	if (ret)
+		return ret;
+
+	ret = imx219_identify_module(imx219);
+	if (ret)
+		goto error_power_off;
+
+	/* Set default mode to max resolution */
+	imx219->mode = &supported_modes[0];
+
+	ret = imx219_init_controls(imx219);
+	if (ret)
+		goto error_power_off;
+
+	/* Initialize subdev */
+	imx219->sd.internal_ops = &imx219_internal_ops;
+	imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+	/* Initialize source pad */
+	imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
+	if (ret) {
+		dev_err(dev, "failed to init entity pads: %d\n", ret);
+		goto error_handler_free;
+	}
+
+	ret = v4l2_async_register_subdev_sensor_common(&imx219->sd);
+	if (ret < 0) {
+		dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
+		goto error_media_entity;
+	}
+
+	/* Enable runtime PM and turn off the device */
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_idle(dev);
+
+	return 0;
+
+error_media_entity:
+	media_entity_cleanup(&imx219->sd.entity);
+
+error_handler_free:
+	imx219_free_controls(imx219);
+
+error_power_off:
+	imx219_power_off(dev);
+
+	return ret;
+}
+
+static int imx219_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx219 *imx219 = to_imx219(sd);
+
+	v4l2_async_unregister_subdev(sd);
+	media_entity_cleanup(&sd->entity);
+	imx219_free_controls(imx219);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		imx219_power_off(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+static const struct of_device_id imx219_dt_ids[] = {
+	{ .compatible = "sony,imx219" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx219_dt_ids);
+
+static const struct dev_pm_ops imx219_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume)
+	SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL)
+};
+
+static struct i2c_driver imx219_i2c_driver = {
+	.driver = {
+		.name = "imx219",
+		.of_match_table	= imx219_dt_ids,
+		.pm = &imx219_pm_ops,
+	},
+	.probe_new = imx219_probe,
+	.remove = imx219_remove,
+};
+
+module_i2c_driver(imx219_i2c_driver);
+
+MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com");
+MODULE_DESCRIPTION("Sony IMX219 sensor driver");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


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

* Re: [PATCH v4 1/2] dt-bindings: media: i2c: Add IMX219 CMOS sensor binding
  2020-01-20  8:15 ` [PATCH v4 1/2] dt-bindings: media: i2c: Add IMX219 CMOS sensor binding Andrey Konovalov
@ 2020-01-20 11:32   ` Andrey Konovalov
  2020-01-20 11:48     ` Sakari Ailus
  2020-01-21 21:45     ` Rob Herring
  0 siblings, 2 replies; 7+ messages in thread
From: Andrey Konovalov @ 2020-01-20 11:32 UTC (permalink / raw)
  To: mchehab, robh+dt
  Cc: linux-media, devicetree, sakari.ailus, peter.griffin,
	dave.stevenson, ezequiel

v3 of this patch got "Reviewed-by" from Rob Herring.
But unfortunately, as one more property had to be added afterwards, my understanding
is that that "Reviewed-by" doesn't apply to v4.

Thanks,
Andrey

On 20.01.2020 11:15, Andrey Konovalov wrote:
> Add YAML device tree binding for IMX219 CMOS image sensor, and
> the relevant MAINTAINERS entries.
> 
> Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
> ---
>   .../devicetree/bindings/media/i2c/imx219.yaml | 114 ++++++++++++++++++
>   MAINTAINERS                                   |   8 ++
>   2 files changed, 122 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.yaml
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/imx219.yaml b/Documentation/devicetree/bindings/media/i2c/imx219.yaml
> new file mode 100644
> index 000000000000..32d6b693274f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/imx219.yaml
> @@ -0,0 +1,114 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/i2c/imx219.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor
> +
> +maintainers:
> +  - Dave Stevenson <dave.stevenson@raspberrypi.com>
> +
> +description: |-
> +  The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor
> +  with an active array size of 3280H x 2464V. It is programmable through
> +  I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet.
> +  Image data is sent through MIPI CSI-2, which is configured as either 2 or
> +  4 data lanes.
> +
> +properties:
> +  compatible:
> +    const: sony,imx219
> +
> +  reg:
> +    description: I2C device address
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1
> +
> +  VDIG-supply:
> +    description:
> +      Digital I/O voltage supply, 1.8 volts
> +
> +  VANA-supply:
> +    description:
> +      Analog voltage supply, 2.8 volts
> +
> +  VDDL-supply:
> +    description:
> +      Digital core voltage supply, 1.2 volts
> +
> +  reset-gpios:
> +    description: |-
> +      Reference to the GPIO connected to the xclr pin, if any.
> +      Must be released (set high) after all supplies are applied.
> +
> +  # See ../video-interfaces.txt for more details
> +  port:
> +    type: object
> +    properties:
> +      endpoint:
> +        type: object
> +        properties:
> +          data-lanes:
> +            description: |-
> +              The sensor supports either two-lane, or four-lane operation.
> +              If this property is omitted four-lane operation is assumed.
> +              For two-lane operation the property must be set to <1 2>.
> +            items:
> +              - const: 1
> +              - const: 2
> +
> +          clock-noncontinuous:
> +            type: boolean
> +            description: |-
> +              MIPI CSI-2 clock is non-continuous if this property is present,
> +              otherwise it's continuous.
> +
> +          link-frequencies:
> +            allOf:
> +              - $ref: /schemas/types.yaml#/definitions/uint64-array
> +            description:
> +              Allowed data bus frequencies.
> +
> +        required:
> +          - link-frequencies
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - VANA-supply
> +  - VDIG-supply
> +  - VDDL-supply
> +  - port
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    i2c0 {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        imx219: sensor@10 {
> +            compatible = "sony,imx219";
> +            reg = <0x10>;
> +            clocks = <&imx219_clk>;
> +            VANA-supply = <&imx219_vana>;   /* 2.8v */
> +            VDIG-supply = <&imx219_vdig>;   /* 1.8v */
> +            VDDL-supply = <&imx219_vddl>;   /* 1.2v */
> +
> +            port {
> +                imx219_0: endpoint {
> +                    remote-endpoint = <&csi1_ep>;
> +                    data-lanes = <1 2>;
> +                    clock-noncontinuous;
> +                    link-frequencies = /bits/ 64 <456000000>;
> +                };
> +            };
> +        };
> +    };
> +
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index cf6ccca6e61c..aae83699e146 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15351,6 +15351,14 @@ S:	Maintained
>   F:	drivers/media/i2c/imx214.c
>   F:	Documentation/devicetree/bindings/media/i2c/sony,imx214.txt
>   
> +SONY IMX219 SENSOR DRIVER
> +M:	Dave Stevenson <dave.stevenson@raspberrypi.com>
> +L:	linux-media@vger.kernel.org
> +T:	git git://linuxtv.org/media_tree.git
> +S:	Maintained
> +F:	drivers/media/i2c/imx219.c
> +F:	Documentation/devicetree/bindings/media/i2c/imx219.yaml
> +
>   SONY IMX258 SENSOR DRIVER
>   M:	Sakari Ailus <sakari.ailus@linux.intel.com>
>   L:	linux-media@vger.kernel.org
> 

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

* Re: [PATCH v4 1/2] dt-bindings: media: i2c: Add IMX219 CMOS sensor binding
  2020-01-20 11:32   ` Andrey Konovalov
@ 2020-01-20 11:48     ` Sakari Ailus
  2020-01-21 21:45     ` Rob Herring
  1 sibling, 0 replies; 7+ messages in thread
From: Sakari Ailus @ 2020-01-20 11:48 UTC (permalink / raw)
  To: Andrey Konovalov
  Cc: mchehab, robh+dt, linux-media, devicetree, peter.griffin,
	dave.stevenson, ezequiel

Hi Andrey,

On Mon, Jan 20, 2020 at 02:32:16PM +0300, Andrey Konovalov wrote:
> v3 of this patch got "Reviewed-by" from Rob Herring.
> But unfortunately, as one more property had to be added afterwards, my understanding
> is that that "Reviewed-by" doesn't apply to v4.

I assumed that Rob's Reviewed-by: applies also to v4 as adding the link
frequency is a small change, and also because the link-frequency property
is also present in pretty much all other recently added camera sensor DT
bindings (apart from the one that only supports a single frequency).

> 
> Thanks,
> Andrey
> 
> On 20.01.2020 11:15, Andrey Konovalov wrote:
> > Add YAML device tree binding for IMX219 CMOS image sensor, and
> > the relevant MAINTAINERS entries.
> > 
> > Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
> > ---
> >   .../devicetree/bindings/media/i2c/imx219.yaml | 114 ++++++++++++++++++
> >   MAINTAINERS                                   |   8 ++
> >   2 files changed, 122 insertions(+)
> >   create mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.yaml
> > 
> > diff --git a/Documentation/devicetree/bindings/media/i2c/imx219.yaml b/Documentation/devicetree/bindings/media/i2c/imx219.yaml
> > new file mode 100644
> > index 000000000000..32d6b693274f
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/i2c/imx219.yaml
> > @@ -0,0 +1,114 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/media/i2c/imx219.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor
> > +
> > +maintainers:
> > +  - Dave Stevenson <dave.stevenson@raspberrypi.com>
> > +
> > +description: |-
> > +  The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor
> > +  with an active array size of 3280H x 2464V. It is programmable through
> > +  I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet.
> > +  Image data is sent through MIPI CSI-2, which is configured as either 2 or
> > +  4 data lanes.
> > +
> > +properties:
> > +  compatible:
> > +    const: sony,imx219
> > +
> > +  reg:
> > +    description: I2C device address
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    maxItems: 1
> > +
> > +  VDIG-supply:
> > +    description:
> > +      Digital I/O voltage supply, 1.8 volts
> > +
> > +  VANA-supply:
> > +    description:
> > +      Analog voltage supply, 2.8 volts
> > +
> > +  VDDL-supply:
> > +    description:
> > +      Digital core voltage supply, 1.2 volts
> > +
> > +  reset-gpios:
> > +    description: |-
> > +      Reference to the GPIO connected to the xclr pin, if any.
> > +      Must be released (set high) after all supplies are applied.
> > +
> > +  # See ../video-interfaces.txt for more details
> > +  port:
> > +    type: object
> > +    properties:
> > +      endpoint:
> > +        type: object
> > +        properties:
> > +          data-lanes:
> > +            description: |-
> > +              The sensor supports either two-lane, or four-lane operation.
> > +              If this property is omitted four-lane operation is assumed.
> > +              For two-lane operation the property must be set to <1 2>.
> > +            items:
> > +              - const: 1
> > +              - const: 2
> > +
> > +          clock-noncontinuous:
> > +            type: boolean
> > +            description: |-
> > +              MIPI CSI-2 clock is non-continuous if this property is present,
> > +              otherwise it's continuous.
> > +
> > +          link-frequencies:
> > +            allOf:
> > +              - $ref: /schemas/types.yaml#/definitions/uint64-array
> > +            description:
> > +              Allowed data bus frequencies.
> > +
> > +        required:
> > +          - link-frequencies
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - clocks
> > +  - VANA-supply
> > +  - VDIG-supply
> > +  - VDDL-supply
> > +  - port
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  - |
> > +    i2c0 {
> > +        #address-cells = <1>;
> > +        #size-cells = <0>;
> > +
> > +        imx219: sensor@10 {
> > +            compatible = "sony,imx219";
> > +            reg = <0x10>;
> > +            clocks = <&imx219_clk>;
> > +            VANA-supply = <&imx219_vana>;   /* 2.8v */
> > +            VDIG-supply = <&imx219_vdig>;   /* 1.8v */
> > +            VDDL-supply = <&imx219_vddl>;   /* 1.2v */
> > +
> > +            port {
> > +                imx219_0: endpoint {
> > +                    remote-endpoint = <&csi1_ep>;
> > +                    data-lanes = <1 2>;
> > +                    clock-noncontinuous;
> > +                    link-frequencies = /bits/ 64 <456000000>;
> > +                };
> > +            };
> > +        };
> > +    };
> > +
> > +...
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index cf6ccca6e61c..aae83699e146 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -15351,6 +15351,14 @@ S:	Maintained
> >   F:	drivers/media/i2c/imx214.c
> >   F:	Documentation/devicetree/bindings/media/i2c/sony,imx214.txt
> > +SONY IMX219 SENSOR DRIVER
> > +M:	Dave Stevenson <dave.stevenson@raspberrypi.com>
> > +L:	linux-media@vger.kernel.org
> > +T:	git git://linuxtv.org/media_tree.git
> > +S:	Maintained
> > +F:	drivers/media/i2c/imx219.c
> > +F:	Documentation/devicetree/bindings/media/i2c/imx219.yaml
> > +
> >   SONY IMX258 SENSOR DRIVER
> >   M:	Sakari Ailus <sakari.ailus@linux.intel.com>
> >   L:	linux-media@vger.kernel.org
> > 

-- 
Regards,

Sakari Ailus

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

* Re: [PATCH v4 2/2] media: i2c: Add driver for Sony IMX219 sensor
  2020-01-20  8:15 ` [PATCH v4 2/2] media: i2c: Add driver for Sony IMX219 sensor Andrey Konovalov
@ 2020-01-21 16:47   ` Sakari Ailus
  0 siblings, 0 replies; 7+ messages in thread
From: Sakari Ailus @ 2020-01-21 16:47 UTC (permalink / raw)
  To: Andrey Konovalov
  Cc: mchehab, robh+dt, linux-media, devicetree, peter.griffin,
	dave.stevenson, ezequiel

On Mon, Jan 20, 2020 at 11:15:58AM +0300, Andrey Konovalov wrote:
> From: Dave Stevenson <dave.stevenson@raspberrypi.com>
> 
> Adds a driver for the 8MPix Sony IMX219 CSI2 sensor.
> Whilst the sensor supports 2 or 4 CSI2 data lanes, this driver
> currently only supports 2 lanes.
> 8MPix @ 15fps, 1080P @ 30fps (cropped FOV), and 1640x1232 (2x2 binned)
> @ 30fps are currently supported.
> 
> Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>

Applied the set with the following diff:

diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
index 8627a5903039..f1effb5a5f66 100644
--- a/drivers/media/i2c/imx219.c
+++ b/drivers/media/i2c/imx219.c
@@ -1122,7 +1122,7 @@ static void imx219_free_controls(struct imx219 *imx219)
 	mutex_destroy(&imx219->mutex);
 }
 
-int imx219_check_hwcfg(struct device *dev)
+static int imx219_check_hwcfg(struct device *dev)
 {
 	struct fwnode_handle *endpoint;
 	struct v4l2_fwnode_endpoint ep_cfg = {

> ---
>  drivers/media/i2c/Kconfig  |   11 +
>  drivers/media/i2c/Makefile |    1 +
>  drivers/media/i2c/imx219.c | 1312 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 1324 insertions(+)
>  create mode 100644 drivers/media/i2c/imx219.c
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index c68e002d26ea..87fba68542b5 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -591,6 +591,17 @@ config VIDEO_IMX214
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called imx214.
>  
> +config VIDEO_IMX219
> +	tristate "Sony IMX219 sensor support"
> +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	select V4L2_FWNODE
> +	help
> +	  This is a Video4Linux2 sensor driver for the Sony
> +	  IMX219 camera.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called imx219.
> +
>  config VIDEO_IMX258
>  	tristate "Sony IMX258 sensor support"
>  	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index c147bb9d28db..77bf7d0b691f 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -111,6 +111,7 @@ obj-$(CONFIG_VIDEO_OV2659)	+= ov2659.o
>  obj-$(CONFIG_VIDEO_TC358743)	+= tc358743.o
>  obj-$(CONFIG_VIDEO_HI556)	+= hi556.o
>  obj-$(CONFIG_VIDEO_IMX214)	+= imx214.o
> +obj-$(CONFIG_VIDEO_IMX219)	+= imx219.o
>  obj-$(CONFIG_VIDEO_IMX258)	+= imx258.o
>  obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
>  obj-$(CONFIG_VIDEO_IMX290)	+= imx290.o
> diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
> new file mode 100644
> index 000000000000..8627a5903039
> --- /dev/null
> +++ b/drivers/media/i2c/imx219.c
> @@ -0,0 +1,1312 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * A V4L2 driver for Sony IMX219 cameras.
> + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd
> + *
> + * Based on Sony imx258 camera driver
> + * Copyright (C) 2018 Intel Corporation
> + *
> + * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver
> + * Copyright 2018 Qtechnology A/S
> + *
> + * Flip handling taken from the Sony IMX319 driver.
> + * Copyright (C) 2018 Intel Corporation
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.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/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-mediabus.h>
> +#include <asm/unaligned.h>
> +
> +#define IMX219_REG_VALUE_08BIT		1
> +#define IMX219_REG_VALUE_16BIT		2
> +
> +#define IMX219_REG_MODE_SELECT		0x0100
> +#define IMX219_MODE_STANDBY		0x00
> +#define IMX219_MODE_STREAMING		0x01
> +
> +/* Chip ID */
> +#define IMX219_REG_CHIP_ID		0x0000
> +#define IMX219_CHIP_ID			0x0219
> +
> +/* External clock frequency is 24.0M */
> +#define IMX219_XCLK_FREQ		24000000
> +
> +/* Pixel rate is fixed at 182.4M for all the modes */
> +#define IMX219_PIXEL_RATE		182400000
> +
> +#define IMX219_DEFAULT_LINK_FREQ	456000000
> +
> +/* V_TIMING internal */
> +#define IMX219_REG_VTS			0x0160
> +#define IMX219_VTS_15FPS		0x0dc6
> +#define IMX219_VTS_30FPS_1080P		0x06e3
> +#define IMX219_VTS_30FPS_BINNED		0x06e3
> +#define IMX219_VTS_MAX			0xffff
> +
> +#define IMX219_VBLANK_MIN		4
> +
> +/*Frame Length Line*/
> +#define IMX219_FLL_MIN			0x08a6
> +#define IMX219_FLL_MAX			0xffff
> +#define IMX219_FLL_STEP			1
> +#define IMX219_FLL_DEFAULT		0x0c98
> +
> +/* HBLANK control - read only */
> +#define IMX219_PPL_DEFAULT		3448
> +
> +/* Exposure control */
> +#define IMX219_REG_EXPOSURE		0x015a
> +#define IMX219_EXPOSURE_MIN		4
> +#define IMX219_EXPOSURE_STEP		1
> +#define IMX219_EXPOSURE_DEFAULT		0x640
> +#define IMX219_EXPOSURE_MAX		65535
> +
> +/* Analog gain control */
> +#define IMX219_REG_ANALOG_GAIN		0x0157
> +#define IMX219_ANA_GAIN_MIN		0
> +#define IMX219_ANA_GAIN_MAX		232
> +#define IMX219_ANA_GAIN_STEP		1
> +#define IMX219_ANA_GAIN_DEFAULT		0x0
> +
> +/* Digital gain control */
> +#define IMX219_REG_DIGITAL_GAIN		0x0158
> +#define IMX219_DGTL_GAIN_MIN		0x0100
> +#define IMX219_DGTL_GAIN_MAX		0x0fff
> +#define IMX219_DGTL_GAIN_DEFAULT	0x0100
> +#define IMX219_DGTL_GAIN_STEP		1
> +
> +#define IMX219_REG_ORIENTATION		0x0172
> +
> +/* Test Pattern Control */
> +#define IMX219_REG_TEST_PATTERN		0x0600
> +#define IMX219_TEST_PATTERN_DISABLE	0
> +#define IMX219_TEST_PATTERN_SOLID_COLOR	1
> +#define IMX219_TEST_PATTERN_COLOR_BARS	2
> +#define IMX219_TEST_PATTERN_GREY_COLOR	3
> +#define IMX219_TEST_PATTERN_PN9		4
> +
> +/* Test pattern colour components */
> +#define IMX219_REG_TESTP_RED		0x0602
> +#define IMX219_REG_TESTP_GREENR		0x0604
> +#define IMX219_REG_TESTP_BLUE		0x0606
> +#define IMX219_REG_TESTP_GREENB		0x0608
> +#define IMX219_TESTP_COLOUR_MIN		0
> +#define IMX219_TESTP_COLOUR_MAX		0x03ff
> +#define IMX219_TESTP_COLOUR_STEP	1
> +#define IMX219_TESTP_RED_DEFAULT	IMX219_TESTP_COLOUR_MAX
> +#define IMX219_TESTP_GREENR_DEFAULT	0
> +#define IMX219_TESTP_BLUE_DEFAULT	0
> +#define IMX219_TESTP_GREENB_DEFAULT	0
> +
> +struct imx219_reg {
> +	u16 address;
> +	u8 val;
> +};
> +
> +struct imx219_reg_list {
> +	unsigned int num_of_regs;
> +	const struct imx219_reg *regs;
> +};
> +
> +/* Mode : resolution and related config&values */
> +struct imx219_mode {
> +	/* Frame width */
> +	unsigned int width;
> +	/* Frame height */
> +	unsigned int height;
> +
> +	/* V-timing */
> +	unsigned int vts_def;
> +
> +	/* Default register values */
> +	struct imx219_reg_list reg_list;
> +};
> +
> +/*
> + * Register sets lifted off the i2C interface from the Raspberry Pi firmware
> + * driver.
> + * 3280x2464 = mode 2, 1920x1080 = mode 1, and 1640x1232 = mode 4.
> + */
> +static const struct imx219_reg mode_3280x2464_regs[] = {
> +	{0x0100, 0x00},
> +	{0x30eb, 0x0c},
> +	{0x30eb, 0x05},
> +	{0x300a, 0xff},
> +	{0x300b, 0xff},
> +	{0x30eb, 0x05},
> +	{0x30eb, 0x09},
> +	{0x0114, 0x01},
> +	{0x0128, 0x00},
> +	{0x012a, 0x18},
> +	{0x012b, 0x00},
> +	{0x0164, 0x00},
> +	{0x0165, 0x00},
> +	{0x0166, 0x0c},
> +	{0x0167, 0xcf},
> +	{0x0168, 0x00},
> +	{0x0169, 0x00},
> +	{0x016a, 0x09},
> +	{0x016b, 0x9f},
> +	{0x016c, 0x0c},
> +	{0x016d, 0xd0},
> +	{0x016e, 0x09},
> +	{0x016f, 0xa0},
> +	{0x0170, 0x01},
> +	{0x0171, 0x01},
> +	{0x0174, 0x00},
> +	{0x0175, 0x00},
> +	{0x018c, 0x0a},
> +	{0x018d, 0x0a},
> +	{0x0301, 0x05},
> +	{0x0303, 0x01},
> +	{0x0304, 0x03},
> +	{0x0305, 0x03},
> +	{0x0306, 0x00},
> +	{0x0307, 0x39},
> +	{0x0309, 0x0a},
> +	{0x030b, 0x01},
> +	{0x030c, 0x00},
> +	{0x030d, 0x72},
> +	{0x0624, 0x0c},
> +	{0x0625, 0xd0},
> +	{0x0626, 0x09},
> +	{0x0627, 0xa0},
> +	{0x455e, 0x00},
> +	{0x471e, 0x4b},
> +	{0x4767, 0x0f},
> +	{0x4750, 0x14},
> +	{0x4540, 0x00},
> +	{0x47b4, 0x14},
> +	{0x4713, 0x30},
> +	{0x478b, 0x10},
> +	{0x478f, 0x10},
> +	{0x4793, 0x10},
> +	{0x4797, 0x0e},
> +	{0x479b, 0x0e},
> +	{0x0162, 0x0d},
> +	{0x0163, 0x78},
> +};
> +
> +static const struct imx219_reg mode_1920_1080_regs[] = {
> +	{0x0100, 0x00},
> +	{0x30eb, 0x05},
> +	{0x30eb, 0x0c},
> +	{0x300a, 0xff},
> +	{0x300b, 0xff},
> +	{0x30eb, 0x05},
> +	{0x30eb, 0x09},
> +	{0x0114, 0x01},
> +	{0x0128, 0x00},
> +	{0x012a, 0x18},
> +	{0x012b, 0x00},
> +	{0x0162, 0x0d},
> +	{0x0163, 0x78},
> +	{0x0164, 0x02},
> +	{0x0165, 0xa8},
> +	{0x0166, 0x0a},
> +	{0x0167, 0x27},
> +	{0x0168, 0x02},
> +	{0x0169, 0xb4},
> +	{0x016a, 0x06},
> +	{0x016b, 0xeb},
> +	{0x016c, 0x07},
> +	{0x016d, 0x80},
> +	{0x016e, 0x04},
> +	{0x016f, 0x38},
> +	{0x0170, 0x01},
> +	{0x0171, 0x01},
> +	{0x0174, 0x00},
> +	{0x0175, 0x00},
> +	{0x018c, 0x0a},
> +	{0x018d, 0x0a},
> +	{0x0301, 0x05},
> +	{0x0303, 0x01},
> +	{0x0304, 0x03},
> +	{0x0305, 0x03},
> +	{0x0306, 0x00},
> +	{0x0307, 0x39},
> +	{0x0309, 0x0a},
> +	{0x030b, 0x01},
> +	{0x030c, 0x00},
> +	{0x030d, 0x72},
> +	{0x0624, 0x07},
> +	{0x0625, 0x80},
> +	{0x0626, 0x04},
> +	{0x0627, 0x38},
> +	{0x455e, 0x00},
> +	{0x471e, 0x4b},
> +	{0x4767, 0x0f},
> +	{0x4750, 0x14},
> +	{0x4540, 0x00},
> +	{0x47b4, 0x14},
> +	{0x4713, 0x30},
> +	{0x478b, 0x10},
> +	{0x478f, 0x10},
> +	{0x4793, 0x10},
> +	{0x4797, 0x0e},
> +	{0x479b, 0x0e},
> +	{0x0162, 0x0d},
> +	{0x0163, 0x78},
> +};
> +
> +static const struct imx219_reg mode_1640_1232_regs[] = {
> +	{0x0100, 0x00},
> +	{0x30eb, 0x0c},
> +	{0x30eb, 0x05},
> +	{0x300a, 0xff},
> +	{0x300b, 0xff},
> +	{0x30eb, 0x05},
> +	{0x30eb, 0x09},
> +	{0x0114, 0x01},
> +	{0x0128, 0x00},
> +	{0x012a, 0x18},
> +	{0x012b, 0x00},
> +	{0x0164, 0x00},
> +	{0x0165, 0x00},
> +	{0x0166, 0x0c},
> +	{0x0167, 0xcf},
> +	{0x0168, 0x00},
> +	{0x0169, 0x00},
> +	{0x016a, 0x09},
> +	{0x016b, 0x9f},
> +	{0x016c, 0x06},
> +	{0x016d, 0x68},
> +	{0x016e, 0x04},
> +	{0x016f, 0xd0},
> +	{0x0170, 0x01},
> +	{0x0171, 0x01},
> +	{0x0174, 0x01},
> +	{0x0175, 0x01},
> +	{0x018c, 0x0a},
> +	{0x018d, 0x0a},
> +	{0x0301, 0x05},
> +	{0x0303, 0x01},
> +	{0x0304, 0x03},
> +	{0x0305, 0x03},
> +	{0x0306, 0x00},
> +	{0x0307, 0x39},
> +	{0x0309, 0x0a},
> +	{0x030b, 0x01},
> +	{0x030c, 0x00},
> +	{0x030d, 0x72},
> +	{0x0624, 0x06},
> +	{0x0625, 0x68},
> +	{0x0626, 0x04},
> +	{0x0627, 0xd0},
> +	{0x455e, 0x00},
> +	{0x471e, 0x4b},
> +	{0x4767, 0x0f},
> +	{0x4750, 0x14},
> +	{0x4540, 0x00},
> +	{0x47b4, 0x14},
> +	{0x4713, 0x30},
> +	{0x478b, 0x10},
> +	{0x478f, 0x10},
> +	{0x4793, 0x10},
> +	{0x4797, 0x0e},
> +	{0x479b, 0x0e},
> +	{0x0162, 0x0d},
> +	{0x0163, 0x78},
> +};
> +
> +static const char * const imx219_test_pattern_menu[] = {
> +	"Disabled",
> +	"Color Bars",
> +	"Solid Color",
> +	"Grey Color Bars",
> +	"PN9"
> +};
> +
> +static const int imx219_test_pattern_val[] = {
> +	IMX219_TEST_PATTERN_DISABLE,
> +	IMX219_TEST_PATTERN_COLOR_BARS,
> +	IMX219_TEST_PATTERN_SOLID_COLOR,
> +	IMX219_TEST_PATTERN_GREY_COLOR,
> +	IMX219_TEST_PATTERN_PN9,
> +};
> +
> +/* regulator supplies */
> +static const char * const imx219_supply_name[] = {
> +	/* Supplies can be enabled in any order */
> +	"VANA",  /* Analog (2.8V) supply */
> +	"VDIG",  /* Digital Core (1.8V) supply */
> +	"VDDL",  /* IF (1.2V) supply */
> +};
> +
> +#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name)
> +
> +/*
> + * Initialisation delay between XCLR low->high and the moment when the sensor
> + * can start capture (i.e. can leave software stanby) must be not less than:
> + *   t4 + max(t5, t6 + <time to initialize the sensor register over I2C>)
> + * where
> + *   t4 is fixed, and is max 200uS,
> + *   t5 is fixed, and is 6000uS,
> + *   t6 depends on the sensor external clock, and is max 32000 clock periods.
> + * As per sensor datasheet, the external clock must be from 6MHz to 27MHz.
> + * So for any acceptable external clock t6 is always within the range of
> + * 1185 to 5333 uS, and is always less than t5.
> + * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then
> + * initialize the sensor over I2C, and then exit the software standby.
> + *
> + * This start-up time can be optimized a bit more, if we start the writes
> + * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor
> + * initialization over I2C may complete before (t4+t5) expires, and we must
> + * ensure that capture is not started before (t4+t5).
> + *
> + * This delay doesn't account for the power supply startup time. If needed,
> + * this should be taken care of via the regulator framework. E.g. in the
> + * case of DT for regulator-fixed one should define the startup-delay-us
> + * property.
> + */
> +#define IMX219_XCLR_MIN_DELAY_US	6200
> +#define IMX219_XCLR_DELAY_RANGE_US	1000
> +
> +/* Mode configs */
> +static const struct imx219_mode supported_modes[] = {
> +	{
> +		/* 8MPix 15fps mode */
> +		.width = 3280,
> +		.height = 2464,
> +		.vts_def = IMX219_VTS_15FPS,
> +		.reg_list = {
> +			.num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
> +			.regs = mode_3280x2464_regs,
> +		},
> +	},
> +	{
> +		/* 1080P 30fps cropped */
> +		.width = 1920,
> +		.height = 1080,
> +		.vts_def = IMX219_VTS_30FPS_1080P,
> +		.reg_list = {
> +			.num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
> +			.regs = mode_1920_1080_regs,
> +		},
> +	},
> +	{
> +		/* 2x2 binned 30fps mode */
> +		.width = 1640,
> +		.height = 1232,
> +		.vts_def = IMX219_VTS_30FPS_BINNED,
> +		.reg_list = {
> +			.num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
> +			.regs = mode_1640_1232_regs,
> +		},
> +	},
> +};
> +
> +struct imx219 {
> +	struct v4l2_subdev sd;
> +	struct media_pad pad;
> +
> +	struct clk *xclk; /* system clock to IMX219 */
> +	u32 xclk_freq;
> +
> +	struct gpio_desc *reset_gpio;
> +	struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES];
> +
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	/* V4L2 Controls */
> +	struct v4l2_ctrl *pixel_rate;
> +	struct v4l2_ctrl *exposure;
> +	struct v4l2_ctrl *vflip;
> +	struct v4l2_ctrl *hflip;
> +	struct v4l2_ctrl *vblank;
> +	struct v4l2_ctrl *hblank;
> +
> +	/* Current mode */
> +	const struct imx219_mode *mode;
> +
> +	/*
> +	 * Mutex for serialized access:
> +	 * Protect sensor module set pad format and start/stop streaming safely.
> +	 */
> +	struct mutex mutex;
> +
> +	/* Streaming on/off */
> +	bool streaming;
> +};
> +
> +static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
> +{
> +	return container_of(_sd, struct imx219, sd);
> +}
> +
> +/* Read registers up to 2 at a time */
> +static int imx219_read_reg(struct imx219 *imx219, u16 reg, u32 len, u32 *val)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> +	struct i2c_msg msgs[2];
> +	u8 addr_buf[2] = { reg >> 8, reg & 0xff };
> +	u8 data_buf[4] = { 0, };
> +	int ret;
> +
> +	if (len > 4)
> +		return -EINVAL;
> +
> +	/* Write register address */
> +	msgs[0].addr = client->addr;
> +	msgs[0].flags = 0;
> +	msgs[0].len = ARRAY_SIZE(addr_buf);
> +	msgs[0].buf = addr_buf;
> +
> +	/* Read data from register */
> +	msgs[1].addr = client->addr;
> +	msgs[1].flags = I2C_M_RD;
> +	msgs[1].len = len;
> +	msgs[1].buf = &data_buf[4 - len];
> +
> +	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> +	if (ret != ARRAY_SIZE(msgs))
> +		return -EIO;
> +
> +	*val = get_unaligned_be32(data_buf);
> +
> +	return 0;
> +}
> +
> +/* Write registers up to 2 at a time */
> +static int imx219_write_reg(struct imx219 *imx219, u16 reg, u32 len, u32 val)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> +	u8 buf[6];
> +
> +	if (len > 4)
> +		return -EINVAL;
> +
> +	put_unaligned_be16(reg, buf);
> +	put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
> +	if (i2c_master_send(client, buf, len + 2) != len + 2)
> +		return -EIO;
> +
> +	return 0;
> +}
> +
> +/* Write a list of registers */
> +static int imx219_write_regs(struct imx219 *imx219,
> +			     const struct imx219_reg *regs, u32 len)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < len; i++) {
> +		ret = imx219_write_reg(imx219, regs[i].address, 1, regs[i].val);
> +		if (ret) {
> +			dev_err_ratelimited(&client->dev,
> +					    "Failed to write reg 0x%4.4x. error = %d\n",
> +					    regs[i].address, ret);
> +
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/* Get bayer order based on flip setting. */
> +static u32 imx219_get_format_code(struct imx219 *imx219)
> +{
> +	/*
> +	 * Only one bayer order is supported.
> +	 * It depends on the flip settings.
> +	 */
> +	static const u32 codes[2][2] = {
> +		{ MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, },
> +		{ MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, },
> +	};
> +
> +	lockdep_assert_held(&imx219->mutex);
> +	return codes[imx219->vflip->val][imx219->hflip->val];
> +}
> +
> +static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct imx219 *imx219 = to_imx219(sd);
> +	struct v4l2_mbus_framefmt *try_fmt =
> +		v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +
> +	mutex_lock(&imx219->mutex);
> +
> +	/* Initialize try_fmt */
> +	try_fmt->width = supported_modes[0].width;
> +	try_fmt->height = supported_modes[0].height;
> +	try_fmt->code = imx219_get_format_code(imx219);
> +	try_fmt->field = V4L2_FIELD_NONE;
> +
> +	mutex_unlock(&imx219->mutex);
> +
> +	return 0;
> +}
> +
> +static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct imx219 *imx219 =
> +		container_of(ctrl->handler, struct imx219, ctrl_handler);
> +	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> +	int ret;
> +
> +	if (ctrl->id == V4L2_CID_VBLANK) {
> +		int exposure_max, exposure_def;
> +
> +		/* Update max exposure while meeting expected vblanking */
> +		exposure_max = imx219->mode->height + ctrl->val - 4;
> +		exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
> +			exposure_max : IMX219_EXPOSURE_DEFAULT;
> +		__v4l2_ctrl_modify_range(imx219->exposure,
> +					 imx219->exposure->minimum,
> +					 exposure_max, imx219->exposure->step,
> +					 exposure_def);
> +	}
> +
> +	/*
> +	 * Applying V4L2 control value only happens
> +	 * when power is up for streaming
> +	 */
> +	if (pm_runtime_get_if_in_use(&client->dev) == 0)
> +		return 0;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_ANALOGUE_GAIN:
> +		ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN,
> +				       IMX219_REG_VALUE_08BIT, ctrl->val);
> +		break;
> +	case V4L2_CID_EXPOSURE:
> +		ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE,
> +				       IMX219_REG_VALUE_16BIT, ctrl->val);
> +		break;
> +	case V4L2_CID_DIGITAL_GAIN:
> +		ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN,
> +				       IMX219_REG_VALUE_16BIT, ctrl->val);
> +		break;
> +	case V4L2_CID_TEST_PATTERN:
> +		ret = imx219_write_reg(imx219, IMX219_REG_TEST_PATTERN,
> +				       IMX219_REG_VALUE_16BIT,
> +				       imx219_test_pattern_val[ctrl->val]);
> +		break;
> +	case V4L2_CID_HFLIP:
> +	case V4L2_CID_VFLIP:
> +		ret = imx219_write_reg(imx219, IMX219_REG_ORIENTATION, 1,
> +				       imx219->hflip->val |
> +				       imx219->vflip->val << 1);
> +		break;
> +	case V4L2_CID_VBLANK:
> +		ret = imx219_write_reg(imx219, IMX219_REG_VTS,
> +				       IMX219_REG_VALUE_16BIT,
> +				       imx219->mode->height + ctrl->val);
> +		break;
> +	case V4L2_CID_TEST_PATTERN_RED:
> +		ret = imx219_write_reg(imx219, IMX219_REG_TESTP_RED,
> +				       IMX219_REG_VALUE_16BIT, ctrl->val);
> +		break;
> +	case V4L2_CID_TEST_PATTERN_GREENR:
> +		ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENR,
> +				       IMX219_REG_VALUE_16BIT, ctrl->val);
> +		break;
> +	case V4L2_CID_TEST_PATTERN_BLUE:
> +		ret = imx219_write_reg(imx219, IMX219_REG_TESTP_BLUE,
> +				       IMX219_REG_VALUE_16BIT, ctrl->val);
> +		break;
> +	case V4L2_CID_TEST_PATTERN_GREENB:
> +		ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENB,
> +				       IMX219_REG_VALUE_16BIT, ctrl->val);
> +		break;
> +	default:
> +		dev_info(&client->dev,
> +			 "ctrl(id:0x%x,val:0x%x) is not handled\n",
> +			 ctrl->id, ctrl->val);
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	pm_runtime_put(&client->dev);
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops imx219_ctrl_ops = {
> +	.s_ctrl = imx219_set_ctrl,
> +};
> +
> +static int imx219_enum_mbus_code(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct imx219 *imx219 = to_imx219(sd);
> +
> +	/*
> +	 * Only one bayer order is supported (though it depends on the flip
> +	 * settings)
> +	 */
> +	if (code->index > 0)
> +		return -EINVAL;
> +
> +	code->code = imx219_get_format_code(imx219);
> +
> +	return 0;
> +}
> +
> +static int imx219_enum_frame_size(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_pad_config *cfg,
> +				  struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct imx219 *imx219 = to_imx219(sd);
> +
> +	if (fse->index >= ARRAY_SIZE(supported_modes))
> +		return -EINVAL;
> +
> +	if (fse->code != imx219_get_format_code(imx219))
> +		return -EINVAL;
> +
> +	fse->min_width = supported_modes[fse->index].width;
> +	fse->max_width = fse->min_width;
> +	fse->min_height = supported_modes[fse->index].height;
> +	fse->max_height = fse->min_height;
> +
> +	return 0;
> +}
> +
> +static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
> +{
> +	fmt->colorspace = V4L2_COLORSPACE_SRGB;
> +	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
> +	fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
> +							  fmt->colorspace,
> +							  fmt->ycbcr_enc);
> +	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
> +}
> +
> +static void imx219_update_pad_format(struct imx219 *imx219,
> +				     const struct imx219_mode *mode,
> +				     struct v4l2_subdev_format *fmt)
> +{
> +	fmt->format.width = mode->width;
> +	fmt->format.height = mode->height;
> +	fmt->format.code = imx219_get_format_code(imx219);
> +	fmt->format.field = V4L2_FIELD_NONE;
> +
> +	imx219_reset_colorspace(&fmt->format);
> +}
> +
> +static int __imx219_get_pad_format(struct imx219 *imx219,
> +				   struct v4l2_subdev_pad_config *cfg,
> +				   struct v4l2_subdev_format *fmt)
> +{
> +	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> +		struct v4l2_mbus_framefmt *try_fmt =
> +			v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad);
> +		/* update the code which could change due to vflip or hflip: */
> +		try_fmt->code = imx219_get_format_code(imx219);
> +		fmt->format = *try_fmt;
> +	} else {
> +		imx219_update_pad_format(imx219, imx219->mode, fmt);
> +	}
> +
> +	return 0;
> +}
> +
> +static int imx219_get_pad_format(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_format *fmt)
> +{
> +	struct imx219 *imx219 = to_imx219(sd);
> +	int ret;
> +
> +	mutex_lock(&imx219->mutex);
> +	ret = __imx219_get_pad_format(imx219, cfg, fmt);
> +	mutex_unlock(&imx219->mutex);
> +
> +	return ret;
> +}
> +
> +static int imx219_set_pad_format(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_format *fmt)
> +{
> +	struct imx219 *imx219 = to_imx219(sd);
> +	const struct imx219_mode *mode;
> +	struct v4l2_mbus_framefmt *framefmt;
> +	int exposure_max, exposure_def, hblank;
> +
> +	mutex_lock(&imx219->mutex);
> +
> +	/* Bayer order varies with flips */
> +	fmt->format.code = imx219_get_format_code(imx219);
> +
> +	mode = v4l2_find_nearest_size(supported_modes,
> +				      ARRAY_SIZE(supported_modes),
> +				      width, height,
> +				      fmt->format.width, fmt->format.height);
> +	imx219_update_pad_format(imx219, mode, fmt);
> +	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
> +		framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
> +		*framefmt = fmt->format;
> +	} else if (imx219->mode != mode) {
> +		imx219->mode = mode;
> +		/* Update limits and set FPS to default */
> +		__v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
> +					 IMX219_VTS_MAX - mode->height, 1,
> +					 mode->vts_def - mode->height);
> +		__v4l2_ctrl_s_ctrl(imx219->vblank,
> +				   mode->vts_def - mode->height);
> +		/* Update max exposure while meeting expected vblanking */
> +		exposure_max = mode->vts_def - 4;
> +		exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
> +			exposure_max : IMX219_EXPOSURE_DEFAULT;
> +		__v4l2_ctrl_modify_range(imx219->exposure,
> +					 imx219->exposure->minimum,
> +					 exposure_max, imx219->exposure->step,
> +					 exposure_def);
> +		/*
> +		 * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
> +		 * depends on mode->width only, and is not changeble in any
> +		 * way other than changing the mode.
> +		 */
> +		hblank = IMX219_PPL_DEFAULT - mode->width;
> +		__v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
> +					 hblank);
> +	}
> +
> +	mutex_unlock(&imx219->mutex);
> +
> +	return 0;
> +}
> +
> +static int imx219_start_streaming(struct imx219 *imx219)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> +	const struct imx219_reg_list *reg_list;
> +	int ret;
> +
> +	/* Apply default values of current mode */
> +	reg_list = &imx219->mode->reg_list;
> +	ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
> +	if (ret) {
> +		dev_err(&client->dev, "%s failed to set mode\n", __func__);
> +		return ret;
> +	}
> +
> +	/* Apply customized values from user */
> +	ret =  __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
> +	if (ret)
> +		return ret;
> +
> +	/* set stream on register */
> +	return imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
> +				IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
> +}
> +
> +static void imx219_stop_streaming(struct imx219 *imx219)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> +	int ret;
> +
> +	/* set stream off register */
> +	ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
> +			       IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
> +	if (ret)
> +		dev_err(&client->dev, "%s failed to set stream\n", __func__);
> +}
> +
> +static int imx219_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct imx219 *imx219 = to_imx219(sd);
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	int ret = 0;
> +
> +	mutex_lock(&imx219->mutex);
> +	if (imx219->streaming == enable) {
> +		mutex_unlock(&imx219->mutex);
> +		return 0;
> +	}
> +
> +	if (enable) {
> +		ret = pm_runtime_get_sync(&client->dev);
> +		if (ret < 0) {
> +			pm_runtime_put_noidle(&client->dev);
> +			goto err_unlock;
> +		}
> +
> +		/*
> +		 * Apply default & customized values
> +		 * and then start streaming.
> +		 */
> +		ret = imx219_start_streaming(imx219);
> +		if (ret)
> +			goto err_rpm_put;
> +	} else {
> +		imx219_stop_streaming(imx219);
> +		pm_runtime_put(&client->dev);
> +	}
> +
> +	imx219->streaming = enable;
> +
> +	/* vflip and hflip cannot change during streaming */
> +	__v4l2_ctrl_grab(imx219->vflip, enable);
> +	__v4l2_ctrl_grab(imx219->hflip, enable);
> +
> +	mutex_unlock(&imx219->mutex);
> +
> +	return ret;
> +
> +err_rpm_put:
> +	pm_runtime_put(&client->dev);
> +err_unlock:
> +	mutex_unlock(&imx219->mutex);
> +
> +	return ret;
> +}
> +
> +/* Power/clock management functions */
> +static int imx219_power_on(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct imx219 *imx219 = to_imx219(sd);
> +	int ret;
> +
> +	ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES,
> +				    imx219->supplies);
> +	if (ret) {
> +		dev_err(&client->dev, "%s: failed to enable regulators\n",
> +			__func__);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(imx219->xclk);
> +	if (ret) {
> +		dev_err(&client->dev, "%s: failed to enable clock\n",
> +			__func__);
> +		goto reg_off;
> +	}
> +
> +	gpiod_set_value_cansleep(imx219->reset_gpio, 1);
> +	usleep_range(IMX219_XCLR_MIN_DELAY_US,
> +		     IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
> +
> +	return 0;
> +
> +reg_off:
> +	regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
> +
> +	return ret;
> +}
> +
> +static int imx219_power_off(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct imx219 *imx219 = to_imx219(sd);
> +
> +	gpiod_set_value_cansleep(imx219->reset_gpio, 0);
> +	regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
> +	clk_disable_unprepare(imx219->xclk);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused imx219_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct imx219 *imx219 = to_imx219(sd);
> +
> +	if (imx219->streaming)
> +		imx219_stop_streaming(imx219);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused imx219_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct imx219 *imx219 = to_imx219(sd);
> +	int ret;
> +
> +	if (imx219->streaming) {
> +		ret = imx219_start_streaming(imx219);
> +		if (ret)
> +			goto error;
> +	}
> +
> +	return 0;
> +
> +error:
> +	imx219_stop_streaming(imx219);
> +	imx219->streaming = 0;
> +
> +	return ret;
> +}
> +
> +static int imx219_get_regulators(struct imx219 *imx219)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> +	unsigned int i;
> +
> +	for (i = 0; i < IMX219_NUM_SUPPLIES; i++)
> +		imx219->supplies[i].supply = imx219_supply_name[i];
> +
> +	return devm_regulator_bulk_get(&client->dev,
> +				       IMX219_NUM_SUPPLIES,
> +				       imx219->supplies);
> +}
> +
> +/* Verify chip ID */
> +static int imx219_identify_module(struct imx219 *imx219)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> +	int ret;
> +	u32 val;
> +
> +	ret = imx219_read_reg(imx219, IMX219_REG_CHIP_ID,
> +			      IMX219_REG_VALUE_16BIT, &val);
> +	if (ret) {
> +		dev_err(&client->dev, "failed to read chip id %x\n",
> +			IMX219_CHIP_ID);
> +		return ret;
> +	}
> +
> +	if (val != IMX219_CHIP_ID) {
> +		dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
> +			IMX219_CHIP_ID, val);
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_core_ops imx219_core_ops = {
> +	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
> +	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
> +};
> +
> +static const struct v4l2_subdev_video_ops imx219_video_ops = {
> +	.s_stream = imx219_set_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops imx219_pad_ops = {
> +	.enum_mbus_code = imx219_enum_mbus_code,
> +	.get_fmt = imx219_get_pad_format,
> +	.set_fmt = imx219_set_pad_format,
> +	.enum_frame_size = imx219_enum_frame_size,
> +};
> +
> +static const struct v4l2_subdev_ops imx219_subdev_ops = {
> +	.core = &imx219_core_ops,
> +	.video = &imx219_video_ops,
> +	.pad = &imx219_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
> +	.open = imx219_open,
> +};
> +
> +/* Initialize control handlers */
> +static int imx219_init_controls(struct imx219 *imx219)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
> +	struct v4l2_ctrl_handler *ctrl_hdlr;
> +	unsigned int height = imx219->mode->height;
> +	int exposure_max, exposure_def, hblank;
> +	int i, ret;
> +
> +	ctrl_hdlr = &imx219->ctrl_handler;
> +	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
> +	if (ret)
> +		return ret;
> +
> +	mutex_init(&imx219->mutex);
> +	ctrl_hdlr->lock = &imx219->mutex;
> +
> +	/* By default, PIXEL_RATE is read only */
> +	imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
> +					       V4L2_CID_PIXEL_RATE,
> +					       IMX219_PIXEL_RATE,
> +					       IMX219_PIXEL_RATE, 1,
> +					       IMX219_PIXEL_RATE);
> +
> +	/* Initial vblank/hblank/exposure parameters based on current mode */
> +	imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
> +					   V4L2_CID_VBLANK, IMX219_VBLANK_MIN,
> +					   IMX219_VTS_MAX - height, 1,
> +					   imx219->mode->vts_def - height);
> +	hblank = IMX219_PPL_DEFAULT - imx219->mode->width;
> +	imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
> +					   V4L2_CID_HBLANK, hblank, hblank,
> +					   1, hblank);
> +	if (imx219->hblank)
> +		imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +	exposure_max = imx219->mode->vts_def - 4;
> +	exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
> +		exposure_max : IMX219_EXPOSURE_DEFAULT;
> +	imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
> +					     V4L2_CID_EXPOSURE,
> +					     IMX219_EXPOSURE_MIN, exposure_max,
> +					     IMX219_EXPOSURE_STEP,
> +					     exposure_def);
> +
> +	v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
> +			  IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX,
> +			  IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT);
> +
> +	v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
> +			  IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX,
> +			  IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT);
> +
> +	imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
> +					  V4L2_CID_HFLIP, 0, 1, 1, 0);
> +	if (imx219->hflip)
> +		imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> +
> +	imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
> +					  V4L2_CID_VFLIP, 0, 1, 1, 0);
> +	if (imx219->vflip)
> +		imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> +
> +	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops,
> +				     V4L2_CID_TEST_PATTERN,
> +				     ARRAY_SIZE(imx219_test_pattern_menu) - 1,
> +				     0, 0, imx219_test_pattern_menu);
> +	for (i = 0; i < 4; i++) {
> +		/*
> +		 * The assumption is that
> +		 * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
> +		 * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
> +		 * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
> +		 */
> +		v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
> +				  V4L2_CID_TEST_PATTERN_RED + i,
> +				  IMX219_TESTP_COLOUR_MIN,
> +				  IMX219_TESTP_COLOUR_MAX,
> +				  IMX219_TESTP_COLOUR_STEP,
> +				  IMX219_TESTP_COLOUR_MAX);
> +		/* The "Solid color" pattern is white by default */
> +	}
> +
> +	if (ctrl_hdlr->error) {
> +		ret = ctrl_hdlr->error;
> +		dev_err(&client->dev, "%s control init failed (%d)\n",
> +			__func__, ret);
> +		goto error;
> +	}
> +
> +	imx219->sd.ctrl_handler = ctrl_hdlr;
> +
> +	return 0;
> +
> +error:
> +	v4l2_ctrl_handler_free(ctrl_hdlr);
> +	mutex_destroy(&imx219->mutex);
> +
> +	return ret;
> +}
> +
> +static void imx219_free_controls(struct imx219 *imx219)
> +{
> +	v4l2_ctrl_handler_free(imx219->sd.ctrl_handler);
> +	mutex_destroy(&imx219->mutex);
> +}
> +
> +int imx219_check_hwcfg(struct device *dev)
> +{
> +	struct fwnode_handle *endpoint;
> +	struct v4l2_fwnode_endpoint ep_cfg = {
> +		.bus_type = V4L2_MBUS_CSI2_DPHY
> +	};
> +	int ret = -EINVAL;
> +
> +	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
> +	if (!endpoint) {
> +		dev_err(dev, "endpoint node not found\n");
> +		return -EINVAL;
> +	}
> +
> +	if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
> +		dev_err(dev, "could not parse endpoint\n");
> +		goto error_out;
> +	}
> +
> +	/* Check the number of MIPI CSI2 data lanes */
> +	if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
> +		dev_err(dev, "only 2 data lanes are currently supported\n");
> +		goto error_out;
> +	}
> +
> +	/* Check the link frequency set in device tree */
> +	if (!ep_cfg.nr_of_link_frequencies) {
> +		dev_err(dev, "link-frequency property not found in DT\n");
> +		goto error_out;
> +	}
> +
> +	if (ep_cfg.nr_of_link_frequencies != 1 ||
> +	    ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
> +		dev_err(dev, "Link frequency not supported: %lld\n",
> +			ep_cfg.link_frequencies[0]);
> +		goto error_out;
> +	}
> +
> +	ret = 0;
> +
> +error_out:
> +	v4l2_fwnode_endpoint_free(&ep_cfg);
> +	fwnode_handle_put(endpoint);
> +
> +	return ret;
> +}
> +
> +static int imx219_probe(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct imx219 *imx219;
> +	int ret;
> +
> +	imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
> +	if (!imx219)
> +		return -ENOMEM;
> +
> +	v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
> +
> +	/* Check the hardware configuration in device tree */
> +	if (imx219_check_hwcfg(dev))
> +		return -EINVAL;
> +
> +	/* Get system clock (xclk) */
> +	imx219->xclk = devm_clk_get(dev, NULL);
> +	if (IS_ERR(imx219->xclk)) {
> +		dev_err(dev, "failed to get xclk\n");
> +		return PTR_ERR(imx219->xclk);
> +	}
> +
> +	imx219->xclk_freq = clk_get_rate(imx219->xclk);
> +	if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
> +		dev_err(dev, "xclk frequency not supported: %d Hz\n",
> +			imx219->xclk_freq);
> +		return -EINVAL;
> +	}
> +
> +	ret = imx219_get_regulators(imx219);
> +	if (ret) {
> +		dev_err(dev, "failed to get regulators\n");
> +		return ret;
> +	}
> +
> +	/* Request optional enable pin */
> +	imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
> +						     GPIOD_OUT_HIGH);
> +
> +	/*
> +	 * The sensor must be powered for imx219_identify_module()
> +	 * to be able to read the CHIP_ID register
> +	 */
> +	ret = imx219_power_on(dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = imx219_identify_module(imx219);
> +	if (ret)
> +		goto error_power_off;
> +
> +	/* Set default mode to max resolution */
> +	imx219->mode = &supported_modes[0];
> +
> +	ret = imx219_init_controls(imx219);
> +	if (ret)
> +		goto error_power_off;
> +
> +	/* Initialize subdev */
> +	imx219->sd.internal_ops = &imx219_internal_ops;
> +	imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +
> +	/* Initialize source pad */
> +	imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
> +
> +	ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
> +	if (ret) {
> +		dev_err(dev, "failed to init entity pads: %d\n", ret);
> +		goto error_handler_free;
> +	}
> +
> +	ret = v4l2_async_register_subdev_sensor_common(&imx219->sd);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
> +		goto error_media_entity;
> +	}
> +
> +	/* Enable runtime PM and turn off the device */
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
> +	pm_runtime_idle(dev);
> +
> +	return 0;
> +
> +error_media_entity:
> +	media_entity_cleanup(&imx219->sd.entity);
> +
> +error_handler_free:
> +	imx219_free_controls(imx219);
> +
> +error_power_off:
> +	imx219_power_off(dev);
> +
> +	return ret;
> +}
> +
> +static int imx219_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct imx219 *imx219 = to_imx219(sd);
> +
> +	v4l2_async_unregister_subdev(sd);
> +	media_entity_cleanup(&sd->entity);
> +	imx219_free_controls(imx219);
> +
> +	pm_runtime_disable(&client->dev);
> +	if (!pm_runtime_status_suspended(&client->dev))
> +		imx219_power_off(&client->dev);
> +	pm_runtime_set_suspended(&client->dev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id imx219_dt_ids[] = {
> +	{ .compatible = "sony,imx219" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, imx219_dt_ids);
> +
> +static const struct dev_pm_ops imx219_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume)
> +	SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL)
> +};
> +
> +static struct i2c_driver imx219_i2c_driver = {
> +	.driver = {
> +		.name = "imx219",
> +		.of_match_table	= imx219_dt_ids,
> +		.pm = &imx219_pm_ops,
> +	},
> +	.probe_new = imx219_probe,
> +	.remove = imx219_remove,
> +};
> +
> +module_i2c_driver(imx219_i2c_driver);
> +
> +MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com");
> +MODULE_DESCRIPTION("Sony IMX219 sensor driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 2.17.1
> 

-- 
Sakari Ailus

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

* Re: [PATCH v4 1/2] dt-bindings: media: i2c: Add IMX219 CMOS sensor binding
  2020-01-20 11:32   ` Andrey Konovalov
  2020-01-20 11:48     ` Sakari Ailus
@ 2020-01-21 21:45     ` Rob Herring
  1 sibling, 0 replies; 7+ messages in thread
From: Rob Herring @ 2020-01-21 21:45 UTC (permalink / raw)
  To: Andrey Konovalov
  Cc: Mauro Carvalho Chehab, Linux Media Mailing List, devicetree,
	Sakari Ailus, Peter Griffin, dave.stevenson, Ezequiel Garcia

On Mon, Jan 20, 2020 at 5:32 AM Andrey Konovalov
<andrey.konovalov@linaro.org> wrote:
>
> v3 of this patch got "Reviewed-by" from Rob Herring.
> But unfortunately, as one more property had to be added afterwards, my understanding
> is that that "Reviewed-by" doesn't apply to v4.

It's fine to keep it.

Rob

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

end of thread, other threads:[~2020-01-21 21:45 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-20  8:15 [PATCH v4 0/2] Add IMX219 CMOS image sensor support Andrey Konovalov
2020-01-20  8:15 ` [PATCH v4 1/2] dt-bindings: media: i2c: Add IMX219 CMOS sensor binding Andrey Konovalov
2020-01-20 11:32   ` Andrey Konovalov
2020-01-20 11:48     ` Sakari Ailus
2020-01-21 21:45     ` Rob Herring
2020-01-20  8:15 ` [PATCH v4 2/2] media: i2c: Add driver for Sony IMX219 sensor Andrey Konovalov
2020-01-21 16:47   ` Sakari Ailus

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.