Linux Media Controller development
 help / color / mirror / Atom feed
* [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support
@ 2026-09-02 20:45 Peter Marshall
  2026-09-02 20:45 ` [PATCH 1/7] media: i2c: vd55g: move patch array to a separate firmware header Peter Marshall
                   ` (7 more replies)
  0 siblings, 8 replies; 14+ messages in thread
From: Peter Marshall @ 2026-09-02 20:45 UTC (permalink / raw)
  To: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot
  Cc: linux-kernel, devicetree, linux-media, platform-driver-x86,
	Peter Marshall, Conor Dooley, Daniel Scally, Hans de Goede,
	Ilpo Järvinen, Krzysztof Kozlowski, Rob Herring

This patch series refactors the STMicroelectronics VD55G1 and VD65G4 image
sensor driver (vd55g1) into a generic VD55G family driver (vd55g) and adds
support for the VD55G0 monochrome sensor.

The VD55G0 sensor is found in devices such as the Microsoft Surface Pro 9 under
ACPI HID `SMO55F0`. It features a native resolution of 644 x 604 pixels and
differs from later variants in the family in several ways:

* It uses a different native resolution and register addresses mapping,
  requiring specification of generational feature differences.
* It lacks support for spatial and temporal HDR.
* It requires an exposure duty cycle limit to keep the illuminator current safe.

Tested on a Microsoft Surface Pro 9 (an Intel IPU6 platform with ACPI
HID "SMO55F0") running libcamera.

Peter Marshall (7):
  media: i2c: vd55g: move patch array to a separate firmware header
  dt-bindings: media: i2c: st,vd55g: rename and add VD55G0 variant
  media: ipu-bridge: Add VD55G0 to the list of supported sensors
  platform/x86: int3472: Add VD55G0 supply GPIO mapping
  media: i2c: st-vd55g: Add generic driver and VD55G0 support
  media: i2c: st-vd55g: Remove legacy vd55g1 remnants
  MAINTAINERS: Update ST VD55G camera driver entry

 .../i2c/{st,vd55g1.yaml => st,vd55g.yaml}     |   30 +-
 MAINTAINERS                                   |    7 +-
 drivers/media/i2c/Kconfig                     |   11 +
 drivers/media/i2c/Makefile                    |    2 +-
 drivers/media/i2c/vd55g-fw.h                  |  305 +++
 drivers/media/i2c/vd55g.c                     | 2166 +++++++++++++++++
 drivers/media/i2c/vd55g1.c                    | 2061 ----------------
 drivers/media/pci/intel/ipu-bridge.c          |    2 +
 drivers/platform/x86/intel/int3472/discrete.c |   18 +
 9 files changed, 2531 insertions(+), 2071 deletions(-)
 rename Documentation/devicetree/bindings/media/i2c/{st,vd55g1.yaml => st,vd55g.yaml} (76%)
 create mode 100644 drivers/media/i2c/vd55g-fw.h
 create mode 100644 drivers/media/i2c/vd55g.c
 delete mode 100644 drivers/media/i2c/vd55g1.c

-- 
2.55.0


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

* [PATCH 1/7] media: i2c: vd55g: move patch array to a separate firmware header
  2026-09-02 20:45 [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Peter Marshall
@ 2026-09-02 20:45 ` Peter Marshall
  2026-09-03 13:37   ` Krzysztof Kozlowski
  2026-09-02 20:45 ` [PATCH 2/7] dt-bindings: media: i2c: st,vd55g: rename and add VD55G0 variant Peter Marshall
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 14+ messages in thread
From: Peter Marshall @ 2026-09-02 20:45 UTC (permalink / raw)
  To: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot
  Cc: linux-kernel, devicetree, linux-media, platform-driver-x86,
	Peter Marshall

A firmware patch consisting of an array of bytes is uploaded to the chip
during its boot sequence. Declutter the driver source by moving it to a
separate header file.

Signed-off-by: Peter Marshall <pm@petermarshall.ca>
---
 drivers/media/i2c/vd55g-fw.h | 305 +++++++++++++++++++++++++++++++++++
 drivers/media/i2c/vd55g1.c   | 296 +--------------------------------
 2 files changed, 306 insertions(+), 295 deletions(-)
 create mode 100644 drivers/media/i2c/vd55g-fw.h

diff --git a/drivers/media/i2c/vd55g-fw.h b/drivers/media/i2c/vd55g-fw.h
new file mode 100644
index 000000000..02faed7e4
--- /dev/null
+++ b/drivers/media/i2c/vd55g-fw.h
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Driver for VD55G1 global shutter sensor family driver
+ *
+ * Copyright (C) 2025 STMicroelectronics SA
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+static const u8 vd55g1_patch_array[] = {
+	0x44, 0x03, 0x09, 0x02, 0xe6, 0x01, 0x42, 0x00, 0xea, 0x01, 0x42, 0x00,
+	0xf0, 0x01, 0x42, 0x00, 0xe6, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0xfa, 0x68, 0x40, 0x00, 0xe8,
+	0x09, 0xbe, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x1c, 0x00, 0xc0, 0xe2,
+	0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x80, 0x98, 0x7f,
+	0xfc, 0xef, 0x11, 0xc1, 0x0f, 0x82, 0x69, 0xbe, 0x0f, 0xac, 0x58, 0x40,
+	0x00, 0xe8, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0x1c, 0x00, 0x40, 0xe3,
+	0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x84, 0xfa, 0x46, 0x0e, 0xe8, 0xe0,
+	0x08, 0xde, 0x4a, 0x40, 0x84, 0xe0, 0xa5, 0x86, 0xa8, 0x7d, 0xfc, 0xef,
+	0x6b, 0x80, 0x01, 0xbf, 0x28, 0x77, 0x0c, 0xef, 0x0b, 0x0e, 0x21, 0x78,
+	0x06, 0xc0, 0x0b, 0xa5, 0xb5, 0x84, 0x06, 0x42, 0x98, 0xe1, 0x01, 0x81,
+	0x01, 0x42, 0x38, 0xe0, 0x0c, 0xc4, 0x0e, 0x84, 0x46, 0x02, 0x84, 0xe0,
+	0x0c, 0x84, 0x11, 0x81, 0x21, 0x81, 0x31, 0x81, 0x41, 0x81, 0x51, 0x81,
+	0xc1, 0x81, 0x05, 0x83, 0x0c, 0x0c, 0x84, 0xf2, 0x93, 0xdd, 0x06, 0x40,
+	0x98, 0xe1, 0xc8, 0x80, 0x58, 0x82, 0x48, 0xc0, 0x38, 0xc2, 0x29, 0x00,
+	0x10, 0xe0, 0x19, 0x00, 0x14, 0xe0, 0x09, 0x00, 0x38, 0xe0, 0x5f, 0xb8,
+	0x5f, 0xa8, 0x5f, 0xa6, 0x5f, 0xa4, 0x5f, 0xa2, 0x5f, 0xa0, 0x56, 0x41,
+	0x98, 0xe1, 0x18, 0x82, 0x28, 0x80, 0x38, 0xc0, 0x5f, 0xa2, 0x19, 0x00,
+	0x20, 0xf8, 0x5f, 0xa4, 0x28, 0xc2, 0x5f, 0xa6, 0x39, 0x00, 0x10, 0xe0,
+	0x5f, 0xa2, 0x19, 0x00, 0x14, 0xe0, 0x5f, 0xa4, 0x29, 0x00, 0x18, 0xe0,
+	0x5f, 0xa6, 0x39, 0x00, 0x40, 0xe0, 0x5f, 0xa2, 0x19, 0x00, 0x44, 0xe0,
+	0x5f, 0xa4, 0x29, 0x00, 0x1c, 0xe0, 0x5f, 0xa6, 0x39, 0x00, 0x38, 0xe0,
+	0x5f, 0xa2, 0x19, 0x00, 0x20, 0xe0, 0x5f, 0xa4, 0x29, 0x00, 0x24, 0xe0,
+	0x5f, 0xa6, 0x39, 0x00, 0x28, 0xe0, 0x5f, 0xa2, 0x19, 0x00, 0x2c, 0xe0,
+	0x5f, 0xa4, 0x29, 0x00, 0x30, 0xe0, 0x5f, 0xa6, 0x09, 0x00, 0x34, 0xe0,
+	0x5f, 0xa2, 0x5f, 0xa4, 0x5f, 0xa0, 0x4a, 0x0a, 0xfc, 0xfb, 0xe5, 0x82,
+	0x08, 0xde, 0x4a, 0x40, 0x88, 0xe0, 0xf6, 0x40, 0x00, 0xe0, 0x01, 0x4e,
+	0x99, 0x78, 0x0a, 0xc0, 0x85, 0x80, 0x98, 0x40, 0x00, 0xe8, 0x35, 0x81,
+	0xa8, 0x40, 0x00, 0xe8, 0x0b, 0x8c, 0x0c, 0x0c, 0x84, 0xf2, 0xd5, 0xed,
+	0x83, 0xc1, 0x13, 0xc5, 0x93, 0xdd, 0xc3, 0xc1, 0x83, 0xc1, 0x13, 0xc3,
+	0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0xc6, 0x0f, 0x94, 0xe0,
+	0x19, 0x0e, 0xc9, 0x65, 0x01, 0xc0, 0x28, 0xde, 0x0a, 0x42, 0x80, 0xe0,
+	0x24, 0x02, 0x00, 0xfc, 0x16, 0xde, 0xa5, 0x8a, 0x19, 0x00, 0xb8, 0xe0,
+	0x10, 0x02, 0x0c, 0xec, 0x1d, 0xe6, 0x14, 0x02, 0x88, 0x80, 0x4e, 0x04,
+	0x01, 0x00, 0x10, 0x80, 0x25, 0x02, 0x08, 0x9c, 0x86, 0x02, 0x00, 0x80,
+	0x08, 0x44, 0x00, 0x98, 0x55, 0x81, 0x11, 0x85, 0x45, 0x81, 0x11, 0x89,
+	0x25, 0x81, 0x11, 0x83, 0x2b, 0x00, 0x24, 0xe0, 0x64, 0xc2, 0x0b, 0x84,
+	0x08, 0x51, 0x00, 0xef, 0x2b, 0x80, 0x01, 0x83, 0x1b, 0x8c, 0x38, 0x7d,
+	0x5c, 0xef, 0x18, 0xde, 0x0b, 0xa1, 0x25, 0x82, 0x0b, 0x0e, 0x88, 0xf9,
+	0x0a, 0x00, 0x00, 0xe8, 0x10, 0x42, 0x04, 0x9c, 0x11, 0x4e, 0x0c, 0x80,
+	0x10, 0x40, 0x04, 0xf0, 0x4e, 0x05, 0x01, 0x60, 0x10, 0xc0, 0x06, 0x88,
+	0x10, 0x40, 0xf8, 0xf3, 0x06, 0xde, 0x4c, 0x0c, 0x04, 0xf2, 0x93, 0xdd,
+	0x0c, 0x04, 0x1c, 0xfe, 0xf6, 0x0f, 0x94, 0xe0, 0x38, 0x9c, 0x46, 0x51,
+	0xfc, 0xe0, 0x46, 0x49, 0x38, 0xe2, 0x30, 0x46, 0xf8, 0xf3, 0x36, 0x9c,
+	0xc6, 0x46, 0x0c, 0xe1, 0x34, 0x8c, 0x94, 0xa0, 0x4e, 0xa0, 0x39, 0x06,
+	0x80, 0xe0, 0x4a, 0x46, 0x94, 0xe0, 0x05, 0x8c, 0x6a, 0x40, 0x80, 0xe0,
+	0x2c, 0x0c, 0x00, 0xe2, 0x0b, 0x8c, 0xb8, 0x7c, 0x5c, 0xef, 0x0b, 0x8c,
+	0x9e, 0xa0, 0xf8, 0x40, 0x60, 0xef, 0x0b, 0xa1, 0x5a, 0x40, 0x80, 0xe0,
+	0x65, 0x88, 0x28, 0x02, 0x01, 0x40, 0x00, 0x80, 0x2a, 0x42, 0x9c, 0xe1,
+	0x28, 0x49, 0x60, 0xef, 0x96, 0x4d, 0x9c, 0xe1, 0x01, 0x81, 0x06, 0x98,
+	0xd5, 0x81, 0x09, 0x0e, 0xa1, 0x64, 0x01, 0xc0, 0x4a, 0x40, 0x88, 0xe0,
+	0x85, 0x80, 0xb8, 0x77, 0xfc, 0xef, 0x35, 0x81, 0xc8, 0x77, 0xfc, 0xef,
+	0x08, 0x98, 0x4a, 0x00, 0xfc, 0xfb, 0x55, 0xfc, 0xe8, 0x4a, 0x60, 0xef,
+	0x1a, 0x44, 0x9c, 0xe1, 0x35, 0x81, 0x1a, 0x4e, 0x9c, 0xe9, 0x1c, 0x00,
+	0x00, 0xe2, 0x0c, 0x0c, 0x1c, 0xf6, 0x93, 0xdd, 0x0d, 0xc3, 0x1a, 0x41,
+	0x08, 0xe4, 0x0a, 0x40, 0x84, 0xe1, 0x0c, 0x00, 0x00, 0xe2, 0x93, 0xdd,
+	0x4c, 0x04, 0x1c, 0xfa, 0x86, 0x52, 0xec, 0xe1, 0x08, 0xa6, 0x65, 0x12,
+	0x24, 0xf8, 0x0e, 0x02, 0x99, 0x7a, 0x00, 0xc0, 0x00, 0x40, 0xa0, 0xf3,
+	0x06, 0xa6, 0x0b, 0x8c, 0x08, 0x49, 0x00, 0xef, 0x85, 0x12, 0x28, 0xf8,
+	0x02, 0x02, 0xfc, 0xed, 0xf6, 0x47, 0xfd, 0x6f, 0xe0, 0xff, 0x04, 0xe2,
+	0x14, 0x04, 0xc0, 0xe0, 0x0f, 0x86, 0x2f, 0xa0, 0x0b, 0x8c, 0x2e, 0xe2,
+	0x08, 0x48, 0x00, 0xef, 0x86, 0x02, 0x84, 0xfe, 0x0e, 0x05, 0x09, 0x7d,
+	0x00, 0xc0, 0x05, 0x52, 0x08, 0xf8, 0x18, 0x7d, 0xfc, 0xef, 0x4a, 0x40,
+	0x80, 0xe0, 0x09, 0x12, 0x04, 0xc0, 0x65, 0x12, 0x20, 0xf8, 0x00, 0x40,
+	0x40, 0xdc, 0x01, 0x52, 0x04, 0xc0, 0x0e, 0x00, 0x41, 0x78, 0xf5, 0xc5,
+	0x6d, 0xc0, 0xb5, 0x82, 0x05, 0x10, 0x10, 0xe0, 0x11, 0xf1, 0x0f, 0x82,
+	0x05, 0x50, 0x10, 0xe0, 0x05, 0x10, 0x10, 0xe0, 0xfe, 0x02, 0xf0, 0xff,
+	0x0f, 0x82, 0x85, 0x83, 0x15, 0x10, 0x10, 0xe0, 0x16, 0x00, 0x91, 0x6e,
+	0x69, 0xcd, 0x21, 0xf1, 0x6d, 0xc1, 0x01, 0x83, 0x2f, 0x82, 0x26, 0x00,
+	0x00, 0x80, 0x2f, 0xa0, 0x25, 0x50, 0x10, 0xe0, 0x05, 0x10, 0x10, 0xe0,
+	0x11, 0xa1, 0xfe, 0x04, 0xf0, 0xff, 0x06, 0x42, 0x00, 0x80, 0x0f, 0x84,
+	0x0f, 0xa2, 0x05, 0x50, 0x10, 0xe0, 0x16, 0x00, 0x91, 0x6e, 0x69, 0xcd,
+	0x6d, 0xc1, 0x71, 0x8d, 0x16, 0x00, 0x79, 0x61, 0x2d, 0xcb, 0x86, 0x0e,
+	0x00, 0x80, 0x6d, 0xc1, 0x56, 0x0e, 0x00, 0xc0, 0x0b, 0x8c, 0x1b, 0x8e,
+	0x71, 0x52, 0x0c, 0xf8, 0x08, 0x43, 0x00, 0xef, 0x05, 0x52, 0x14, 0xf8,
+	0x15, 0x10, 0x28, 0xe0, 0x70, 0x04, 0x04, 0xec, 0x31, 0xe1, 0x29, 0x9e,
+	0x1f, 0x86, 0x1f, 0xa4, 0x15, 0x50, 0x28, 0xe0, 0x86, 0x42, 0x3c, 0xe0,
+	0x0e, 0x04, 0x9d, 0x64, 0x9b, 0xc2, 0x05, 0x52, 0x1c, 0xf8, 0x78, 0xa6,
+	0x48, 0x77, 0xfc, 0xef, 0x4a, 0x40, 0x80, 0xe0, 0x70, 0x4e, 0x10, 0xdc,
+	0x1e, 0x00, 0x81, 0x70, 0xeb, 0xcb, 0x70, 0x4e, 0xec, 0x93, 0x6d, 0xc1,
+	0x11, 0x85, 0x36, 0x02, 0x00, 0x80, 0x76, 0xa6, 0x11, 0x52, 0x10, 0xf8,
+	0x05, 0x10, 0x40, 0xe0, 0xfe, 0x47, 0x0c, 0xff, 0x14, 0x04, 0xa0, 0xe0,
+	0x0f, 0x86, 0x0f, 0xa4, 0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x28, 0xe0,
+	0xfe, 0x47, 0xfd, 0x7f, 0xe3, 0xff, 0x14, 0x04, 0xd0, 0xe0, 0x0f, 0x86,
+	0x2f, 0xa0, 0x20, 0x00, 0x01, 0x6c, 0x00, 0xd0, 0x05, 0x50, 0x28, 0xe0,
+	0x0b, 0x8c, 0xf8, 0x7e, 0xfc, 0xee, 0x0e, 0x03, 0x59, 0x78, 0xf5, 0xc5,
+	0x0d, 0xc2, 0x05, 0x52, 0x0c, 0xf8, 0x08, 0xa6, 0x46, 0x42, 0xb4, 0xe0,
+	0x18, 0x84, 0x00, 0x40, 0xf4, 0x93, 0x00, 0x40, 0x08, 0xdc, 0x1b, 0xa1,
+	0x06, 0xa6, 0x05, 0x10, 0x40, 0x80, 0x04, 0x00, 0x50, 0x9c, 0x65, 0x8a,
+	0x05, 0x10, 0x44, 0xe0, 0xf6, 0x43, 0xfd, 0x6f, 0x00, 0xf8, 0x0f, 0x82,
+	0x06, 0x02, 0x01, 0x60, 0x1e, 0xc0, 0x0f, 0xa2, 0x05, 0x50, 0x44, 0xe0,
+	0x05, 0x10, 0x44, 0xe0, 0x0e, 0x02, 0x00, 0xf8, 0x0f, 0x82, 0x09, 0xf6,
+	0x05, 0x50, 0x44, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0x54, 0xfc,
+	0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0xcc, 0xfc,
+	0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0x4c, 0xfc,
+	0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0xd0, 0xfc,
+	0x05, 0x50, 0x40, 0xe0, 0x4c, 0x0c, 0x1c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1,
+	0xc6, 0x40, 0xfc, 0xe0, 0x04, 0x80, 0xc6, 0x44, 0x0c, 0xe1, 0x15, 0x04,
+	0x0c, 0xf8, 0x0a, 0x80, 0x06, 0x07, 0x04, 0xe0, 0x03, 0x42, 0x48, 0xe1,
+	0x46, 0x02, 0x40, 0xe2, 0x08, 0xc6, 0x44, 0x88, 0x06, 0x46, 0x0e, 0xe0,
+	0x86, 0x01, 0x84, 0xe0, 0x33, 0x80, 0x39, 0x06, 0xd8, 0xef, 0x0a, 0x46,
+	0x80, 0xe0, 0x31, 0xbf, 0x06, 0x06, 0x00, 0xc0, 0x31, 0x48, 0x60, 0xe0,
+	0x34, 0x88, 0x49, 0x06, 0x40, 0xe1, 0x40, 0x48, 0x7c, 0xf3, 0x41, 0x46,
+	0x40, 0xe1, 0x24, 0x8a, 0x39, 0x04, 0x10, 0xe0, 0x39, 0xc2, 0x31, 0x44,
+	0x10, 0xe0, 0x14, 0xc4, 0x1b, 0xa5, 0x11, 0x83, 0x11, 0x40, 0x25, 0x6a,
+	0x01, 0xc0, 0x08, 0x5c, 0x00, 0xda, 0x15, 0x00, 0xcc, 0xe0, 0x25, 0x00,
+	0xf8, 0xe0, 0x1b, 0x85, 0x08, 0x5c, 0x00, 0x9a, 0x4e, 0x03, 0x01, 0x60,
+	0x10, 0xc0, 0x29, 0x00, 0x1c, 0xe4, 0x18, 0x84, 0x20, 0x44, 0xf8, 0xf3,
+	0x2f, 0xa2, 0x21, 0x40, 0x1c, 0xe4, 0x93, 0xdd, 0x0c, 0x00, 0x80, 0xfa,
+	0x15, 0x00, 0x3c, 0xe0, 0x21, 0x81, 0x31, 0x85, 0x21, 0x42, 0x60, 0xe0,
+	0x15, 0x00, 0x44, 0xe0, 0x31, 0x42, 0x40, 0xe1, 0x15, 0x00, 0x34, 0xe0,
+	0x21, 0x42, 0x20, 0xe0, 0x15, 0x00, 0x34, 0xe0, 0xd6, 0x04, 0x10, 0xe0,
+	0x23, 0x42, 0x30, 0xe0, 0x15, 0x00, 0x34, 0xe0, 0x86, 0x44, 0x04, 0xe0,
+	0x23, 0x42, 0x38, 0xe0, 0x05, 0x00, 0x30, 0xe0, 0xc6, 0x02, 0x08, 0xe0,
+	0x13, 0x40, 0x10, 0xe3, 0xe8, 0x56, 0x40, 0xef, 0x06, 0x40, 0x0c, 0xe1,
+	0x04, 0x80, 0x06, 0x02, 0x94, 0xe0, 0x2b, 0x02, 0xc4, 0xea, 0x3b, 0x00,
+	0x78, 0xe2, 0x20, 0x44, 0xfd, 0x73, 0x07, 0xc0, 0x30, 0x46, 0x01, 0x70,
+	0xf8, 0xc0, 0x3f, 0xa4, 0x33, 0x40, 0x78, 0xe2, 0x0a, 0x84, 0x0c, 0x08,
+	0x80, 0xf2, 0xf8, 0x3b, 0x3c, 0xff, 0xc3, 0xc1, 0x06, 0x40, 0x0c, 0xe1,
+	0x04, 0x80, 0x1b, 0x00, 0x40, 0xe4, 0x19, 0xc2, 0x13, 0x40, 0x40, 0xe4,
+	0x1b, 0x00, 0x40, 0xe4, 0x19, 0xc4, 0x13, 0x40, 0x40, 0xe4, 0x93, 0xdd,
+	0xc6, 0x43, 0xec, 0xe0, 0x46, 0x41, 0xfc, 0xe0, 0x24, 0x84, 0x04, 0x80,
+	0x31, 0x81, 0x4a, 0x44, 0x80, 0xe0, 0x86, 0x44, 0x0c, 0xe1, 0x09, 0x00,
+	0x6c, 0xe0, 0xc4, 0x8a, 0x8e, 0x47, 0xfc, 0x9f, 0x01, 0x42, 0x51, 0x78,
+	0x0c, 0xc0, 0x31, 0x58, 0x90, 0xe0, 0x34, 0x8a, 0x41, 0xbf, 0x06, 0x08,
+	0x00, 0xc0, 0x41, 0x46, 0xa0, 0xe0, 0x34, 0x8a, 0x51, 0x81, 0xf6, 0x0b,
+	0x00, 0xc0, 0x51, 0x46, 0xd0, 0xe0, 0x34, 0x8a, 0x01, 0xbf, 0x51, 0x46,
+	0xe0, 0xe0, 0x44, 0x84, 0x0a, 0x48, 0x84, 0xe0, 0x75, 0x86, 0x54, 0xca,
+	0x49, 0x88, 0x44, 0x06, 0x88, 0xe1, 0x36, 0x94, 0x4a, 0x46, 0x80, 0xe0,
+	0x34, 0xca, 0x47, 0xc6, 0x11, 0x8d, 0x41, 0x46, 0xd0, 0xe0, 0x34, 0x88,
+	0x76, 0x02, 0x00, 0xc0, 0x06, 0x00, 0x00, 0xc0, 0x16, 0x8c, 0x14, 0x88,
+	0x01, 0x42, 0xc0, 0xe1, 0x01, 0x42, 0xe0, 0xe1, 0x01, 0x42, 0xf0, 0xe1,
+	0x93, 0xdd, 0x34, 0xca, 0x41, 0x85, 0x46, 0x8c, 0x34, 0xca, 0x06, 0x48,
+	0x00, 0xe0, 0x41, 0x46, 0xd0, 0xe0, 0x34, 0x88, 0x41, 0x83, 0x46, 0x8c,
+	0x34, 0x88, 0x01, 0x46, 0xc0, 0xe1, 0x01, 0x46, 0xe0, 0xe1, 0x01, 0x46,
+	0xf0, 0xe1, 0x09, 0x02, 0x20, 0xe0, 0x14, 0xca, 0x03, 0x42, 0x58, 0xe0,
+	0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x46, 0x4e, 0x08, 0xe1,
+	0x06, 0x4c, 0x0c, 0xe1, 0x0a, 0x9e, 0x14, 0x98, 0x05, 0x42, 0x44, 0xe0,
+	0x10, 0x00, 0xe1, 0x65, 0x03, 0xc0, 0x78, 0x41, 0x00, 0xe8, 0x08, 0x9c,
+	0x0b, 0xa1, 0x04, 0x98, 0x06, 0x02, 0x10, 0x80, 0x13, 0x40, 0xf8, 0x86,
+	0x65, 0x82, 0x00, 0x00, 0xe1, 0x65, 0x03, 0xc0, 0xa8, 0x40, 0x00, 0xe8,
+	0x14, 0x98, 0x04, 0x00, 0xa0, 0xfc, 0x03, 0x42, 0x00, 0xe7, 0x4c, 0x0c,
+	0x04, 0xf2, 0x93, 0xdd, 0x0a, 0x80, 0x93, 0xdd, 0x0c, 0x04, 0x00, 0xfa,
+	0x06, 0x02, 0xec, 0xe1, 0x64, 0x84, 0x15, 0x0c, 0x2c, 0xe0, 0x14, 0x02,
+	0xa0, 0xfc, 0x15, 0x4c, 0x2c, 0xe0, 0xd8, 0x40, 0x00, 0xe8, 0x14, 0xd8,
+	0x09, 0x82, 0x14, 0x02, 0x00, 0xfc, 0x1f, 0xa0, 0x1e, 0xd8, 0x01, 0x85,
+	0x0c, 0x0c, 0x00, 0xf2, 0xe8, 0x32, 0x2c, 0xff, 0x93, 0xdd, 0xc3, 0xc1,
+	0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x80, 0xf6, 0x01, 0x94, 0xe0, 0x08, 0x80,
+	0x4a, 0x40, 0x80, 0xe0, 0x45, 0x86, 0x06, 0x40, 0x0c, 0xe1, 0x04, 0x80,
+	0xc6, 0x02, 0x40, 0xe2, 0x09, 0x00, 0xd0, 0xe0, 0x14, 0x84, 0x1b, 0xa5,
+	0x15, 0x84, 0x07, 0xc5, 0x09, 0x82, 0x18, 0x41, 0x00, 0xe8, 0x46, 0x43,
+	0xfc, 0xe0, 0x14, 0x84, 0x19, 0x02, 0xd8, 0xe0, 0x19, 0x82, 0x0b, 0x83,
+	0x16, 0x00, 0x00, 0xc0, 0x01, 0x4c, 0x00, 0xc0, 0x0c, 0x0c, 0x00, 0xf2,
+	0x93, 0xdd, 0xc3, 0xc1, 0x4a, 0x00, 0x00, 0xe0, 0x0c, 0x00, 0x00, 0xe2,
+	0x93, 0xdd, 0xc3, 0xc1, 0x46, 0x40, 0x84, 0xe0, 0x11, 0xaf, 0x13, 0x40,
+	0x6c, 0xec, 0x11, 0xb3, 0x13, 0x40, 0x70, 0xec, 0xc6, 0x43, 0xf0, 0xe0,
+	0x13, 0x40, 0xdc, 0xec, 0xc6, 0x02, 0x24, 0xe0, 0x1c, 0x80, 0x93, 0xdd,
+	0x4c, 0x00, 0x00, 0xfa, 0xc8, 0x60, 0x7c, 0xef, 0xe8, 0x61, 0x7c, 0xef,
+	0x28, 0x7e, 0x80, 0xef, 0xc6, 0x40, 0x98, 0xe1, 0x11, 0x83, 0x16, 0x80,
+	0x46, 0x01, 0x10, 0xe1, 0x11, 0x81, 0x16, 0x80, 0x4c, 0x08, 0x00, 0xf2,
+	0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0x6b, 0x80, 0x04, 0x98,
+	0x7b, 0x82, 0x56, 0x42, 0xb4, 0xe0, 0x88, 0x84, 0x05, 0x00, 0x10, 0xe0,
+	0x09, 0x86, 0x0b, 0xa5, 0x46, 0x02, 0x00, 0x80, 0x06, 0x05, 0x00, 0x80,
+	0x25, 0x82, 0x0b, 0xa3, 0xa5, 0x80, 0x0b, 0xa1, 0x06, 0x00, 0xf4, 0xef,
+	0xd5, 0x84, 0x11, 0x85, 0x21, 0x91, 0x0b, 0x8e, 0x88, 0x74, 0x10, 0xef,
+	0x0b, 0xa1, 0xf5, 0x82, 0x0a, 0x9e, 0x1a, 0x9c, 0x24, 0x98, 0x07, 0xe0,
+	0x0f, 0xa2, 0x0e, 0xca, 0x0a, 0xde, 0x1a, 0xdc, 0x24, 0x98, 0x03, 0xb0,
+	0x07, 0xe0, 0x0f, 0xa2, 0x0e, 0xc8, 0x01, 0x81, 0x0c, 0x0c, 0x0c, 0xf2,
+	0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x7c, 0xfa, 0x46, 0x42, 0x9c, 0xe0,
+	0x0b, 0x02, 0x04, 0xe3, 0xf0, 0x1e, 0x30, 0xec, 0x0b, 0xa3, 0x35, 0x96,
+	0x8e, 0x01, 0x01, 0x60, 0x10, 0xc0, 0x0e, 0xfc, 0xc6, 0x05, 0xd0, 0xe1,
+	0x0b, 0x82, 0x31, 0x81, 0x10, 0x16, 0x00, 0xe5, 0x20, 0x10, 0x20, 0xe7,
+	0x0e, 0xbe, 0xb5, 0x85, 0x94, 0xfc, 0xa4, 0xbe, 0x82, 0x4c, 0x9c, 0xf0,
+	0x05, 0x0c, 0x40, 0xe0, 0x11, 0x89, 0x93, 0x8e, 0xa3, 0x8e, 0x58, 0x44,
+	0x00, 0xe8, 0x15, 0x0c, 0xc0, 0xf8, 0x04, 0x0c, 0x80, 0xfb, 0x0c, 0xed,
+	0x0b, 0x82, 0x1b, 0x8c, 0x48, 0x44, 0x00, 0xe8, 0x15, 0x10, 0x1c, 0xfc,
+	0x0e, 0xa8, 0x0b, 0x82, 0x1b, 0x8c, 0xd8, 0x43, 0x00, 0xe8, 0x71, 0x88,
+	0x0e, 0xa4, 0x0a, 0x0e, 0x40, 0xe0, 0x35, 0xf8, 0x04, 0xbe, 0x14, 0xbc,
+	0x81, 0xa0, 0x03, 0x8e, 0x0e, 0xbe, 0x04, 0xfc, 0x11, 0x82, 0x3b, 0x82,
+	0x03, 0x8e, 0x0e, 0xfc, 0x3b, 0xa9, 0x06, 0x0e, 0x00, 0xc0, 0x35, 0x5e,
+	0x00, 0xc0, 0xd5, 0xfa, 0xc6, 0x01, 0xd0, 0xe1, 0x7b, 0x80, 0x04, 0x9e,
+	0x11, 0x91, 0x98, 0x41, 0x00, 0xe8, 0x24, 0x9c, 0x46, 0x42, 0x9c, 0xe0,
+	0x6b, 0x82, 0x03, 0x4c, 0xc4, 0xe0, 0x11, 0x91, 0x0b, 0x84, 0xf8, 0x40,
+	0x00, 0xe8, 0x19, 0x0e, 0x20, 0xe5, 0x03, 0x4c, 0xc0, 0xe0, 0x0b, 0x82,
+	0x08, 0x72, 0xfc, 0xef, 0x01, 0x4c, 0x24, 0xf9, 0xf1, 0x98, 0x0c, 0x0c,
+	0x7c, 0xf2, 0x93, 0xdd, 0x4c, 0x00, 0x00, 0xfa, 0x48, 0x65, 0x2c, 0xef,
+	0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa,
+	0x6b, 0x82, 0x78, 0x6e, 0xfc, 0xee, 0x46, 0x42, 0xec, 0xe0, 0x24, 0x84,
+	0x24, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x83, 0xf5, 0x82, 0x24, 0x02,
+	0xa0, 0xe1, 0x14, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x85, 0x15, 0x82,
+	0x27, 0xe1, 0x24, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x89, 0x86, 0x02,
+	0x00, 0x80, 0x0c, 0x0c, 0x00, 0xf2, 0x18, 0x17, 0xfc, 0xfe, 0xc3, 0xc1,
+	0x0c, 0x04, 0x00, 0xfa, 0x06, 0x41, 0x8c, 0xe0, 0x1b, 0x00, 0xec, 0xe4,
+	0x1b, 0xa3, 0x75, 0x84, 0x11, 0x81, 0x8e, 0x05, 0x01, 0x60, 0x10, 0xc0,
+	0x00, 0x06, 0xc0, 0xe5, 0x95, 0x81, 0x44, 0x88, 0x1d, 0xee, 0x75, 0x80,
+	0x4e, 0xc1, 0x25, 0x81, 0x4e, 0xcd, 0x21, 0x88, 0x11, 0x82, 0x0a, 0x02,
+	0x40, 0xe0, 0xd5, 0xfc, 0x56, 0x00, 0x00, 0xe1, 0x18, 0x80, 0x1b, 0xa1,
+	0xc5, 0x84, 0x08, 0x82, 0x4a, 0x00, 0xfc, 0xfb, 0x45, 0x84, 0x86, 0x4d,
+	0x84, 0xe1, 0x04, 0x98, 0x05, 0x00, 0x10, 0xe0, 0x4a, 0x40, 0x80, 0xe0,
+	0x45, 0x82, 0x11, 0x81, 0x0b, 0x8c, 0x58, 0x76, 0x28, 0xef, 0x0b, 0x8c,
+	0x0c, 0x0c, 0x00, 0xf2, 0x88, 0x35, 0x28, 0xff, 0x0c, 0x0c, 0x00, 0xf2,
+	0x93, 0xdd, 0xc3, 0xc1, 0x46, 0x41, 0xfc, 0xe0, 0x04, 0x80, 0x09, 0x00,
+	0x80, 0xe0, 0x09, 0x9e, 0x0b, 0xa3, 0x75, 0x82, 0x46, 0x41, 0x80, 0xe1,
+	0x04, 0x80, 0xc6, 0x42, 0x8c, 0xe0, 0x04, 0xc2, 0x00, 0x40, 0x00, 0xf2,
+	0x07, 0xcf, 0x06, 0x84, 0x06, 0x40, 0x84, 0xe0, 0x15, 0x00, 0x28, 0xe5,
+	0x1c, 0xc2, 0x93, 0xdd, 0x0b, 0xa1, 0xc6, 0x00, 0xa0, 0xe1, 0x15, 0x00,
+	0x04, 0xf8, 0x05, 0x84, 0x21, 0x8b, 0x2c, 0x84, 0x14, 0x80, 0x2c, 0x84,
+	0x14, 0x82, 0x2c, 0x84, 0x15, 0x00, 0x10, 0xe0, 0x21, 0xa1, 0x21, 0x42,
+	0x10, 0xe0, 0x05, 0x00, 0x14, 0xe0, 0x01, 0x88, 0x75, 0x83, 0x21, 0x85,
+	0x2c, 0x84, 0x14, 0x80, 0x06, 0x46, 0x00, 0xe0, 0x2c, 0x84, 0x14, 0x82,
+	0x2c, 0x84, 0x14, 0xc0, 0x21, 0xa1, 0x21, 0x42, 0x20, 0xe0, 0x14, 0xc2,
+	0x31, 0x42, 0x20, 0xe0, 0x15, 0x00, 0x10, 0xe0, 0x21, 0x42, 0x20, 0xe0,
+	0x05, 0x00, 0x14, 0xe0, 0x01, 0x90, 0x06, 0x42, 0x00, 0xe0, 0x16, 0x80,
+	0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x7c, 0xfa, 0x4a, 0x40, 0x80, 0xe0,
+	0xf0, 0x1e, 0x30, 0xec, 0xe5, 0x82, 0xa6, 0x40, 0x00, 0xe1, 0x1a, 0x80,
+	0x2a, 0xc0, 0x3a, 0xc2, 0x13, 0x40, 0x10, 0xe0, 0x1a, 0x82, 0x23, 0x40,
+	0x18, 0xe0, 0x33, 0x40, 0x1c, 0xe0, 0x13, 0x40, 0x14, 0xe0, 0xf8, 0x61,
+	0x68, 0xef, 0xc6, 0x13, 0x00, 0xe1, 0x15, 0x12, 0x28, 0xf8, 0x0b, 0x02,
+	0x2c, 0xe0, 0x1b, 0x02, 0x24, 0xe0, 0x8a, 0x00, 0xa5, 0x64, 0x03, 0xc0,
+	0x35, 0x82, 0x0a, 0x4e, 0x9c, 0xe1, 0x1a, 0x03, 0x11, 0x6f, 0x02, 0xc0,
+	0xe8, 0x13, 0x01, 0x20, 0x00, 0xc0, 0x1f, 0xa0, 0x5a, 0x42, 0x80, 0xe0,
+	0x0a, 0x4e, 0x9c, 0xe1, 0x68, 0x13, 0x00, 0xa0, 0x09, 0x12, 0x78, 0xf8,
+	0xa1, 0x81, 0xf0, 0x02, 0x10, 0xe4, 0x07, 0xc4, 0x0c, 0xfc, 0xf0, 0x00,
+	0x20, 0xe4, 0xa6, 0x91, 0xa8, 0x53, 0x74, 0xef, 0x05, 0x12, 0x30, 0xf8,
+	0x25, 0x12, 0x28, 0xf8, 0x61, 0x87, 0x09, 0x00, 0x48, 0xe0, 0x81, 0x85,
+	0x09, 0x86, 0x0b, 0xa7, 0x26, 0x0c, 0x00, 0xc0, 0x0b, 0xa1, 0x0b, 0x04,
+	0x28, 0xe0, 0x16, 0x0c, 0x00, 0x80, 0x03, 0x52, 0x04, 0xf8, 0x0b, 0x04,
+	0x20, 0xe0, 0x0c, 0xa6, 0x1b, 0x04, 0x2c, 0xe0, 0x3b, 0x04, 0x28, 0xe0,
+	0x4b, 0x04, 0x20, 0xe0, 0x13, 0x86, 0x3b, 0x04, 0x24, 0xe0, 0x10, 0x0a,
+	0x04, 0xec, 0x1a, 0xfc, 0x33, 0x88, 0x30, 0x06, 0x04, 0xec, 0x12, 0x4e,
+	0x94, 0xf0, 0x32, 0x48, 0x84, 0xf0, 0x4c, 0xe4, 0x7c, 0xa4, 0xcb, 0x04,
+	0x28, 0xe0, 0x14, 0x08, 0x84, 0xe1, 0xcd, 0xc9, 0xc2, 0x58, 0x90, 0x91,
+	0x42, 0x4e, 0x94, 0x90, 0xc3, 0x52, 0x04, 0x98, 0x73, 0x52, 0x00, 0x80,
+	0x5b, 0x04, 0x20, 0xe0, 0x5d, 0xc9, 0x52, 0x40, 0x90, 0x91, 0x42, 0x48,
+	0x8c, 0x90, 0x03, 0x52, 0x04, 0x80, 0x43, 0x52, 0x08, 0x80, 0x3b, 0x04,
+	0x2c, 0xe0, 0x49, 0x04, 0xb8, 0xe0, 0x33, 0x52, 0x1c, 0xf8, 0x2b, 0x04,
+	0x24, 0xe0, 0x4b, 0xab, 0x23, 0x52, 0x18, 0xf8, 0x65, 0x8a, 0x4b, 0xa9,
+	0xe5, 0x90, 0x4b, 0xa7, 0x22, 0x44, 0x84, 0xd0, 0x32, 0x46, 0x84, 0xd0,
+	0x33, 0x52, 0x1c, 0xd8, 0x23, 0x52, 0x18, 0xd8, 0x95, 0x96, 0x20, 0x44,
+	0xf9, 0x73, 0xff, 0xc0, 0x27, 0xc3, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8,
+	0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x58, 0x52,
+	0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc3,
+	0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02,
+	0x80, 0xfb, 0x2b, 0x8c, 0x68, 0x51, 0x74, 0xef, 0xc5, 0x87, 0x20, 0x44,
+	0xe1, 0x73, 0xff, 0xc0, 0x27, 0xc7, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8,
+	0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x78, 0x57,
+	0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc7,
+	0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02,
+	0x80, 0xfb, 0x2b, 0x8c, 0x88, 0x56, 0x74, 0xef, 0xe5, 0x83, 0x20, 0x44,
+	0xf1, 0x73, 0xff, 0xc0, 0x27, 0xc5, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8,
+	0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x18, 0x52,
+	0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc5,
+	0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02,
+	0x80, 0xfb, 0x2b, 0x8c, 0x28, 0x51, 0x74, 0xef, 0x7b, 0x80, 0x7c, 0xa4,
+	0x08, 0x91, 0xa3, 0x52, 0x1c, 0xe0, 0xa3, 0x52, 0x24, 0xe0, 0x0b, 0xa1,
+	0x83, 0x52, 0x1c, 0x80, 0x83, 0x52, 0x24, 0x80, 0x89, 0x12, 0x78, 0xf8,
+	0xf6, 0x57, 0xfc, 0xef, 0x6b, 0x12, 0x1c, 0xf8, 0xab, 0x12, 0x18, 0xf8,
+	0xd6, 0x57, 0xfc, 0x8f, 0x8b, 0xa3, 0xa0, 0x40, 0x00, 0x9c, 0xa5, 0x86,
+	0x64, 0x00, 0x80, 0xfb, 0x1b, 0x90, 0xf8, 0x7d, 0xf8, 0xee, 0x6b, 0x80,
+	0xa4, 0x00, 0x80, 0xfb, 0x1b, 0x90, 0x98, 0x7d, 0xf8, 0xee, 0x15, 0x12,
+	0x28, 0xf8, 0x19, 0x02, 0xb8, 0xe0, 0x1b, 0xad, 0x95, 0x82, 0x1a, 0xa6,
+	0xa0, 0x44, 0xf9, 0x73, 0xff, 0xc0, 0x27, 0xc3, 0x13, 0x94, 0x10, 0x02,
+	0x08, 0xec, 0x1c, 0xe4, 0x23, 0x52, 0x18, 0xf8, 0x1b, 0x12, 0x04, 0xf8,
+	0x03, 0x96, 0x03, 0x52, 0x28, 0xe0, 0x1c, 0xe6, 0x0a, 0xa6, 0x1a, 0xe4,
+	0x63, 0x96, 0x63, 0x52, 0x20, 0xe0, 0x73, 0x52, 0x10, 0xe0, 0x03, 0x52,
+	0x14, 0xe0, 0x13, 0x52, 0x18, 0xe0, 0x98, 0x52, 0x74, 0xef, 0x09, 0x12,
+	0x8c, 0xe0, 0x0b, 0xa1, 0x01, 0x81, 0x01, 0x52, 0x90, 0xe0, 0x65, 0x82,
+	0x05, 0x12, 0x30, 0xf8, 0x09, 0x00, 0xa8, 0xe0, 0x0a, 0x00, 0x0c, 0xf8,
+	0x16, 0x00, 0x00, 0xc0, 0x01, 0x52, 0x90, 0xc0, 0x46, 0x41, 0x84, 0xe0,
+	0x0a, 0x80, 0x0a, 0x4e, 0x9c, 0xe9, 0x1a, 0x00, 0x08, 0xe0, 0x38, 0x01,
+	0x01, 0x20, 0x00, 0xc0, 0x0b, 0x12, 0x1c, 0xe0, 0x1b, 0x12, 0x24, 0xe0,
+	0x2b, 0x12, 0x28, 0xe0, 0x03, 0x52, 0x2c, 0xe0, 0x0b, 0x12, 0x20, 0xe0,
+	0x13, 0x52, 0x34, 0xe0, 0x23, 0x52, 0x38, 0xe0, 0x03, 0x52, 0x30, 0xe0,
+	0x0c, 0x00, 0x00, 0xe2, 0xf1, 0x98, 0x0c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd,
+	0x13, 0xa9, 0x00, 0x00, 0xa8, 0xc1, 0x40, 0x00, 0x68, 0x04, 0xa0, 0xe0,
+	0x40, 0x6c, 0x40, 0x00, 0xe8, 0x34, 0xc8, 0xe0, 0xfc, 0x91, 0x40, 0x00,
+	0x68, 0x1f, 0xb8, 0xe0, 0x30, 0x16, 0x41, 0x00, 0x28, 0x39, 0x74, 0xe0,
+	0xb0, 0x7e, 0x40, 0x00, 0xe8, 0x38, 0xc0, 0xe0, 0x30, 0x04, 0x41, 0x00,
+	0x48, 0x1b, 0x80, 0xe0, 0x30, 0x2e, 0x40, 0x00, 0x88, 0x0c, 0xec, 0xe0,
+	0x10, 0x9f, 0x40, 0x00, 0x88, 0x08, 0xb4, 0xe0, 0x10, 0x01, 0x41, 0x00,
+	0x68, 0x01, 0x84, 0xe0, 0x54, 0xd6, 0x40, 0x00, 0xc8, 0x1a, 0x98, 0xe0,
+	0xd0, 0xc8, 0x40, 0x00, 0x68, 0x08, 0xa0, 0xe0, 0x80, 0xdb, 0x40, 0x00,
+	0xe8, 0x35, 0x94, 0xe0, 0x74, 0xff, 0x40, 0x00, 0xa8, 0x11, 0x80, 0xe0,
+	0xf8, 0x89, 0x40, 0x00, 0x88, 0x16, 0xbc, 0xe0, 0x00, 0x90, 0x40, 0x00,
+	0x08, 0x35, 0xb8, 0xe0, 0x7c, 0x73, 0x40, 0x00, 0x88, 0x1b, 0xc8, 0xe0,
+	0xf4, 0xff, 0x40, 0x00, 0x68, 0x39, 0x80, 0xe0, 0xa4, 0xa4, 0x40, 0x00,
+	0xa8, 0x16, 0xb0, 0xe0, 0x50, 0xc9, 0x40, 0x00, 0x28, 0x3a, 0x98, 0xe0,
+	0x00, 0xb9, 0x00, 0x00, 0xb6, 0x85, 0x00, 0x00,
+};
diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c
index 78d18c028..1ae42e448 100644
--- a/drivers/media/i2c/vd55g1.c
+++ b/drivers/media/i2c/vd55g1.c
@@ -138,301 +138,7 @@
 #define VD55G1_MODEL_ID_NAME(id) \
 	((id) == VD55G1_MODEL_ID_VD55G1 ? "vd55g1" : "vd65g4")
 
-static const u8 vd55g1_patch_array[] = {
-	0x44, 0x03, 0x09, 0x02, 0xe6, 0x01, 0x42, 0x00, 0xea, 0x01, 0x42, 0x00,
-	0xf0, 0x01, 0x42, 0x00, 0xe6, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0xfa, 0x68, 0x40, 0x00, 0xe8,
-	0x09, 0xbe, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x1c, 0x00, 0xc0, 0xe2,
-	0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x80, 0x98, 0x7f,
-	0xfc, 0xef, 0x11, 0xc1, 0x0f, 0x82, 0x69, 0xbe, 0x0f, 0xac, 0x58, 0x40,
-	0x00, 0xe8, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0x1c, 0x00, 0x40, 0xe3,
-	0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x84, 0xfa, 0x46, 0x0e, 0xe8, 0xe0,
-	0x08, 0xde, 0x4a, 0x40, 0x84, 0xe0, 0xa5, 0x86, 0xa8, 0x7d, 0xfc, 0xef,
-	0x6b, 0x80, 0x01, 0xbf, 0x28, 0x77, 0x0c, 0xef, 0x0b, 0x0e, 0x21, 0x78,
-	0x06, 0xc0, 0x0b, 0xa5, 0xb5, 0x84, 0x06, 0x42, 0x98, 0xe1, 0x01, 0x81,
-	0x01, 0x42, 0x38, 0xe0, 0x0c, 0xc4, 0x0e, 0x84, 0x46, 0x02, 0x84, 0xe0,
-	0x0c, 0x84, 0x11, 0x81, 0x21, 0x81, 0x31, 0x81, 0x41, 0x81, 0x51, 0x81,
-	0xc1, 0x81, 0x05, 0x83, 0x0c, 0x0c, 0x84, 0xf2, 0x93, 0xdd, 0x06, 0x40,
-	0x98, 0xe1, 0xc8, 0x80, 0x58, 0x82, 0x48, 0xc0, 0x38, 0xc2, 0x29, 0x00,
-	0x10, 0xe0, 0x19, 0x00, 0x14, 0xe0, 0x09, 0x00, 0x38, 0xe0, 0x5f, 0xb8,
-	0x5f, 0xa8, 0x5f, 0xa6, 0x5f, 0xa4, 0x5f, 0xa2, 0x5f, 0xa0, 0x56, 0x41,
-	0x98, 0xe1, 0x18, 0x82, 0x28, 0x80, 0x38, 0xc0, 0x5f, 0xa2, 0x19, 0x00,
-	0x20, 0xf8, 0x5f, 0xa4, 0x28, 0xc2, 0x5f, 0xa6, 0x39, 0x00, 0x10, 0xe0,
-	0x5f, 0xa2, 0x19, 0x00, 0x14, 0xe0, 0x5f, 0xa4, 0x29, 0x00, 0x18, 0xe0,
-	0x5f, 0xa6, 0x39, 0x00, 0x40, 0xe0, 0x5f, 0xa2, 0x19, 0x00, 0x44, 0xe0,
-	0x5f, 0xa4, 0x29, 0x00, 0x1c, 0xe0, 0x5f, 0xa6, 0x39, 0x00, 0x38, 0xe0,
-	0x5f, 0xa2, 0x19, 0x00, 0x20, 0xe0, 0x5f, 0xa4, 0x29, 0x00, 0x24, 0xe0,
-	0x5f, 0xa6, 0x39, 0x00, 0x28, 0xe0, 0x5f, 0xa2, 0x19, 0x00, 0x2c, 0xe0,
-	0x5f, 0xa4, 0x29, 0x00, 0x30, 0xe0, 0x5f, 0xa6, 0x09, 0x00, 0x34, 0xe0,
-	0x5f, 0xa2, 0x5f, 0xa4, 0x5f, 0xa0, 0x4a, 0x0a, 0xfc, 0xfb, 0xe5, 0x82,
-	0x08, 0xde, 0x4a, 0x40, 0x88, 0xe0, 0xf6, 0x40, 0x00, 0xe0, 0x01, 0x4e,
-	0x99, 0x78, 0x0a, 0xc0, 0x85, 0x80, 0x98, 0x40, 0x00, 0xe8, 0x35, 0x81,
-	0xa8, 0x40, 0x00, 0xe8, 0x0b, 0x8c, 0x0c, 0x0c, 0x84, 0xf2, 0xd5, 0xed,
-	0x83, 0xc1, 0x13, 0xc5, 0x93, 0xdd, 0xc3, 0xc1, 0x83, 0xc1, 0x13, 0xc3,
-	0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0xc6, 0x0f, 0x94, 0xe0,
-	0x19, 0x0e, 0xc9, 0x65, 0x01, 0xc0, 0x28, 0xde, 0x0a, 0x42, 0x80, 0xe0,
-	0x24, 0x02, 0x00, 0xfc, 0x16, 0xde, 0xa5, 0x8a, 0x19, 0x00, 0xb8, 0xe0,
-	0x10, 0x02, 0x0c, 0xec, 0x1d, 0xe6, 0x14, 0x02, 0x88, 0x80, 0x4e, 0x04,
-	0x01, 0x00, 0x10, 0x80, 0x25, 0x02, 0x08, 0x9c, 0x86, 0x02, 0x00, 0x80,
-	0x08, 0x44, 0x00, 0x98, 0x55, 0x81, 0x11, 0x85, 0x45, 0x81, 0x11, 0x89,
-	0x25, 0x81, 0x11, 0x83, 0x2b, 0x00, 0x24, 0xe0, 0x64, 0xc2, 0x0b, 0x84,
-	0x08, 0x51, 0x00, 0xef, 0x2b, 0x80, 0x01, 0x83, 0x1b, 0x8c, 0x38, 0x7d,
-	0x5c, 0xef, 0x18, 0xde, 0x0b, 0xa1, 0x25, 0x82, 0x0b, 0x0e, 0x88, 0xf9,
-	0x0a, 0x00, 0x00, 0xe8, 0x10, 0x42, 0x04, 0x9c, 0x11, 0x4e, 0x0c, 0x80,
-	0x10, 0x40, 0x04, 0xf0, 0x4e, 0x05, 0x01, 0x60, 0x10, 0xc0, 0x06, 0x88,
-	0x10, 0x40, 0xf8, 0xf3, 0x06, 0xde, 0x4c, 0x0c, 0x04, 0xf2, 0x93, 0xdd,
-	0x0c, 0x04, 0x1c, 0xfe, 0xf6, 0x0f, 0x94, 0xe0, 0x38, 0x9c, 0x46, 0x51,
-	0xfc, 0xe0, 0x46, 0x49, 0x38, 0xe2, 0x30, 0x46, 0xf8, 0xf3, 0x36, 0x9c,
-	0xc6, 0x46, 0x0c, 0xe1, 0x34, 0x8c, 0x94, 0xa0, 0x4e, 0xa0, 0x39, 0x06,
-	0x80, 0xe0, 0x4a, 0x46, 0x94, 0xe0, 0x05, 0x8c, 0x6a, 0x40, 0x80, 0xe0,
-	0x2c, 0x0c, 0x00, 0xe2, 0x0b, 0x8c, 0xb8, 0x7c, 0x5c, 0xef, 0x0b, 0x8c,
-	0x9e, 0xa0, 0xf8, 0x40, 0x60, 0xef, 0x0b, 0xa1, 0x5a, 0x40, 0x80, 0xe0,
-	0x65, 0x88, 0x28, 0x02, 0x01, 0x40, 0x00, 0x80, 0x2a, 0x42, 0x9c, 0xe1,
-	0x28, 0x49, 0x60, 0xef, 0x96, 0x4d, 0x9c, 0xe1, 0x01, 0x81, 0x06, 0x98,
-	0xd5, 0x81, 0x09, 0x0e, 0xa1, 0x64, 0x01, 0xc0, 0x4a, 0x40, 0x88, 0xe0,
-	0x85, 0x80, 0xb8, 0x77, 0xfc, 0xef, 0x35, 0x81, 0xc8, 0x77, 0xfc, 0xef,
-	0x08, 0x98, 0x4a, 0x00, 0xfc, 0xfb, 0x55, 0xfc, 0xe8, 0x4a, 0x60, 0xef,
-	0x1a, 0x44, 0x9c, 0xe1, 0x35, 0x81, 0x1a, 0x4e, 0x9c, 0xe9, 0x1c, 0x00,
-	0x00, 0xe2, 0x0c, 0x0c, 0x1c, 0xf6, 0x93, 0xdd, 0x0d, 0xc3, 0x1a, 0x41,
-	0x08, 0xe4, 0x0a, 0x40, 0x84, 0xe1, 0x0c, 0x00, 0x00, 0xe2, 0x93, 0xdd,
-	0x4c, 0x04, 0x1c, 0xfa, 0x86, 0x52, 0xec, 0xe1, 0x08, 0xa6, 0x65, 0x12,
-	0x24, 0xf8, 0x0e, 0x02, 0x99, 0x7a, 0x00, 0xc0, 0x00, 0x40, 0xa0, 0xf3,
-	0x06, 0xa6, 0x0b, 0x8c, 0x08, 0x49, 0x00, 0xef, 0x85, 0x12, 0x28, 0xf8,
-	0x02, 0x02, 0xfc, 0xed, 0xf6, 0x47, 0xfd, 0x6f, 0xe0, 0xff, 0x04, 0xe2,
-	0x14, 0x04, 0xc0, 0xe0, 0x0f, 0x86, 0x2f, 0xa0, 0x0b, 0x8c, 0x2e, 0xe2,
-	0x08, 0x48, 0x00, 0xef, 0x86, 0x02, 0x84, 0xfe, 0x0e, 0x05, 0x09, 0x7d,
-	0x00, 0xc0, 0x05, 0x52, 0x08, 0xf8, 0x18, 0x7d, 0xfc, 0xef, 0x4a, 0x40,
-	0x80, 0xe0, 0x09, 0x12, 0x04, 0xc0, 0x65, 0x12, 0x20, 0xf8, 0x00, 0x40,
-	0x40, 0xdc, 0x01, 0x52, 0x04, 0xc0, 0x0e, 0x00, 0x41, 0x78, 0xf5, 0xc5,
-	0x6d, 0xc0, 0xb5, 0x82, 0x05, 0x10, 0x10, 0xe0, 0x11, 0xf1, 0x0f, 0x82,
-	0x05, 0x50, 0x10, 0xe0, 0x05, 0x10, 0x10, 0xe0, 0xfe, 0x02, 0xf0, 0xff,
-	0x0f, 0x82, 0x85, 0x83, 0x15, 0x10, 0x10, 0xe0, 0x16, 0x00, 0x91, 0x6e,
-	0x69, 0xcd, 0x21, 0xf1, 0x6d, 0xc1, 0x01, 0x83, 0x2f, 0x82, 0x26, 0x00,
-	0x00, 0x80, 0x2f, 0xa0, 0x25, 0x50, 0x10, 0xe0, 0x05, 0x10, 0x10, 0xe0,
-	0x11, 0xa1, 0xfe, 0x04, 0xf0, 0xff, 0x06, 0x42, 0x00, 0x80, 0x0f, 0x84,
-	0x0f, 0xa2, 0x05, 0x50, 0x10, 0xe0, 0x16, 0x00, 0x91, 0x6e, 0x69, 0xcd,
-	0x6d, 0xc1, 0x71, 0x8d, 0x16, 0x00, 0x79, 0x61, 0x2d, 0xcb, 0x86, 0x0e,
-	0x00, 0x80, 0x6d, 0xc1, 0x56, 0x0e, 0x00, 0xc0, 0x0b, 0x8c, 0x1b, 0x8e,
-	0x71, 0x52, 0x0c, 0xf8, 0x08, 0x43, 0x00, 0xef, 0x05, 0x52, 0x14, 0xf8,
-	0x15, 0x10, 0x28, 0xe0, 0x70, 0x04, 0x04, 0xec, 0x31, 0xe1, 0x29, 0x9e,
-	0x1f, 0x86, 0x1f, 0xa4, 0x15, 0x50, 0x28, 0xe0, 0x86, 0x42, 0x3c, 0xe0,
-	0x0e, 0x04, 0x9d, 0x64, 0x9b, 0xc2, 0x05, 0x52, 0x1c, 0xf8, 0x78, 0xa6,
-	0x48, 0x77, 0xfc, 0xef, 0x4a, 0x40, 0x80, 0xe0, 0x70, 0x4e, 0x10, 0xdc,
-	0x1e, 0x00, 0x81, 0x70, 0xeb, 0xcb, 0x70, 0x4e, 0xec, 0x93, 0x6d, 0xc1,
-	0x11, 0x85, 0x36, 0x02, 0x00, 0x80, 0x76, 0xa6, 0x11, 0x52, 0x10, 0xf8,
-	0x05, 0x10, 0x40, 0xe0, 0xfe, 0x47, 0x0c, 0xff, 0x14, 0x04, 0xa0, 0xe0,
-	0x0f, 0x86, 0x0f, 0xa4, 0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x28, 0xe0,
-	0xfe, 0x47, 0xfd, 0x7f, 0xe3, 0xff, 0x14, 0x04, 0xd0, 0xe0, 0x0f, 0x86,
-	0x2f, 0xa0, 0x20, 0x00, 0x01, 0x6c, 0x00, 0xd0, 0x05, 0x50, 0x28, 0xe0,
-	0x0b, 0x8c, 0xf8, 0x7e, 0xfc, 0xee, 0x0e, 0x03, 0x59, 0x78, 0xf5, 0xc5,
-	0x0d, 0xc2, 0x05, 0x52, 0x0c, 0xf8, 0x08, 0xa6, 0x46, 0x42, 0xb4, 0xe0,
-	0x18, 0x84, 0x00, 0x40, 0xf4, 0x93, 0x00, 0x40, 0x08, 0xdc, 0x1b, 0xa1,
-	0x06, 0xa6, 0x05, 0x10, 0x40, 0x80, 0x04, 0x00, 0x50, 0x9c, 0x65, 0x8a,
-	0x05, 0x10, 0x44, 0xe0, 0xf6, 0x43, 0xfd, 0x6f, 0x00, 0xf8, 0x0f, 0x82,
-	0x06, 0x02, 0x01, 0x60, 0x1e, 0xc0, 0x0f, 0xa2, 0x05, 0x50, 0x44, 0xe0,
-	0x05, 0x10, 0x44, 0xe0, 0x0e, 0x02, 0x00, 0xf8, 0x0f, 0x82, 0x09, 0xf6,
-	0x05, 0x50, 0x44, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0x54, 0xfc,
-	0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0xcc, 0xfc,
-	0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0x4c, 0xfc,
-	0x05, 0x50, 0x40, 0xe0, 0x05, 0x10, 0x40, 0xe0, 0x04, 0x00, 0xd0, 0xfc,
-	0x05, 0x50, 0x40, 0xe0, 0x4c, 0x0c, 0x1c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1,
-	0xc6, 0x40, 0xfc, 0xe0, 0x04, 0x80, 0xc6, 0x44, 0x0c, 0xe1, 0x15, 0x04,
-	0x0c, 0xf8, 0x0a, 0x80, 0x06, 0x07, 0x04, 0xe0, 0x03, 0x42, 0x48, 0xe1,
-	0x46, 0x02, 0x40, 0xe2, 0x08, 0xc6, 0x44, 0x88, 0x06, 0x46, 0x0e, 0xe0,
-	0x86, 0x01, 0x84, 0xe0, 0x33, 0x80, 0x39, 0x06, 0xd8, 0xef, 0x0a, 0x46,
-	0x80, 0xe0, 0x31, 0xbf, 0x06, 0x06, 0x00, 0xc0, 0x31, 0x48, 0x60, 0xe0,
-	0x34, 0x88, 0x49, 0x06, 0x40, 0xe1, 0x40, 0x48, 0x7c, 0xf3, 0x41, 0x46,
-	0x40, 0xe1, 0x24, 0x8a, 0x39, 0x04, 0x10, 0xe0, 0x39, 0xc2, 0x31, 0x44,
-	0x10, 0xe0, 0x14, 0xc4, 0x1b, 0xa5, 0x11, 0x83, 0x11, 0x40, 0x25, 0x6a,
-	0x01, 0xc0, 0x08, 0x5c, 0x00, 0xda, 0x15, 0x00, 0xcc, 0xe0, 0x25, 0x00,
-	0xf8, 0xe0, 0x1b, 0x85, 0x08, 0x5c, 0x00, 0x9a, 0x4e, 0x03, 0x01, 0x60,
-	0x10, 0xc0, 0x29, 0x00, 0x1c, 0xe4, 0x18, 0x84, 0x20, 0x44, 0xf8, 0xf3,
-	0x2f, 0xa2, 0x21, 0x40, 0x1c, 0xe4, 0x93, 0xdd, 0x0c, 0x00, 0x80, 0xfa,
-	0x15, 0x00, 0x3c, 0xe0, 0x21, 0x81, 0x31, 0x85, 0x21, 0x42, 0x60, 0xe0,
-	0x15, 0x00, 0x44, 0xe0, 0x31, 0x42, 0x40, 0xe1, 0x15, 0x00, 0x34, 0xe0,
-	0x21, 0x42, 0x20, 0xe0, 0x15, 0x00, 0x34, 0xe0, 0xd6, 0x04, 0x10, 0xe0,
-	0x23, 0x42, 0x30, 0xe0, 0x15, 0x00, 0x34, 0xe0, 0x86, 0x44, 0x04, 0xe0,
-	0x23, 0x42, 0x38, 0xe0, 0x05, 0x00, 0x30, 0xe0, 0xc6, 0x02, 0x08, 0xe0,
-	0x13, 0x40, 0x10, 0xe3, 0xe8, 0x56, 0x40, 0xef, 0x06, 0x40, 0x0c, 0xe1,
-	0x04, 0x80, 0x06, 0x02, 0x94, 0xe0, 0x2b, 0x02, 0xc4, 0xea, 0x3b, 0x00,
-	0x78, 0xe2, 0x20, 0x44, 0xfd, 0x73, 0x07, 0xc0, 0x30, 0x46, 0x01, 0x70,
-	0xf8, 0xc0, 0x3f, 0xa4, 0x33, 0x40, 0x78, 0xe2, 0x0a, 0x84, 0x0c, 0x08,
-	0x80, 0xf2, 0xf8, 0x3b, 0x3c, 0xff, 0xc3, 0xc1, 0x06, 0x40, 0x0c, 0xe1,
-	0x04, 0x80, 0x1b, 0x00, 0x40, 0xe4, 0x19, 0xc2, 0x13, 0x40, 0x40, 0xe4,
-	0x1b, 0x00, 0x40, 0xe4, 0x19, 0xc4, 0x13, 0x40, 0x40, 0xe4, 0x93, 0xdd,
-	0xc6, 0x43, 0xec, 0xe0, 0x46, 0x41, 0xfc, 0xe0, 0x24, 0x84, 0x04, 0x80,
-	0x31, 0x81, 0x4a, 0x44, 0x80, 0xe0, 0x86, 0x44, 0x0c, 0xe1, 0x09, 0x00,
-	0x6c, 0xe0, 0xc4, 0x8a, 0x8e, 0x47, 0xfc, 0x9f, 0x01, 0x42, 0x51, 0x78,
-	0x0c, 0xc0, 0x31, 0x58, 0x90, 0xe0, 0x34, 0x8a, 0x41, 0xbf, 0x06, 0x08,
-	0x00, 0xc0, 0x41, 0x46, 0xa0, 0xe0, 0x34, 0x8a, 0x51, 0x81, 0xf6, 0x0b,
-	0x00, 0xc0, 0x51, 0x46, 0xd0, 0xe0, 0x34, 0x8a, 0x01, 0xbf, 0x51, 0x46,
-	0xe0, 0xe0, 0x44, 0x84, 0x0a, 0x48, 0x84, 0xe0, 0x75, 0x86, 0x54, 0xca,
-	0x49, 0x88, 0x44, 0x06, 0x88, 0xe1, 0x36, 0x94, 0x4a, 0x46, 0x80, 0xe0,
-	0x34, 0xca, 0x47, 0xc6, 0x11, 0x8d, 0x41, 0x46, 0xd0, 0xe0, 0x34, 0x88,
-	0x76, 0x02, 0x00, 0xc0, 0x06, 0x00, 0x00, 0xc0, 0x16, 0x8c, 0x14, 0x88,
-	0x01, 0x42, 0xc0, 0xe1, 0x01, 0x42, 0xe0, 0xe1, 0x01, 0x42, 0xf0, 0xe1,
-	0x93, 0xdd, 0x34, 0xca, 0x41, 0x85, 0x46, 0x8c, 0x34, 0xca, 0x06, 0x48,
-	0x00, 0xe0, 0x41, 0x46, 0xd0, 0xe0, 0x34, 0x88, 0x41, 0x83, 0x46, 0x8c,
-	0x34, 0x88, 0x01, 0x46, 0xc0, 0xe1, 0x01, 0x46, 0xe0, 0xe1, 0x01, 0x46,
-	0xf0, 0xe1, 0x09, 0x02, 0x20, 0xe0, 0x14, 0xca, 0x03, 0x42, 0x58, 0xe0,
-	0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x46, 0x4e, 0x08, 0xe1,
-	0x06, 0x4c, 0x0c, 0xe1, 0x0a, 0x9e, 0x14, 0x98, 0x05, 0x42, 0x44, 0xe0,
-	0x10, 0x00, 0xe1, 0x65, 0x03, 0xc0, 0x78, 0x41, 0x00, 0xe8, 0x08, 0x9c,
-	0x0b, 0xa1, 0x04, 0x98, 0x06, 0x02, 0x10, 0x80, 0x13, 0x40, 0xf8, 0x86,
-	0x65, 0x82, 0x00, 0x00, 0xe1, 0x65, 0x03, 0xc0, 0xa8, 0x40, 0x00, 0xe8,
-	0x14, 0x98, 0x04, 0x00, 0xa0, 0xfc, 0x03, 0x42, 0x00, 0xe7, 0x4c, 0x0c,
-	0x04, 0xf2, 0x93, 0xdd, 0x0a, 0x80, 0x93, 0xdd, 0x0c, 0x04, 0x00, 0xfa,
-	0x06, 0x02, 0xec, 0xe1, 0x64, 0x84, 0x15, 0x0c, 0x2c, 0xe0, 0x14, 0x02,
-	0xa0, 0xfc, 0x15, 0x4c, 0x2c, 0xe0, 0xd8, 0x40, 0x00, 0xe8, 0x14, 0xd8,
-	0x09, 0x82, 0x14, 0x02, 0x00, 0xfc, 0x1f, 0xa0, 0x1e, 0xd8, 0x01, 0x85,
-	0x0c, 0x0c, 0x00, 0xf2, 0xe8, 0x32, 0x2c, 0xff, 0x93, 0xdd, 0xc3, 0xc1,
-	0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x80, 0xf6, 0x01, 0x94, 0xe0, 0x08, 0x80,
-	0x4a, 0x40, 0x80, 0xe0, 0x45, 0x86, 0x06, 0x40, 0x0c, 0xe1, 0x04, 0x80,
-	0xc6, 0x02, 0x40, 0xe2, 0x09, 0x00, 0xd0, 0xe0, 0x14, 0x84, 0x1b, 0xa5,
-	0x15, 0x84, 0x07, 0xc5, 0x09, 0x82, 0x18, 0x41, 0x00, 0xe8, 0x46, 0x43,
-	0xfc, 0xe0, 0x14, 0x84, 0x19, 0x02, 0xd8, 0xe0, 0x19, 0x82, 0x0b, 0x83,
-	0x16, 0x00, 0x00, 0xc0, 0x01, 0x4c, 0x00, 0xc0, 0x0c, 0x0c, 0x00, 0xf2,
-	0x93, 0xdd, 0xc3, 0xc1, 0x4a, 0x00, 0x00, 0xe0, 0x0c, 0x00, 0x00, 0xe2,
-	0x93, 0xdd, 0xc3, 0xc1, 0x46, 0x40, 0x84, 0xe0, 0x11, 0xaf, 0x13, 0x40,
-	0x6c, 0xec, 0x11, 0xb3, 0x13, 0x40, 0x70, 0xec, 0xc6, 0x43, 0xf0, 0xe0,
-	0x13, 0x40, 0xdc, 0xec, 0xc6, 0x02, 0x24, 0xe0, 0x1c, 0x80, 0x93, 0xdd,
-	0x4c, 0x00, 0x00, 0xfa, 0xc8, 0x60, 0x7c, 0xef, 0xe8, 0x61, 0x7c, 0xef,
-	0x28, 0x7e, 0x80, 0xef, 0xc6, 0x40, 0x98, 0xe1, 0x11, 0x83, 0x16, 0x80,
-	0x46, 0x01, 0x10, 0xe1, 0x11, 0x81, 0x16, 0x80, 0x4c, 0x08, 0x00, 0xf2,
-	0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0x6b, 0x80, 0x04, 0x98,
-	0x7b, 0x82, 0x56, 0x42, 0xb4, 0xe0, 0x88, 0x84, 0x05, 0x00, 0x10, 0xe0,
-	0x09, 0x86, 0x0b, 0xa5, 0x46, 0x02, 0x00, 0x80, 0x06, 0x05, 0x00, 0x80,
-	0x25, 0x82, 0x0b, 0xa3, 0xa5, 0x80, 0x0b, 0xa1, 0x06, 0x00, 0xf4, 0xef,
-	0xd5, 0x84, 0x11, 0x85, 0x21, 0x91, 0x0b, 0x8e, 0x88, 0x74, 0x10, 0xef,
-	0x0b, 0xa1, 0xf5, 0x82, 0x0a, 0x9e, 0x1a, 0x9c, 0x24, 0x98, 0x07, 0xe0,
-	0x0f, 0xa2, 0x0e, 0xca, 0x0a, 0xde, 0x1a, 0xdc, 0x24, 0x98, 0x03, 0xb0,
-	0x07, 0xe0, 0x0f, 0xa2, 0x0e, 0xc8, 0x01, 0x81, 0x0c, 0x0c, 0x0c, 0xf2,
-	0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x7c, 0xfa, 0x46, 0x42, 0x9c, 0xe0,
-	0x0b, 0x02, 0x04, 0xe3, 0xf0, 0x1e, 0x30, 0xec, 0x0b, 0xa3, 0x35, 0x96,
-	0x8e, 0x01, 0x01, 0x60, 0x10, 0xc0, 0x0e, 0xfc, 0xc6, 0x05, 0xd0, 0xe1,
-	0x0b, 0x82, 0x31, 0x81, 0x10, 0x16, 0x00, 0xe5, 0x20, 0x10, 0x20, 0xe7,
-	0x0e, 0xbe, 0xb5, 0x85, 0x94, 0xfc, 0xa4, 0xbe, 0x82, 0x4c, 0x9c, 0xf0,
-	0x05, 0x0c, 0x40, 0xe0, 0x11, 0x89, 0x93, 0x8e, 0xa3, 0x8e, 0x58, 0x44,
-	0x00, 0xe8, 0x15, 0x0c, 0xc0, 0xf8, 0x04, 0x0c, 0x80, 0xfb, 0x0c, 0xed,
-	0x0b, 0x82, 0x1b, 0x8c, 0x48, 0x44, 0x00, 0xe8, 0x15, 0x10, 0x1c, 0xfc,
-	0x0e, 0xa8, 0x0b, 0x82, 0x1b, 0x8c, 0xd8, 0x43, 0x00, 0xe8, 0x71, 0x88,
-	0x0e, 0xa4, 0x0a, 0x0e, 0x40, 0xe0, 0x35, 0xf8, 0x04, 0xbe, 0x14, 0xbc,
-	0x81, 0xa0, 0x03, 0x8e, 0x0e, 0xbe, 0x04, 0xfc, 0x11, 0x82, 0x3b, 0x82,
-	0x03, 0x8e, 0x0e, 0xfc, 0x3b, 0xa9, 0x06, 0x0e, 0x00, 0xc0, 0x35, 0x5e,
-	0x00, 0xc0, 0xd5, 0xfa, 0xc6, 0x01, 0xd0, 0xe1, 0x7b, 0x80, 0x04, 0x9e,
-	0x11, 0x91, 0x98, 0x41, 0x00, 0xe8, 0x24, 0x9c, 0x46, 0x42, 0x9c, 0xe0,
-	0x6b, 0x82, 0x03, 0x4c, 0xc4, 0xe0, 0x11, 0x91, 0x0b, 0x84, 0xf8, 0x40,
-	0x00, 0xe8, 0x19, 0x0e, 0x20, 0xe5, 0x03, 0x4c, 0xc0, 0xe0, 0x0b, 0x82,
-	0x08, 0x72, 0xfc, 0xef, 0x01, 0x4c, 0x24, 0xf9, 0xf1, 0x98, 0x0c, 0x0c,
-	0x7c, 0xf2, 0x93, 0xdd, 0x4c, 0x00, 0x00, 0xfa, 0x48, 0x65, 0x2c, 0xef,
-	0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa,
-	0x6b, 0x82, 0x78, 0x6e, 0xfc, 0xee, 0x46, 0x42, 0xec, 0xe0, 0x24, 0x84,
-	0x24, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x83, 0xf5, 0x82, 0x24, 0x02,
-	0xa0, 0xe1, 0x14, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x85, 0x15, 0x82,
-	0x27, 0xe1, 0x24, 0x02, 0x80, 0xfa, 0x1d, 0xcc, 0x11, 0x89, 0x86, 0x02,
-	0x00, 0x80, 0x0c, 0x0c, 0x00, 0xf2, 0x18, 0x17, 0xfc, 0xfe, 0xc3, 0xc1,
-	0x0c, 0x04, 0x00, 0xfa, 0x06, 0x41, 0x8c, 0xe0, 0x1b, 0x00, 0xec, 0xe4,
-	0x1b, 0xa3, 0x75, 0x84, 0x11, 0x81, 0x8e, 0x05, 0x01, 0x60, 0x10, 0xc0,
-	0x00, 0x06, 0xc0, 0xe5, 0x95, 0x81, 0x44, 0x88, 0x1d, 0xee, 0x75, 0x80,
-	0x4e, 0xc1, 0x25, 0x81, 0x4e, 0xcd, 0x21, 0x88, 0x11, 0x82, 0x0a, 0x02,
-	0x40, 0xe0, 0xd5, 0xfc, 0x56, 0x00, 0x00, 0xe1, 0x18, 0x80, 0x1b, 0xa1,
-	0xc5, 0x84, 0x08, 0x82, 0x4a, 0x00, 0xfc, 0xfb, 0x45, 0x84, 0x86, 0x4d,
-	0x84, 0xe1, 0x04, 0x98, 0x05, 0x00, 0x10, 0xe0, 0x4a, 0x40, 0x80, 0xe0,
-	0x45, 0x82, 0x11, 0x81, 0x0b, 0x8c, 0x58, 0x76, 0x28, 0xef, 0x0b, 0x8c,
-	0x0c, 0x0c, 0x00, 0xf2, 0x88, 0x35, 0x28, 0xff, 0x0c, 0x0c, 0x00, 0xf2,
-	0x93, 0xdd, 0xc3, 0xc1, 0x46, 0x41, 0xfc, 0xe0, 0x04, 0x80, 0x09, 0x00,
-	0x80, 0xe0, 0x09, 0x9e, 0x0b, 0xa3, 0x75, 0x82, 0x46, 0x41, 0x80, 0xe1,
-	0x04, 0x80, 0xc6, 0x42, 0x8c, 0xe0, 0x04, 0xc2, 0x00, 0x40, 0x00, 0xf2,
-	0x07, 0xcf, 0x06, 0x84, 0x06, 0x40, 0x84, 0xe0, 0x15, 0x00, 0x28, 0xe5,
-	0x1c, 0xc2, 0x93, 0xdd, 0x0b, 0xa1, 0xc6, 0x00, 0xa0, 0xe1, 0x15, 0x00,
-	0x04, 0xf8, 0x05, 0x84, 0x21, 0x8b, 0x2c, 0x84, 0x14, 0x80, 0x2c, 0x84,
-	0x14, 0x82, 0x2c, 0x84, 0x15, 0x00, 0x10, 0xe0, 0x21, 0xa1, 0x21, 0x42,
-	0x10, 0xe0, 0x05, 0x00, 0x14, 0xe0, 0x01, 0x88, 0x75, 0x83, 0x21, 0x85,
-	0x2c, 0x84, 0x14, 0x80, 0x06, 0x46, 0x00, 0xe0, 0x2c, 0x84, 0x14, 0x82,
-	0x2c, 0x84, 0x14, 0xc0, 0x21, 0xa1, 0x21, 0x42, 0x20, 0xe0, 0x14, 0xc2,
-	0x31, 0x42, 0x20, 0xe0, 0x15, 0x00, 0x10, 0xe0, 0x21, 0x42, 0x20, 0xe0,
-	0x05, 0x00, 0x14, 0xe0, 0x01, 0x90, 0x06, 0x42, 0x00, 0xe0, 0x16, 0x80,
-	0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x7c, 0xfa, 0x4a, 0x40, 0x80, 0xe0,
-	0xf0, 0x1e, 0x30, 0xec, 0xe5, 0x82, 0xa6, 0x40, 0x00, 0xe1, 0x1a, 0x80,
-	0x2a, 0xc0, 0x3a, 0xc2, 0x13, 0x40, 0x10, 0xe0, 0x1a, 0x82, 0x23, 0x40,
-	0x18, 0xe0, 0x33, 0x40, 0x1c, 0xe0, 0x13, 0x40, 0x14, 0xe0, 0xf8, 0x61,
-	0x68, 0xef, 0xc6, 0x13, 0x00, 0xe1, 0x15, 0x12, 0x28, 0xf8, 0x0b, 0x02,
-	0x2c, 0xe0, 0x1b, 0x02, 0x24, 0xe0, 0x8a, 0x00, 0xa5, 0x64, 0x03, 0xc0,
-	0x35, 0x82, 0x0a, 0x4e, 0x9c, 0xe1, 0x1a, 0x03, 0x11, 0x6f, 0x02, 0xc0,
-	0xe8, 0x13, 0x01, 0x20, 0x00, 0xc0, 0x1f, 0xa0, 0x5a, 0x42, 0x80, 0xe0,
-	0x0a, 0x4e, 0x9c, 0xe1, 0x68, 0x13, 0x00, 0xa0, 0x09, 0x12, 0x78, 0xf8,
-	0xa1, 0x81, 0xf0, 0x02, 0x10, 0xe4, 0x07, 0xc4, 0x0c, 0xfc, 0xf0, 0x00,
-	0x20, 0xe4, 0xa6, 0x91, 0xa8, 0x53, 0x74, 0xef, 0x05, 0x12, 0x30, 0xf8,
-	0x25, 0x12, 0x28, 0xf8, 0x61, 0x87, 0x09, 0x00, 0x48, 0xe0, 0x81, 0x85,
-	0x09, 0x86, 0x0b, 0xa7, 0x26, 0x0c, 0x00, 0xc0, 0x0b, 0xa1, 0x0b, 0x04,
-	0x28, 0xe0, 0x16, 0x0c, 0x00, 0x80, 0x03, 0x52, 0x04, 0xf8, 0x0b, 0x04,
-	0x20, 0xe0, 0x0c, 0xa6, 0x1b, 0x04, 0x2c, 0xe0, 0x3b, 0x04, 0x28, 0xe0,
-	0x4b, 0x04, 0x20, 0xe0, 0x13, 0x86, 0x3b, 0x04, 0x24, 0xe0, 0x10, 0x0a,
-	0x04, 0xec, 0x1a, 0xfc, 0x33, 0x88, 0x30, 0x06, 0x04, 0xec, 0x12, 0x4e,
-	0x94, 0xf0, 0x32, 0x48, 0x84, 0xf0, 0x4c, 0xe4, 0x7c, 0xa4, 0xcb, 0x04,
-	0x28, 0xe0, 0x14, 0x08, 0x84, 0xe1, 0xcd, 0xc9, 0xc2, 0x58, 0x90, 0x91,
-	0x42, 0x4e, 0x94, 0x90, 0xc3, 0x52, 0x04, 0x98, 0x73, 0x52, 0x00, 0x80,
-	0x5b, 0x04, 0x20, 0xe0, 0x5d, 0xc9, 0x52, 0x40, 0x90, 0x91, 0x42, 0x48,
-	0x8c, 0x90, 0x03, 0x52, 0x04, 0x80, 0x43, 0x52, 0x08, 0x80, 0x3b, 0x04,
-	0x2c, 0xe0, 0x49, 0x04, 0xb8, 0xe0, 0x33, 0x52, 0x1c, 0xf8, 0x2b, 0x04,
-	0x24, 0xe0, 0x4b, 0xab, 0x23, 0x52, 0x18, 0xf8, 0x65, 0x8a, 0x4b, 0xa9,
-	0xe5, 0x90, 0x4b, 0xa7, 0x22, 0x44, 0x84, 0xd0, 0x32, 0x46, 0x84, 0xd0,
-	0x33, 0x52, 0x1c, 0xd8, 0x23, 0x52, 0x18, 0xd8, 0x95, 0x96, 0x20, 0x44,
-	0xf9, 0x73, 0xff, 0xc0, 0x27, 0xc3, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8,
-	0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x58, 0x52,
-	0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc3,
-	0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02,
-	0x80, 0xfb, 0x2b, 0x8c, 0x68, 0x51, 0x74, 0xef, 0xc5, 0x87, 0x20, 0x44,
-	0xe1, 0x73, 0xff, 0xc0, 0x27, 0xc7, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8,
-	0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x78, 0x57,
-	0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc7,
-	0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02,
-	0x80, 0xfb, 0x2b, 0x8c, 0x88, 0x56, 0x74, 0xef, 0xe5, 0x83, 0x20, 0x44,
-	0xf1, 0x73, 0xff, 0xc0, 0x27, 0xc5, 0x23, 0x82, 0x23, 0x52, 0x18, 0xf8,
-	0x24, 0x02, 0x80, 0xfb, 0x04, 0x00, 0x80, 0xfb, 0x2b, 0x8c, 0x18, 0x52,
-	0x74, 0xef, 0x1b, 0x12, 0x1c, 0xf8, 0x2a, 0xfc, 0x0c, 0xe4, 0x17, 0xc5,
-	0x13, 0x84, 0x13, 0x52, 0x1c, 0xf8, 0x0b, 0x12, 0x04, 0xf8, 0x14, 0x02,
-	0x80, 0xfb, 0x2b, 0x8c, 0x28, 0x51, 0x74, 0xef, 0x7b, 0x80, 0x7c, 0xa4,
-	0x08, 0x91, 0xa3, 0x52, 0x1c, 0xe0, 0xa3, 0x52, 0x24, 0xe0, 0x0b, 0xa1,
-	0x83, 0x52, 0x1c, 0x80, 0x83, 0x52, 0x24, 0x80, 0x89, 0x12, 0x78, 0xf8,
-	0xf6, 0x57, 0xfc, 0xef, 0x6b, 0x12, 0x1c, 0xf8, 0xab, 0x12, 0x18, 0xf8,
-	0xd6, 0x57, 0xfc, 0x8f, 0x8b, 0xa3, 0xa0, 0x40, 0x00, 0x9c, 0xa5, 0x86,
-	0x64, 0x00, 0x80, 0xfb, 0x1b, 0x90, 0xf8, 0x7d, 0xf8, 0xee, 0x6b, 0x80,
-	0xa4, 0x00, 0x80, 0xfb, 0x1b, 0x90, 0x98, 0x7d, 0xf8, 0xee, 0x15, 0x12,
-	0x28, 0xf8, 0x19, 0x02, 0xb8, 0xe0, 0x1b, 0xad, 0x95, 0x82, 0x1a, 0xa6,
-	0xa0, 0x44, 0xf9, 0x73, 0xff, 0xc0, 0x27, 0xc3, 0x13, 0x94, 0x10, 0x02,
-	0x08, 0xec, 0x1c, 0xe4, 0x23, 0x52, 0x18, 0xf8, 0x1b, 0x12, 0x04, 0xf8,
-	0x03, 0x96, 0x03, 0x52, 0x28, 0xe0, 0x1c, 0xe6, 0x0a, 0xa6, 0x1a, 0xe4,
-	0x63, 0x96, 0x63, 0x52, 0x20, 0xe0, 0x73, 0x52, 0x10, 0xe0, 0x03, 0x52,
-	0x14, 0xe0, 0x13, 0x52, 0x18, 0xe0, 0x98, 0x52, 0x74, 0xef, 0x09, 0x12,
-	0x8c, 0xe0, 0x0b, 0xa1, 0x01, 0x81, 0x01, 0x52, 0x90, 0xe0, 0x65, 0x82,
-	0x05, 0x12, 0x30, 0xf8, 0x09, 0x00, 0xa8, 0xe0, 0x0a, 0x00, 0x0c, 0xf8,
-	0x16, 0x00, 0x00, 0xc0, 0x01, 0x52, 0x90, 0xc0, 0x46, 0x41, 0x84, 0xe0,
-	0x0a, 0x80, 0x0a, 0x4e, 0x9c, 0xe9, 0x1a, 0x00, 0x08, 0xe0, 0x38, 0x01,
-	0x01, 0x20, 0x00, 0xc0, 0x0b, 0x12, 0x1c, 0xe0, 0x1b, 0x12, 0x24, 0xe0,
-	0x2b, 0x12, 0x28, 0xe0, 0x03, 0x52, 0x2c, 0xe0, 0x0b, 0x12, 0x20, 0xe0,
-	0x13, 0x52, 0x34, 0xe0, 0x23, 0x52, 0x38, 0xe0, 0x03, 0x52, 0x30, 0xe0,
-	0x0c, 0x00, 0x00, 0xe2, 0xf1, 0x98, 0x0c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd,
-	0x13, 0xa9, 0x00, 0x00, 0xa8, 0xc1, 0x40, 0x00, 0x68, 0x04, 0xa0, 0xe0,
-	0x40, 0x6c, 0x40, 0x00, 0xe8, 0x34, 0xc8, 0xe0, 0xfc, 0x91, 0x40, 0x00,
-	0x68, 0x1f, 0xb8, 0xe0, 0x30, 0x16, 0x41, 0x00, 0x28, 0x39, 0x74, 0xe0,
-	0xb0, 0x7e, 0x40, 0x00, 0xe8, 0x38, 0xc0, 0xe0, 0x30, 0x04, 0x41, 0x00,
-	0x48, 0x1b, 0x80, 0xe0, 0x30, 0x2e, 0x40, 0x00, 0x88, 0x0c, 0xec, 0xe0,
-	0x10, 0x9f, 0x40, 0x00, 0x88, 0x08, 0xb4, 0xe0, 0x10, 0x01, 0x41, 0x00,
-	0x68, 0x01, 0x84, 0xe0, 0x54, 0xd6, 0x40, 0x00, 0xc8, 0x1a, 0x98, 0xe0,
-	0xd0, 0xc8, 0x40, 0x00, 0x68, 0x08, 0xa0, 0xe0, 0x80, 0xdb, 0x40, 0x00,
-	0xe8, 0x35, 0x94, 0xe0, 0x74, 0xff, 0x40, 0x00, 0xa8, 0x11, 0x80, 0xe0,
-	0xf8, 0x89, 0x40, 0x00, 0x88, 0x16, 0xbc, 0xe0, 0x00, 0x90, 0x40, 0x00,
-	0x08, 0x35, 0xb8, 0xe0, 0x7c, 0x73, 0x40, 0x00, 0x88, 0x1b, 0xc8, 0xe0,
-	0xf4, 0xff, 0x40, 0x00, 0x68, 0x39, 0x80, 0xe0, 0xa4, 0xa4, 0x40, 0x00,
-	0xa8, 0x16, 0xb0, 0xe0, 0x50, 0xc9, 0x40, 0x00, 0x28, 0x3a, 0x98, 0xe0,
-	0x00, 0xb9, 0x00, 0x00, 0xb6, 0x85, 0x00, 0x00,
-};
+#include "vd55g-fw.h"
 
 static const char * const vd55g1_tp_menu[] = {
 	"Disabled",
-- 
2.55.0


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

* [PATCH 2/7] dt-bindings: media: i2c: st,vd55g: rename and add VD55G0 variant
  2026-09-02 20:45 [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Peter Marshall
  2026-09-02 20:45 ` [PATCH 1/7] media: i2c: vd55g: move patch array to a separate firmware header Peter Marshall
@ 2026-09-02 20:45 ` Peter Marshall
  2026-09-03 13:36   ` Krzysztof Kozlowski
  2026-09-02 20:45 ` [PATCH 3/7] media: ipu-bridge: Add VD55G0 to the list of supported sensors Peter Marshall
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 14+ messages in thread
From: Peter Marshall @ 2026-09-02 20:45 UTC (permalink / raw)
  To: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot
  Cc: linux-kernel, devicetree, linux-media, platform-driver-x86,
	Peter Marshall, Rob Herring, Krzysztof Kozlowski, Conor Dooley

Document the STMicroelectronics VD55G0 camera sensor. Rename the device
tree bindings to represent a generic VD55G family sensor.

Signed-off-by: Peter Marshall <pm@petermarshall.ca>
---
 .../i2c/{st,vd55g1.yaml => st,vd55g.yaml}     | 30 +++++++++++++++----
 1 file changed, 24 insertions(+), 6 deletions(-)
 rename Documentation/devicetree/bindings/media/i2c/{st,vd55g1.yaml => st,vd55g.yaml} (76%)

diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g.yaml
similarity index 76%
rename from Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
rename to Documentation/devicetree/bindings/media/i2c/st,vd55g.yaml
index 060ac6829..bd6feeb6c 100644
--- a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g.yaml
@@ -2,19 +2,21 @@
 # Copyright (c) 2025 STMicroelectronics SA.
 %YAML 1.2
 ---
-$id: http://devicetree.org/schemas/media/i2c/st,vd55g1.yaml#
+$id: http://devicetree.org/schemas/media/i2c/st,vd55g.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: STMicroelectronics VD55G1 Global Shutter Image Sensor
+title: STMicroelectronics VD55G Family Global Shutter Image Sensors
 
 maintainers:
   - Benjamin Mugnier <benjamin.mugnier@foss.st.com>
   - Sylvain Petinot <sylvain.petinot@foss.st.com>
+  - Peter Marshall <pm@petermarshall.ca>
 
 description: |-
- The STMicroelectronics VD55G1 is a global shutter image sensor with an active
- array size of 804H x 704V. It is programmable through I2C interface. The I2C
- address is fixed to 0x10.
+ The STMicroelectronics VD55G family (VD55G0, VD55G1, and VD65G4) consists of
+ global shutter image sensors. The VD55G0 has an array size of 644H x 604V.
+ The VD55G1 and VD65G4 have an array size of 804H x 704V. They are programmable
+ through I2C interface. The I2C address is fixed to 0x10.
 
  Image data is sent through MIPI CSI-2, which is configured as only 1 data
  lane. The sensor provides 4 GPIOS that can be used for external LED signal
@@ -23,13 +25,29 @@ description: |-
 allOf:
   - $ref: /schemas/media/video-interface-devices.yaml#
 
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: st,vd55g0
+    then:
+      properties:
+        port:
+          properties:
+            endpoint:
+              properties:
+                link-frequencies:
+                  items:
+                    minimum: 375000000
+
 properties:
   compatible:
     enum:
+      - st,vd55g0
       - st,vd55g1
       - st,vd65g4
     description:
-      VD55G1 is the monochrome variant, while VD65G4 is the color one.
+      VD55G0 and VD55G1 are monochrome variants, while VD65G4 is the color variant.
 
   reg:
     maxItems: 1
-- 
2.55.0


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

* [PATCH 3/7] media: ipu-bridge: Add VD55G0 to the list of supported sensors
  2026-09-02 20:45 [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Peter Marshall
  2026-09-02 20:45 ` [PATCH 1/7] media: i2c: vd55g: move patch array to a separate firmware header Peter Marshall
  2026-09-02 20:45 ` [PATCH 2/7] dt-bindings: media: i2c: st,vd55g: rename and add VD55G0 variant Peter Marshall
@ 2026-09-02 20:45 ` Peter Marshall
  2026-09-02 20:45 ` [PATCH 4/7] platform/x86: int3472: Add VD55G0 supply GPIO mapping Peter Marshall
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Peter Marshall @ 2026-09-02 20:45 UTC (permalink / raw)
  To: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot
  Cc: linux-kernel, devicetree, linux-media, platform-driver-x86,
	Peter Marshall

Add SMO55F0 ACPI HID for the ST VD55G0 sensor to the list of supported
sensors. The sensor is found with this HID on a Microsoft Surface Pro
9 tablet. Use the lowest supported link frequency.

Signed-off-by: Peter Marshall <pm@petermarshall.ca>
---
 drivers/media/pci/intel/ipu-bridge.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 88581a4c0..527d1a81d 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -95,6 +95,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
 	IPU_SENSOR_CONFIG("OVTI5675", 1, 450000000),
 	/* Omnivision OV8856 */
 	IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
+	/* STMicroelectronics VD55G0 */
+	IPU_SENSOR_CONFIG("SMO55F0", 1,  380000000),
 	/* Sony IMX471 */
 	IPU_SENSOR_CONFIG("SONY471A", 1, 200000000),
 	/* Toshiba T4KA3 */
-- 
2.55.0


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

* [PATCH 4/7] platform/x86: int3472: Add VD55G0 supply GPIO mapping
  2026-09-02 20:45 [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Peter Marshall
                   ` (2 preceding siblings ...)
  2026-09-02 20:45 ` [PATCH 3/7] media: ipu-bridge: Add VD55G0 to the list of supported sensors Peter Marshall
@ 2026-09-02 20:45 ` Peter Marshall
  2026-09-02 20:45 ` [PATCH 5/7] media: i2c: st-vd55g: Add generic driver and VD55G0 support Peter Marshall
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Peter Marshall @ 2026-09-02 20:45 UTC (permalink / raw)
  To: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot
  Cc: linux-kernel, devicetree, linux-media, platform-driver-x86,
	Peter Marshall, Daniel Scally, Hans de Goede, Ilpo Järvinen

The vd55g sensor drivers / DT bindings expect non-standard supply names.
Add a mapping to enable them.

Signed-off-by: Peter Marshall <pm@petermarshall.ca>
---
 drivers/platform/x86/intel/int3472/discrete.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index 115bb3757..1dc6a6722 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -164,6 +164,24 @@ static const struct int3472_gpio_map int3472_gpio_map[] = {
 		.con_id = "dvdd",
 		.enable_time_us = 45 * USEC_PER_MSEC,
 	},
+	{
+		.hid = "SMO55F0",
+		.type_from = INT3472_GPIO_TYPE_HANDSHAKE,
+		.type_to = INT3472_GPIO_TYPE_HANDSHAKE,
+		.con_id = "vcore",
+	},
+	{
+		.hid = "SMO55F0",
+		.type_from = INT3472_GPIO_TYPE_DOVDD,
+		.type_to = INT3472_GPIO_TYPE_DOVDD,
+		.con_id = "vddio",
+	},
+	{
+		.hid = "SMO55F0",
+		.type_from = INT3472_GPIO_TYPE_POWER_ENABLE,
+		.type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
+		.con_id = "vana",
+	},
 };
 
 static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type,
-- 
2.55.0


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

* [PATCH 5/7] media: i2c: st-vd55g: Add generic driver and VD55G0 support
  2026-09-02 20:45 [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Peter Marshall
                   ` (3 preceding siblings ...)
  2026-09-02 20:45 ` [PATCH 4/7] platform/x86: int3472: Add VD55G0 supply GPIO mapping Peter Marshall
@ 2026-09-02 20:45 ` Peter Marshall
  2026-09-03 13:41   ` Krzysztof Kozlowski
  2026-09-02 20:45 ` [PATCH 6/7] media: i2c: st-vd55g: Remove legacy vd55g1 remnants Peter Marshall
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 14+ messages in thread
From: Peter Marshall @ 2026-09-02 20:45 UTC (permalink / raw)
  To: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot
  Cc: linux-kernel, devicetree, linux-media, platform-driver-x86,
	Peter Marshall

Rewrite the existing vd55g1 module supporting ST VD55G1 and VD65G4 to
make it generic for ST VD55G family global-shutter camera sensors.

Registers and supported features differ between sensor generations.
Replace the static register addresses and revision-specific branches
with a per-chip info struct.

Add support for the VD55G0 monochrome camera sensor. The VD55G0 has
a native resolution of 644 x 604 pixels and does not support HDR via
internal subtraction.

Add a quirk limiting exposure duty cycle with strobe illumination
enabled to avoid tripping overcurrent protection.

Special thanks to GitHub user @femito1 (Fernando Rimoli) for testing and helping
with bug fixes.

Signed-off-by: Peter Marshall <pm@petermarshall.ca>
---
 drivers/media/i2c/Kconfig  |   11 +
 drivers/media/i2c/Makefile |    1 +
 drivers/media/i2c/vd55g.c  | 2166 ++++++++++++++++++++++++++++++++++++
 3 files changed, 2178 insertions(+)
 create mode 100644 drivers/media/i2c/vd55g.c

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5d173e0ec..b2a39ff4b 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -815,6 +815,17 @@ config VIDEO_T4KA3
 	  To compile this driver as a module, choose M here: the
 	  module will be called t4ka3.
 
+config VIDEO_VD55G
+	tristate "ST VD55G family sensor support"
+	select V4L2_CCI_I2C
+	depends on GPIOLIB
+	help
+	  This is a Video4Linux2 sensor driver for the ST VD55G family
+	  of camera sensors.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called vd55g.
+
 config VIDEO_VD55G1
 	tristate "ST VD55G1 sensor support"
 	select V4L2_CCI_I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index e45359efe..d87b64893 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -164,6 +164,7 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o
 obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o
 obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
+obj-$(CONFIG_VIDEO_VD55G) += vd55g.o
 obj-$(CONFIG_VIDEO_VD55G1) += vd55g1.o
 obj-$(CONFIG_VIDEO_VD56G3) += vd56g3.o
 obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o
diff --git a/drivers/media/i2c/vd55g.c b/drivers/media/i2c/vd55g.c
new file mode 100644
index 000000000..627e2d31f
--- /dev/null
+++ b/drivers/media/i2c/vd55g.c
@@ -0,0 +1,2166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for VD55G family global shutter sensors
+ *
+ * Copyright (C) 2025 STMicroelectronics SA
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+#include <linux/firmware.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* Register Map */
+#define VD55G_REG_MODEL_ID CCI_REG32_LE(0x0000)
+#define VD55G_REG_REVISION CCI_REG16_LE(0x0004)
+#define VD55G_REG_FWPATCH_START_ADDR CCI_REG8(0x2000)
+#define VD55G_SYSTEM_FSM_READY_TO_BOOT 0x01
+#define VD55G_SYSTEM_FSM_SW_STBY 0x02
+#define VD55G_SYSTEM_FSM_STREAMING 0x03
+#define VD55G_REG_BOOT CCI_REG8(0x0200)
+#define VD55G_BOOT_BOOT 1
+#define VD55G_BOOT_PATCH 2
+#define VD55G_REG_STBY CCI_REG8(0x0201)
+#define VD55G_STBY_START_STREAM 1
+#define VD55G_REG_STREAMING CCI_REG8(0x0202)
+#define VD55G_STREAMING_STOP_STREAM 1
+#define VD55G_REG_EXT_CLOCK CCI_REG32_LE(0x0220)
+#define VD55G_REG_LINE_LENGTH CCI_REG16_LE(0x0300)
+#define VD55G_REG_ORIENTATION CCI_REG8(0x0302)
+#define VD55G_REG_FORMAT_CTRL CCI_REG8(0x030a)
+#define VD55G_REG_OIF_CTRL CCI_REG16_LE(0x030c)
+#define VD55G_REG_OIF_IMG_CTRL CCI_REG8(0x030f)
+#define VD55G_REG_MIPI_DATA_RATE CCI_REG32_LE(0x0224)
+#define VD55G_PATGEN_TYPE_SHIFT 4
+#define VD55G_PATGEN_ENABLE BIT(0)
+#define VD55G_READOUT_CTRL_BIN_MODE_NORMAL 0
+#define VD55G_READOUT_CTRL_BIN_MODE_DIGITAL_X2 1
+#define VD55G_DUSTER_ENABLE BIT(0)
+#define VD55G_DUSTER_DISABLE 0
+#define VD55G_DUSTER_DYN_ENABLE BIT(1)
+#define VD55G_DUSTER_RING_ENABLE BIT(4)
+#define VD55G1_EXPOSURE_USE_CASES_MULTI_CONTEXT BIT(2)
+#define VD55G1_EXPOSURE_MAX_COARSE_SUB 446
+
+#define VD55G_REG_EXP_MODE(sensor, ctx) \
+	CCI_REG8(vd55g_get_ctx_addr(sensor, REG_CTX_EXP_MODE, ctx))
+#define VD55G_REG_FRAME_LENGTH(sensor, ctx) \
+	CCI_REG32_LE(vd55g_get_ctx_addr(sensor, REG_CTX_FRAME_LENGTH, ctx))
+#define VD55G_REG_X_START(sensor, ctx) \
+	CCI_REG16_LE(vd55g_get_ctx_addr(sensor, REG_CTX_X_START, ctx))
+#define VD55G_REG_X_WIDTH_OR_END(sensor, ctx) \
+	CCI_REG16_LE(vd55g_get_ctx_addr(sensor, REG_CTX_X_WIDTH_OR_END, ctx))
+#define VD55G_REG_Y_START(sensor, ctx) \
+	CCI_REG16_LE(vd55g_get_ctx_addr(sensor, REG_CTX_Y_START, ctx))
+#define VD55G_REG_Y_HEIGHT_OR_END(sensor, ctx) \
+	CCI_REG16_LE(vd55g_get_ctx_addr(sensor, REG_CTX_Y_HEIGHT_OR_END, ctx))
+#define VD55G_REG_GPIO_CTRL(sensor, ctx, led) \
+	CCI_REG8(vd55g_get_ctx_addr(sensor, REG_CTX_GPIO_0_CTRL, ctx) + (led))
+#define VD55G_GPIO_MODE_IN 0x01
+#define VD55G_GPIO_MODE_STROBE 0x02
+#define VD55G_REG_VT_MODE(sensor, ctx) \
+	CCI_REG8(vd55g_get_ctx_addr(sensor, REG_CTX_VT_MODE, ctx))
+#define VD55G1_VT_MODE_NORMAL 0
+#define VD55G1_VT_MODE_SUBTRACTION 1
+#define VD55G_REG_MASK_FRAME_CTRL(sensor, ctx) \
+	CCI_REG8(vd55g_get_ctx_addr(sensor, REG_CTX_MASK_FRAME_CTRL, ctx))
+#define VD55G1_MASK_FRAME_CTRL_OUTPUT 0
+#define VD55G1_MASK_FRAME_CTRL_MASK 1
+#define VD55G_REG_EXPOSURE_INSTANCE(sensor, ctx) \
+	CCI_REG32_LE(vd55g_get_ctx_addr(sensor, REG_CTX_EXPOSURE_INSTANCE, ctx))
+
+#define VD55G0_WIDTH 644
+#define VD55G0_HEIGHT 604
+#define VD55G1_WIDTH 804
+#define VD55G1_HEIGHT 704
+#define VD55G_MODE_DEF 0
+#define VD55G_NB_GPIOS 4
+#define VD55G_MBUS_CODE_DEF 0
+#define VD55G_DGAIN_DEF 256
+#define VD55G_AGAIN_DEF 19
+#define VD55G_EXPO_MAX_TERM 64
+#define VD55G_EXPO_DEF 500
+#define VD55G_LINE_LENGTH_MIN 1200
+#define VD55G1_LINE_LENGTH_SUB_MIN 1344
+#define VD55G_VBLANK_MIN 86
+#define VD55G_VBLANK_MAX 0xffff
+#define VD55G_FRAME_LENGTH_DEF 1860 /* 60 fps */
+#define VD55G_MIPI_MARGIN 900
+#define VD55G_XCLK_FREQ_MIN (6 * HZ_PER_MHZ)
+#define VD55G_XCLK_FREQ_MAX (27 * HZ_PER_MHZ)
+#define VD55G1_MIPI_RATE_MIN (250 * MEGA)
+#define VD55G1_MIPI_RATE_MAX (1200 * MEGA)
+#define VD55G0_MIPI_RATE_MIN (750 * MEGA)
+#define VD55G0_MIPI_RATE_MAX (1200 * MEGA)
+
+static const char *const vd55g_tp_menu[] = {
+	"Disabled",
+	"Diagonal Grey Scale",
+	"Pseudo-random Noise",
+};
+
+static const s64 vd55g_ev_bias_menu[] = {
+	-3000, -2500, -2000, -1500, -1000, -500, 0,
+	500,   1000,  1500,  2000,  2500,  3000,
+};
+
+static const char *const vd55g1_hdr_menu[] = {
+	"No HDR",
+	/*
+	 * This mode acquires 2 frames on the sensor, the first one is ditched
+	 * out and only used for auto exposure data, the second one is output to
+	 * the host
+	 */
+	"Internal subtraction",
+};
+
+static const char *const vd55g_supply_name[] = {
+	"vcore",
+	"vddio",
+	"vana",
+};
+
+enum vd55g1_hdr_mode {
+	VD55G1_NO_HDR,
+	VD55G1_HDR_SUB,
+};
+
+struct vd55g_mode {
+	u32 width;
+	u32 height;
+};
+
+static const u32 vd55g_mbus_formats_mono[] = {
+	MEDIA_BUS_FMT_Y8_1X8,
+	MEDIA_BUS_FMT_Y10_1X10,
+};
+
+/* Format order is : no flip, hflip, vflip, both */
+static const u32 vd55g_mbus_formats_bayer[][4] = {
+	{
+		MEDIA_BUS_FMT_SRGGB8_1X8,
+		MEDIA_BUS_FMT_SGRBG8_1X8,
+		MEDIA_BUS_FMT_SGBRG8_1X8,
+		MEDIA_BUS_FMT_SBGGR8_1X8,
+	},
+	{
+		MEDIA_BUS_FMT_SRGGB10_1X10,
+		MEDIA_BUS_FMT_SGRBG10_1X10,
+		MEDIA_BUS_FMT_SGBRG10_1X10,
+		MEDIA_BUS_FMT_SBGGR10_1X10,
+	},
+};
+
+static const struct vd55g_mode vd55g0_supported_modes[] = {
+	{
+		.width = VD55G0_WIDTH,
+		.height = VD55G0_HEIGHT,
+	},
+	{
+		.width = 640,
+		.height = 600,
+	},
+	{
+		.width = 640,
+		.height = 480,
+	},
+	{
+		.width = 320,
+		.height = 240,
+	},
+};
+
+static const struct vd55g_mode vd55g1_supported_modes[] = {
+	{
+		.width = VD55G1_WIDTH,
+		.height = VD55G1_HEIGHT,
+	},
+	{
+		.width = 800,
+		.height = VD55G1_HEIGHT,
+	},
+	{
+		.width = 800,
+		.height = 600,
+	},
+	{
+		.width = 640,
+		.height = 480,
+	},
+	{
+		.width = 320,
+		.height = 240,
+	},
+};
+
+enum vd55g_expo_state {
+	VD55G1_EXP_AUTO,
+	VD55G1_EXP_FREEZE,
+	VD55G1_EXP_MANUAL,
+	VD55G1_EXP_SINGLE_STEP,
+	VD55G1_EXP_BYPASS,
+};
+
+struct vd55g_vblank_limits {
+	u16 min;
+	u16 def;
+	u16 max;
+};
+
+struct vd55g_frame_timings {
+	u16 frame_length;
+	u16 expo_max;
+};
+
+enum vd55g_reg_id {
+	REG_SYSTEM_FSM,
+	REG_ISL_ENABLE,
+	REG_FWPATCH_REVISION,
+	REG_PATGEN_CTRL,
+	REG_READOUT_CTRL,
+	REG_DUSTER_CTRL,
+	REG_AE_TARGET_PERCENTAGE,
+	REG_MANUAL_ANALOG_GAIN,
+	REG_MANUAL_COARSE_EXPOSURE,
+	REG_MANUAL_DIGITAL_GAIN,
+	REG_APPLIED_COARSE_EXPOSURE,
+	REG_APPLIED_ANALOG_GAIN,
+	REG_APPLIED_DIGITAL_GAIN,
+	REG_AE_FORCE_COLDSTART,
+	REG_AE_COLDSTART_EXPOSURE_US,
+	REG_AE_COLDSTART_COARSE_EXPOSURE,
+	/* HDR-specific */
+	REG_NEXT_CTX,
+	REG_EXPOSURE_USE_CASES,
+	REG_EXPOSURE_MAX_COARSE,
+	REG_CTX_REPEAT_COUNT_CTX0,
+	REG_CTX_REPEAT_COUNT_CTX1,
+
+	/* Context-specific register bases */
+	REG_CTX_STRIDE, /* Not a real register, stores the context bank offset */
+	REG_CTX_REPEAT_COUNT,
+	REG_CTX_EXP_MODE,
+	REG_CTX_FRAME_LENGTH,
+	REG_CTX_X_START,
+	REG_CTX_X_WIDTH_OR_END,
+	REG_CTX_Y_START,
+	REG_CTX_Y_HEIGHT_OR_END,
+	REG_CTX_GPIO_0_CTRL,
+	/* HDR-specific */
+	REG_CTX_VT_MODE,
+	REG_CTX_MASK_FRAME_CTRL,
+	REG_CTX_EXPOSURE_INSTANCE,
+
+	REG_MAX_INDEX /* Keeps track of enum size */
+};
+
+static const int vd55g0_reg_map[REG_MAX_INDEX] = {
+	[REG_SYSTEM_FSM] = CCI_REG8(0x002c),
+	[REG_ISL_ENABLE] = CCI_REG8(0x0329),
+	[REG_FWPATCH_REVISION] = CCI_REG16_LE(0x0022),
+	[REG_PATGEN_CTRL] = CCI_REG16_LE(0x0400),
+	[REG_READOUT_CTRL] = CCI_REG8(0x047a),
+	[REG_DUSTER_CTRL] = CCI_REG8(0x0316),
+	[REG_AE_TARGET_PERCENTAGE] = CCI_REG8(0x0440),
+	[REG_MANUAL_ANALOG_GAIN] = CCI_REG8(0x044d),
+	[REG_MANUAL_COARSE_EXPOSURE] = CCI_REG16_LE(0x044e),
+	[REG_MANUAL_DIGITAL_GAIN] = CCI_REG16_LE(0x0450),
+	[REG_APPLIED_COARSE_EXPOSURE] = CCI_REG16_LE(0x0064),
+	[REG_APPLIED_ANALOG_GAIN] = CCI_REG16_LE(0x0066),
+	[REG_APPLIED_DIGITAL_GAIN] = CCI_REG16_LE(0x0068),
+	[REG_AE_FORCE_COLDSTART] = CCI_REG8(0x042c),
+	[REG_AE_COLDSTART_COARSE_EXPOSURE] = CCI_REG32_LE(0x042e),
+	[REG_NEXT_CTX] = CCI_REG16_LE(0x0478),
+	[REG_EXPOSURE_MAX_COARSE] = CCI_REG16_LE(0x096c),
+	[REG_CTX_REPEAT_COUNT_CTX0] = CCI_REG16_LE(0x0476),
+	[REG_CTX_REPEAT_COUNT_CTX1] = CCI_REG16_LE(0x04a6),
+
+	/* Context properties */
+	[REG_CTX_STRIDE] = 0x30,
+	[REG_CTX_EXP_MODE] = 0x044c,
+	[REG_CTX_FRAME_LENGTH] = 0x0458,
+	[REG_CTX_X_START] = 0x045e,
+	[REG_CTX_X_WIDTH_OR_END] = 0x0460,
+	[REG_CTX_Y_START] = 0x0462,
+	[REG_CTX_Y_HEIGHT_OR_END] = 0x0464,
+	[REG_CTX_GPIO_0_CTRL] = 0x0467,
+};
+
+static const int vd55g1_reg_map[REG_MAX_INDEX] = {
+	[REG_SYSTEM_FSM] = CCI_REG8(0x001c),
+	[REG_ISL_ENABLE] = CCI_REG8(0x0326),
+	[REG_FWPATCH_REVISION] = CCI_REG16_LE(0x0012),
+	[REG_PATGEN_CTRL] = CCI_REG16_LE(0x0304),
+	[REG_READOUT_CTRL] = CCI_REG8(0x057e),
+	[REG_DUSTER_CTRL] = CCI_REG8(0x03ae),
+	[REG_AE_TARGET_PERCENTAGE] = CCI_REG8(0x0486),
+	[REG_MANUAL_ANALOG_GAIN] = CCI_REG8(0x0501),
+	[REG_MANUAL_COARSE_EXPOSURE] = CCI_REG16_LE(0x0502),
+	[REG_MANUAL_DIGITAL_GAIN] = CCI_REG16_LE(0x0504),
+	[REG_APPLIED_COARSE_EXPOSURE] = CCI_REG16_LE(0x00e8),
+	[REG_APPLIED_ANALOG_GAIN] = CCI_REG16_LE(0x00ea),
+	[REG_APPLIED_DIGITAL_GAIN] = CCI_REG16_LE(0x00ec),
+	[REG_AE_FORCE_COLDSTART] = CCI_REG8(0x0308),
+	[REG_AE_COLDSTART_EXPOSURE_US] = CCI_REG32_LE(0x0374),
+	[REG_NEXT_CTX] = CCI_REG16_LE(0x03e4),
+	[REG_EXPOSURE_USE_CASES] = CCI_REG8(0x0312),
+	[REG_EXPOSURE_MAX_COARSE] = CCI_REG16_LE(0x0372),
+	[REG_CTX_REPEAT_COUNT_CTX0] = CCI_REG16_LE(0x03dc),
+	[REG_CTX_REPEAT_COUNT_CTX1] = CCI_REG16_LE(0x03de),
+
+	/* Context properties */
+	[REG_CTX_STRIDE] = 0x50,
+	[REG_CTX_EXP_MODE] = 0x0500,
+	[REG_CTX_FRAME_LENGTH] = 0x050c,
+	[REG_CTX_X_START] = 0x0514,
+	[REG_CTX_X_WIDTH_OR_END] = 0x0516,
+	[REG_CTX_Y_START] = 0x0510,
+	[REG_CTX_Y_HEIGHT_OR_END] = 0x0512,
+	[REG_CTX_GPIO_0_CTRL] = 0x051d,
+	[REG_CTX_VT_MODE] = 0x0536,
+	[REG_CTX_MASK_FRAME_CTRL] = 0x0537,
+	[REG_CTX_EXPOSURE_INSTANCE] = 0x052d,
+};
+
+#include "vd55g-fw.h"
+
+struct vd55g_patch_header {
+	__le16 patch_size;
+	u8 minor;
+	u8 major;
+} __packed;
+
+struct vd55g_fw_revision_map {
+	u64 revision;
+	bool needs_patch;
+	const char *fw_name;
+	const struct firmware *builtin_fw;
+};
+
+struct vd55g_chip_info {
+	unsigned int id;
+	const char *name;
+	const struct vd55g_mode *modes;
+	u16 num_modes;
+	const int *reg_map;
+	const struct vd55g_fw_revision_map *fw_maps;
+	u16 num_fw_maps;
+	bool needs_patch_boot;
+	u32 mipi_rate_min;
+	u32 mipi_rate_max;
+	u16 width;
+	u16 height;
+	bool bayer;
+	bool hdr;
+	bool absolute_endpoints;
+	bool ae_coldstart_exposure_in_us;
+	bool limit_flash_duty_cycle;
+};
+
+static const struct vd55g_fw_revision_map vd55g0_fw_maps[] = {
+	{
+		.revision = 0x1111,
+		.needs_patch = true,
+		.fw_name = "vd55g0-cut1.bin"
+	},
+	{
+		.revision = 0x1120,
+		.needs_patch = true,
+		.fw_name = "vd55g0-cut2.bin"
+	},
+};
+
+static const struct firmware vd55g1_builtin_fw = {
+	.data = vd55g1_patch_array, .size = ARRAY_SIZE(vd55g1_patch_array)
+};
+
+static const struct vd55g_fw_revision_map vd55g1_fw_maps[] = {
+	{
+		.revision = 0x2020,
+		.needs_patch = true,
+		.fw_name = "vd55g1.bin",
+		.builtin_fw = &vd55g1_builtin_fw
+	},
+};
+
+static const struct vd55g_fw_revision_map vd65g4_fw_maps[] = {
+	{ .revision = 0x3030, .needs_patch = false },
+};
+
+static const struct vd55g_chip_info vd55g0_chip_info = {
+	.id = 0x53354730,
+	.name = "vd55g0",
+	.modes = vd55g0_supported_modes,
+	.num_modes = ARRAY_SIZE(vd55g0_supported_modes),
+	.reg_map = vd55g0_reg_map,
+	.fw_maps = vd55g0_fw_maps,
+	.num_fw_maps = ARRAY_SIZE(vd55g0_fw_maps),
+	.needs_patch_boot = true,
+	.mipi_rate_min = VD55G0_MIPI_RATE_MIN,
+	.mipi_rate_max = VD55G0_MIPI_RATE_MAX,
+	.width = VD55G0_WIDTH,
+	.height = VD55G0_HEIGHT,
+	.bayer = false,
+	.hdr = false,
+	.absolute_endpoints = true,
+	.ae_coldstart_exposure_in_us = false,
+	.limit_flash_duty_cycle = true,
+};
+
+static const struct vd55g_chip_info vd55g1_chip_info = {
+	.id = 0x53354731,
+	.name = "vd55g1",
+	.modes = vd55g1_supported_modes,
+	.num_modes = ARRAY_SIZE(vd55g1_supported_modes),
+	.reg_map = vd55g1_reg_map,
+	.fw_maps = vd55g1_fw_maps,
+	.num_fw_maps = ARRAY_SIZE(vd55g1_fw_maps),
+	.needs_patch_boot = false,
+	.mipi_rate_min = VD55G1_MIPI_RATE_MIN,
+	.mipi_rate_max = VD55G1_MIPI_RATE_MAX,
+	.width = VD55G1_WIDTH,
+	.height = VD55G1_HEIGHT,
+	.bayer = false,
+	.hdr = true,
+	.absolute_endpoints = false,
+	.ae_coldstart_exposure_in_us = true,
+	.limit_flash_duty_cycle = false,
+};
+
+static const struct vd55g_chip_info vd65g4_chip_info = {
+	.id = 0x53354733,
+	.name = "vd65g4",
+	.modes = vd55g1_supported_modes,
+	.num_modes = ARRAY_SIZE(vd55g1_supported_modes),
+	.reg_map = vd55g1_reg_map,
+	.fw_maps = vd65g4_fw_maps,
+	.num_fw_maps = ARRAY_SIZE(vd65g4_fw_maps),
+	.width = VD55G1_WIDTH,
+	.height = VD55G1_HEIGHT,
+	.bayer = true,
+	.hdr = true,
+	.absolute_endpoints = false,
+	.ae_coldstart_exposure_in_us = true,
+	.limit_flash_duty_cycle = false,
+};
+
+struct vd55g {
+	struct device *dev;
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+	struct regulator_bulk_data supplies[ARRAY_SIZE(vd55g_supply_name)];
+	struct gpio_desc *reset_gpio;
+	struct clk *xclk;
+	struct regmap *regmap;
+	const struct vd55g_chip_info *info;
+	u32 xclk_freq;
+	u16 oif_ctrl;
+	u8 gpios[VD55G_NB_GPIOS];
+	unsigned long ext_leds_mask;
+	u32 mipi_rate;
+	u32 pixel_clock;
+	u64 link_freq;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl *pixel_rate_ctrl;
+	struct v4l2_ctrl *vblank_ctrl;
+	struct v4l2_ctrl *hblank_ctrl;
+	struct {
+		struct v4l2_ctrl *hflip_ctrl;
+		struct v4l2_ctrl *vflip_ctrl;
+	};
+	struct v4l2_ctrl *patgen_ctrl;
+	struct {
+		struct v4l2_ctrl *ae_ctrl;
+		struct v4l2_ctrl *expo_ctrl;
+		struct v4l2_ctrl *again_ctrl;
+		struct v4l2_ctrl *dgain_ctrl;
+	};
+	struct v4l2_ctrl *ae_lock_ctrl;
+	struct v4l2_ctrl *ae_bias_ctrl;
+	struct v4l2_ctrl *led_ctrl;
+	struct v4l2_ctrl *hdr_ctrl;
+};
+
+static inline struct vd55g *to_vd55g(struct v4l2_subdev *sd)
+{
+	return container_of_const(sd, struct vd55g, sd);
+}
+
+static inline struct vd55g *ctrl_to_vd55g(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd =
+		&container_of_const(ctrl->handler, struct vd55g, ctrl_handler)
+			 ->sd;
+
+	return to_vd55g(sd);
+}
+
+static inline int vd55g_get_ctx_addr(struct vd55g *sensor,
+				     enum vd55g_reg_id reg, u8 ctx)
+{
+	u16 base_addr = sensor->info->reg_map[reg];
+	u16 stride = sensor->info->reg_map[REG_CTX_STRIDE];
+
+	return base_addr + (stride * ctx);
+}
+
+static unsigned int vd55g_get_fmt_bpp(u32 code)
+{
+	switch (code) {
+	case MEDIA_BUS_FMT_Y8_1X8:
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	default:
+		return 8;
+
+	case MEDIA_BUS_FMT_Y10_1X10:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+		return 10;
+	}
+}
+
+static unsigned int vd55g_get_fmt_data_type(u32 code)
+{
+	switch (code) {
+	case MEDIA_BUS_FMT_Y8_1X8:
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	default:
+		return MIPI_CSI2_DT_RAW8;
+
+	case MEDIA_BUS_FMT_Y10_1X10:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+		return MIPI_CSI2_DT_RAW10;
+	}
+}
+
+static u32 vd55g_get_fmt_code(struct vd55g *sensor, u32 code)
+{
+	unsigned int i, j;
+
+	if (!sensor->info->bayer)
+		return code;
+
+	for (i = 0; i < ARRAY_SIZE(vd55g_mbus_formats_bayer); i++) {
+		for (j = 0; j < ARRAY_SIZE(vd55g_mbus_formats_bayer[i]); j++) {
+			if (vd55g_mbus_formats_bayer[i][j] == code)
+				goto adapt_bayer_pattern;
+		}
+	}
+	dev_warn(sensor->dev, "Unsupported mbus format\n");
+
+	return code;
+
+adapt_bayer_pattern:
+	j = 0;
+	/* In first init_state() call, controls might not be initialized yet */
+	if (sensor->hflip_ctrl && sensor->vflip_ctrl) {
+		j = (sensor->hflip_ctrl->val ? 1 : 0) +
+		    (sensor->vflip_ctrl->val ? 2 : 0);
+	}
+
+	return vd55g_mbus_formats_bayer[i][j];
+}
+
+static s32 vd55g_get_pixel_rate(struct vd55g *sensor,
+				struct v4l2_mbus_framefmt *format)
+{
+	return sensor->mipi_rate / vd55g_get_fmt_bpp(format->code);
+}
+
+static unsigned int vd55g_get_hblank_min(struct vd55g *sensor,
+					 struct v4l2_mbus_framefmt *format,
+					 struct v4l2_rect *crop)
+{
+	u32 mipi_req_line_time;
+	u32 mipi_req_line_length;
+	u32 min_line_length;
+
+	/* MIPI required time */
+	mipi_req_line_time = (crop->width * vd55g_get_fmt_bpp(format->code) +
+			      VD55G_MIPI_MARGIN) /
+			     (sensor->mipi_rate / MEGA);
+	mipi_req_line_length =
+		mipi_req_line_time * sensor->pixel_clock / HZ_PER_MHZ;
+
+	/* Absolute time required for ADCs to convert pixels */
+	min_line_length = VD55G_LINE_LENGTH_MIN;
+	if (sensor->info->hdr)
+		if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB)
+			min_line_length = VD55G1_LINE_LENGTH_SUB_MIN;
+
+	/* Respect both constraint */
+	min_line_length = max(min_line_length, mipi_req_line_length);
+
+	return min_line_length - crop->width;
+}
+
+static void vd55g_get_vblank_limits(struct vd55g *sensor,
+				    struct v4l2_rect *crop,
+				    struct vd55g_vblank_limits *limits)
+{
+	limits->min = VD55G_VBLANK_MIN;
+	limits->def = VD55G_FRAME_LENGTH_DEF - crop->height;
+	limits->max = VD55G_VBLANK_MAX - crop->height;
+}
+
+static void vd55g_get_frame_timings(struct vd55g *sensor,
+				    struct v4l2_rect *crop,
+				    struct vd55g_frame_timings *timings)
+{
+	timings->frame_length = crop->height + sensor->vblank_ctrl->val;
+
+	timings->expo_max = timings->frame_length - VD55G_EXPO_MAX_TERM;
+
+	if (sensor->info->limit_flash_duty_cycle &&
+	    sensor->led_ctrl->val == V4L2_FLASH_LED_MODE_FLASH) {
+		timings->expo_max = timings->expo_max / 2;
+	}
+}
+
+#define vd55g_read(sensor, reg, val, err) \
+	cci_read((sensor)->regmap, reg, val, err)
+
+#define vd55g_write(sensor, reg, val, err) \
+	cci_write((sensor)->regmap, reg, val, err)
+
+static int vd55g_cci_read(struct vd55g *sensor, enum vd55g_reg_id reg, u64 *val,
+			  int *err)
+{
+	u32 physical_addr;
+
+	if (err && *err)
+		return *err;
+
+	physical_addr = sensor->info->reg_map[reg];
+
+	if (!physical_addr) {
+		dev_err(sensor->dev,
+			"Register %d not supported on this variant\n", reg);
+		if (err)
+			*err = -EOPNOTSUPP;
+		return -EOPNOTSUPP;
+	}
+
+	return cci_read(sensor->regmap, physical_addr, val, err);
+}
+
+static int vd55g_cci_write(struct vd55g *sensor, enum vd55g_reg_id reg, u32 val,
+			   int *err)
+{
+	u32 physical_addr;
+
+	if (err && *err)
+		return *err;
+
+	physical_addr = sensor->info->reg_map[reg];
+
+	if (!physical_addr) {
+		dev_err(sensor->dev,
+			"Register %d not supported on this variant\n", reg);
+		if (err)
+			*err = -EOPNOTSUPP;
+		return -EOPNOTSUPP;
+	}
+
+	return cci_write(sensor->regmap, physical_addr, val, err);
+}
+
+static int vd55g_write_array(struct vd55g *sensor, u32 reg, unsigned int len,
+			     const u8 *array, int *err)
+{
+	unsigned int chunk_sz = 1024;
+	unsigned int sz;
+	int ret = 0;
+
+	if (err && *err)
+		return *err;
+
+	/*
+	 * This loop isn't necessary but in certains conditions (platforms, cpu
+	 * load, etc.) it has been observed that the bulk write could timeout.
+	 */
+	while (len) {
+		sz = min(len, chunk_sz);
+		ret = regmap_bulk_write(sensor->regmap, reg, array, sz);
+		if (ret < 0)
+			goto out;
+		len -= sz;
+		reg += sz;
+		array += sz;
+	}
+
+out:
+	if (ret && err)
+		*err = ret;
+
+	return ret;
+}
+
+static int vd55g_poll_reg(struct vd55g *sensor, u32 reg, u8 poll_val, int *err)
+{
+	unsigned int val = 0;
+	int ret;
+
+	if (err && *err)
+		return *err;
+
+	ret = regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val,
+				       (val == poll_val), 2000,
+				       500 * USEC_PER_MSEC);
+
+	if (ret && err)
+		*err = ret;
+
+	return ret;
+}
+
+static int vd55g_wait_state(struct vd55g *sensor, int state, int *err)
+{
+	u32 reg = sensor->info->reg_map[REG_SYSTEM_FSM];
+
+	return vd55g_poll_reg(sensor, reg, state, err);
+}
+
+static int vd55g_prepare_clock_tree(struct vd55g *sensor)
+{
+	u32 sys_clk, mipi_div, pixel_div;
+
+	if (sensor->xclk_freq < VD55G_XCLK_FREQ_MIN ||
+	    sensor->xclk_freq > VD55G_XCLK_FREQ_MAX) {
+		dev_err(sensor->dev,
+			"Only %luMhz-%luMhz clock range supported. Provided %lu MHz\n",
+			VD55G_XCLK_FREQ_MIN / HZ_PER_MHZ,
+			VD55G_XCLK_FREQ_MAX / HZ_PER_MHZ,
+			sensor->xclk_freq / HZ_PER_MHZ);
+		return -EINVAL;
+	}
+
+	/* MIPI bus is double data rate */
+	sensor->mipi_rate = sensor->link_freq * 2;
+
+	if (sensor->mipi_rate < sensor->info->mipi_rate_min ||
+	    sensor->mipi_rate > sensor->info->mipi_rate_max) {
+		dev_err(sensor->dev,
+			"Only %luMbps-%luMbps data rate range supported. Provided %lu Mbps\n",
+			sensor->info->mipi_rate_min / MEGA,
+			sensor->info->mipi_rate_max / MEGA,
+			sensor->mipi_rate / MEGA);
+		return -EINVAL;
+	}
+
+	if (sensor->mipi_rate <= 300 * MEGA)
+		mipi_div = 4;
+	else if (sensor->mipi_rate <= 600 * MEGA)
+		mipi_div = 2;
+	else
+		mipi_div = 1;
+
+	sys_clk = sensor->mipi_rate * mipi_div;
+
+	if (sys_clk <= 780 * HZ_PER_MHZ)
+		pixel_div = 5;
+	else if (sys_clk <= 900 * HZ_PER_MHZ)
+		pixel_div = 6;
+	else
+		pixel_div = 8;
+
+	sensor->pixel_clock = sys_clk / pixel_div;
+
+	return 0;
+}
+
+static int vd55g_update_patgen(struct vd55g *sensor, u32 patgen_index)
+{
+	static const u8 index2val[] = { 0x0, 0x22, 0x28 };
+	u32 pattern = index2val[patgen_index];
+	u32 reg = pattern << VD55G_PATGEN_TYPE_SHIFT;
+	u8 duster = VD55G_DUSTER_RING_ENABLE | VD55G_DUSTER_DYN_ENABLE |
+		    VD55G_DUSTER_ENABLE;
+	int ret = 0;
+
+	BUILD_BUG_ON(ARRAY_SIZE(index2val) != ARRAY_SIZE(vd55g_tp_menu));
+
+	if (pattern != 0) {
+		reg |= VD55G_PATGEN_ENABLE;
+		/* Take care of duster to not mess up the test pattern output */
+		duster = VD55G_DUSTER_DISABLE;
+	}
+
+	vd55g_cci_write(sensor, REG_DUSTER_CTRL, duster, &ret);
+	vd55g_cci_write(sensor, REG_PATGEN_CTRL, reg, &ret);
+
+	return ret;
+}
+
+static int vd55g_update_expo_cluster(struct vd55g *sensor, bool is_auto)
+{
+	enum vd55g_expo_state expo_state = is_auto ? VD55G1_EXP_AUTO :
+						     VD55G1_EXP_MANUAL;
+	int ret = 0;
+
+	if (sensor->ae_ctrl->is_new) {
+		vd55g_write(sensor, VD55G_REG_EXP_MODE(sensor, 0), expo_state,
+			    &ret);
+		vd55g_cci_write(sensor, REG_EXPOSURE_MAX_COARSE,
+				sensor->expo_ctrl->maximum, &ret);
+	}
+
+	if (sensor->info->hdr) {
+		if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB &&
+		    sensor->hdr_ctrl->is_new) {
+			vd55g_write(sensor, VD55G_REG_EXP_MODE(sensor, 1),
+				    VD55G1_EXP_BYPASS, &ret);
+			if (ret)
+				return ret;
+		}
+	}
+
+	if (!is_auto && sensor->expo_ctrl->is_new)
+		vd55g_cci_write(sensor, REG_MANUAL_COARSE_EXPOSURE,
+				sensor->expo_ctrl->val, &ret);
+
+	if (!is_auto && sensor->again_ctrl->is_new)
+		vd55g_cci_write(sensor, REG_MANUAL_ANALOG_GAIN,
+				sensor->again_ctrl->val, &ret);
+
+	if (!is_auto && sensor->dgain_ctrl->is_new)
+		vd55g_cci_write(sensor, REG_MANUAL_DIGITAL_GAIN,
+				sensor->dgain_ctrl->val, &ret);
+
+	return ret;
+}
+
+static int vd55g_lock_exposure(struct vd55g *sensor, u32 lock_val)
+{
+	bool ae_lock = lock_val & V4L2_LOCK_EXPOSURE;
+	enum vd55g_expo_state expo_state = ae_lock ? VD55G1_EXP_FREEZE :
+						     VD55G1_EXP_AUTO;
+	int ret = 0;
+
+	if (sensor->ae_ctrl->val == V4L2_EXPOSURE_AUTO)
+		vd55g_write(sensor, VD55G_REG_EXP_MODE(sensor, 0), expo_state,
+			    &ret);
+
+	return ret;
+}
+
+static int vd55g_read_expo_cluster(struct vd55g *sensor)
+{
+	u64 exposure = 0;
+	u64 again = 0;
+	u64 dgain = 0;
+	int ret = 0;
+
+	vd55g_cci_read(sensor, REG_APPLIED_COARSE_EXPOSURE, &exposure, &ret);
+	vd55g_cci_read(sensor, REG_APPLIED_ANALOG_GAIN, &again, &ret);
+	vd55g_cci_read(sensor, REG_APPLIED_DIGITAL_GAIN, &dgain, &ret);
+	if (ret)
+		return ret;
+
+	sensor->expo_ctrl->cur.val = exposure;
+	sensor->again_ctrl->cur.val = again;
+	sensor->dgain_ctrl->cur.val = dgain;
+
+	return 0;
+}
+
+static int vd55g_update_frame_length(struct vd55g *sensor,
+				     unsigned int frame_length)
+{
+	int ret = 0;
+
+	if (sensor->info->hdr)
+		if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB)
+			vd55g_write(sensor, VD55G_REG_FRAME_LENGTH(sensor, 1),
+				    frame_length, &ret);
+	vd55g_write(sensor, VD55G_REG_FRAME_LENGTH(sensor, 0), frame_length,
+		    &ret);
+
+	return ret;
+}
+
+static int vd55g_update_exposure_target(struct vd55g *sensor, int index)
+{
+	/*
+	 * Find auto exposure target with: default target exposure * 2^EV
+	 * Defaut target exposure being 27 for the sensor.
+	 */
+	static const unsigned int index2exposure_target[] = {
+		3, 5, 7, 10, 14, 19, 27, 38, 54, 76, 108, 153, 216,
+	};
+	int exposure_target = index2exposure_target[index];
+
+	return vd55g_cci_write(sensor, REG_AE_TARGET_PERCENTAGE,
+			       exposure_target, NULL);
+}
+
+static int vd55g_apply_cold_start(struct vd55g *sensor, struct v4l2_rect *crop)
+{
+	int ret;
+
+	if (sensor->info->ae_coldstart_exposure_in_us) {
+		/*
+		 * Cold start register is a single register expressed as exposure time
+		 * in us. This differ from status registers being a combination of
+		 * exposure, digital gain, and analog gain, requiring the following
+		 * format conversion.
+		 */
+		unsigned int line_length =
+			crop->width + sensor->hblank_ctrl->val;
+		unsigned int line_time_us =
+			DIV_ROUND_UP(line_length * MEGA, sensor->pixel_clock);
+		u8 d_gain = DIV_ROUND_CLOSEST(sensor->dgain_ctrl->val, 1 << 8);
+		u8 a_gain =
+			DIV_ROUND_CLOSEST(32, (32 - sensor->again_ctrl->val));
+		unsigned int expo_us =
+			sensor->expo_ctrl->val * d_gain * a_gain * line_time_us;
+		vd55g_cci_write(sensor, REG_AE_COLDSTART_EXPOSURE_US, expo_us,
+				&ret);
+	} else {
+		vd55g_cci_write(sensor, REG_AE_COLDSTART_COARSE_EXPOSURE,
+				sensor->expo_ctrl->val, &ret);
+	}
+
+	vd55g_cci_write(sensor, REG_AE_FORCE_COLDSTART, 1, &ret);
+
+	return ret;
+}
+
+static void vd55g_update_pad_fmt(struct vd55g *sensor,
+				 const struct vd55g_mode *mode, u32 code,
+				 struct v4l2_mbus_framefmt *fmt)
+{
+	fmt->code = vd55g_get_fmt_code(sensor, code);
+	fmt->width = mode->width;
+	fmt->height = mode->height;
+	fmt->colorspace = V4L2_COLORSPACE_RAW;
+	fmt->field = V4L2_FIELD_NONE;
+	fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+	fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int vd55g_update_hdr_mode(struct vd55g *sensor)
+{
+	int ret = 0;
+
+	if (!sensor->info->hdr)
+		return -EINVAL;
+
+	switch (sensor->hdr_ctrl->val) {
+	case VD55G1_NO_HDR:
+		vd55g_cci_write(sensor, REG_EXPOSURE_MAX_COARSE,
+				sensor->expo_ctrl->maximum, &ret);
+		vd55g_cci_write(sensor, REG_EXPOSURE_USE_CASES, 0, &ret);
+		vd55g_cci_write(sensor, REG_NEXT_CTX, 0x0, &ret);
+
+		vd55g_cci_write(sensor, REG_CTX_REPEAT_COUNT_CTX0, 0, &ret);
+
+		vd55g_write(sensor, VD55G_REG_VT_MODE(sensor, 0),
+			    VD55G1_VT_MODE_NORMAL, &ret);
+		vd55g_write(sensor, VD55G_REG_MASK_FRAME_CTRL(sensor, 0),
+			    VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret);
+		break;
+	case VD55G1_HDR_SUB:
+		vd55g_cci_write(sensor, REG_EXPOSURE_MAX_COARSE,
+				VD55G1_EXPOSURE_MAX_COARSE_SUB, &ret);
+		vd55g_cci_write(sensor, REG_EXPOSURE_USE_CASES,
+				VD55G1_EXPOSURE_USE_CASES_MULTI_CONTEXT, &ret);
+		vd55g_cci_write(sensor, REG_NEXT_CTX, 0x0001, &ret);
+
+		vd55g_cci_write(sensor, REG_CTX_REPEAT_COUNT_CTX0, 1, &ret);
+		vd55g_cci_write(sensor, REG_CTX_REPEAT_COUNT_CTX1, 1, &ret);
+
+		vd55g_write(sensor, VD55G_REG_VT_MODE(sensor, 0),
+			    VD55G1_VT_MODE_NORMAL, &ret);
+		vd55g_write(sensor, VD55G_REG_MASK_FRAME_CTRL(sensor, 0),
+			    VD55G1_MASK_FRAME_CTRL_MASK, &ret);
+		vd55g_write(sensor, VD55G_REG_EXPOSURE_INSTANCE(sensor, 0), 0,
+			    &ret);
+		vd55g_write(sensor, VD55G_REG_VT_MODE(sensor, 1),
+			    VD55G1_VT_MODE_SUBTRACTION, &ret);
+		vd55g_write(sensor, VD55G_REG_MASK_FRAME_CTRL(sensor, 1),
+			    VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret);
+		vd55g_write(sensor, VD55G_REG_EXPOSURE_INSTANCE(sensor, 1), 1,
+			    &ret);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int vd55g_set_framefmt(struct vd55g *sensor,
+			      struct v4l2_mbus_framefmt *format,
+			      struct v4l2_rect *crop)
+{
+	u8 binning;
+	int ret = 0;
+	int x_val, y_val;
+
+	vd55g_write(sensor, VD55G_REG_FORMAT_CTRL,
+		    vd55g_get_fmt_bpp(format->code), &ret);
+	vd55g_write(sensor, VD55G_REG_OIF_IMG_CTRL,
+		    vd55g_get_fmt_data_type(format->code), &ret);
+
+	switch (crop->width / format->width) {
+	case 1:
+	default:
+		binning = VD55G_READOUT_CTRL_BIN_MODE_NORMAL;
+		break;
+	case 2:
+		binning = VD55G_READOUT_CTRL_BIN_MODE_DIGITAL_X2;
+		break;
+	}
+	vd55g_cci_write(sensor, REG_READOUT_CTRL, binning, &ret);
+
+	if (sensor->info->absolute_endpoints) {
+		x_val = crop->left + crop->width - 1;
+		y_val = crop->top + crop->height - 1;
+	} else {
+		x_val = crop->width;
+		y_val = crop->height;
+	}
+
+	vd55g_write(sensor, VD55G_REG_X_START(sensor, 0), crop->left, &ret);
+	vd55g_write(sensor, VD55G_REG_X_WIDTH_OR_END(sensor, 0), x_val, &ret);
+	vd55g_write(sensor, VD55G_REG_Y_START(sensor, 0), crop->top, &ret);
+	vd55g_write(sensor, VD55G_REG_Y_HEIGHT_OR_END(sensor, 0), y_val, &ret);
+
+	if (sensor->info->hdr) {
+		vd55g_write(sensor, VD55G_REG_X_START(sensor, 1), crop->left,
+			    &ret);
+		vd55g_write(sensor, VD55G_REG_X_WIDTH_OR_END(sensor, 1), x_val,
+			    &ret);
+		vd55g_write(sensor, VD55G_REG_Y_START(sensor, 1), crop->top,
+			    &ret);
+		vd55g_write(sensor, VD55G_REG_Y_HEIGHT_OR_END(sensor, 1), y_val,
+			    &ret);
+	}
+
+	return ret;
+}
+
+static int vd55g_update_gpios(struct vd55g *sensor, unsigned long gpio_mask)
+{
+	unsigned long io;
+	u8 gpio_val;
+	int ret = 0;
+
+	for_each_set_bit(io, &gpio_mask, VD55G_NB_GPIOS) {
+		gpio_val = sensor->gpios[io];
+
+		if (gpio_val == VD55G_GPIO_MODE_STROBE &&
+		    sensor->led_ctrl->val == V4L2_FLASH_LED_MODE_NONE) {
+			gpio_val = VD55G_GPIO_MODE_IN;
+			if (sensor->info->hdr) {
+				if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) {
+					/* Make its context 1 counterpart strobe too */
+					vd55g_write(sensor,
+						    VD55G_REG_GPIO_CTRL(sensor,
+									1, io),
+						    gpio_val, &ret);
+				}
+			}
+		}
+
+		ret = vd55g_write(sensor, VD55G_REG_GPIO_CTRL(sensor, 0, io),
+				  gpio_val, &ret);
+	}
+
+	return ret;
+}
+
+static int vd55g_ro_ctrls_setup(struct vd55g *sensor, struct v4l2_rect *crop)
+{
+	return vd55g_write(sensor, VD55G_REG_LINE_LENGTH,
+			   crop->width + sensor->hblank_ctrl->val, NULL);
+}
+
+static void vd55g_grab_ctrls(struct vd55g *sensor, bool enable)
+{
+	/* These settings cannot change during stream */
+	v4l2_ctrl_grab(sensor->hflip_ctrl, enable);
+	v4l2_ctrl_grab(sensor->vflip_ctrl, enable);
+	v4l2_ctrl_grab(sensor->patgen_ctrl, enable);
+	v4l2_ctrl_grab(sensor->hdr_ctrl, enable);
+}
+
+static int vd55g_enable_streams(struct v4l2_subdev *sd,
+				struct v4l2_subdev_state *state, u32 pad,
+				u64 streams_mask)
+{
+	struct vd55g *sensor = to_vd55g(sd);
+	struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
+	struct v4l2_mbus_framefmt *format =
+		v4l2_subdev_state_get_format(state, 0);
+	int ret;
+
+	ret = pm_runtime_resume_and_get(sensor->dev);
+	if (ret < 0)
+		return ret;
+
+	/* Configure output */
+	vd55g_write(sensor, VD55G_REG_MIPI_DATA_RATE, sensor->mipi_rate, &ret);
+	vd55g_write(sensor, VD55G_REG_OIF_CTRL, sensor->oif_ctrl, &ret);
+	vd55g_cci_write(sensor, REG_ISL_ENABLE, 0, &ret);
+	if (ret)
+		goto err_rpm_put;
+
+	ret = vd55g_set_framefmt(sensor, format, crop);
+	if (ret)
+		goto err_rpm_put;
+
+	/* Setup default GPIO values; could be overridden by V4L2 ctrl setup */
+	ret = vd55g_update_gpios(sensor, GENMASK(VD55G_NB_GPIOS - 1, 0));
+	if (ret)
+		goto err_rpm_put;
+
+	ret = vd55g_apply_cold_start(sensor, crop);
+	if (ret)
+		goto err_rpm_put;
+
+	/* Apply settings from V4L2 ctrls */
+	ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
+	if (ret)
+		goto err_rpm_put;
+
+	/* Also apply settings from read-only V4L2 ctrls */
+	ret = vd55g_ro_ctrls_setup(sensor, crop);
+	if (ret)
+		goto err_rpm_put;
+
+	/* Start streaming */
+	vd55g_write(sensor, VD55G_REG_STBY, VD55G_STBY_START_STREAM, &ret);
+	vd55g_poll_reg(sensor, VD55G_REG_STBY, 0, &ret);
+	vd55g_wait_state(sensor, VD55G_SYSTEM_FSM_STREAMING, &ret);
+	if (ret)
+		goto err_rpm_put;
+
+	vd55g_grab_ctrls(sensor, true);
+
+	return 0;
+
+err_rpm_put:
+	pm_runtime_put(sensor->dev);
+	return -EINVAL;
+}
+
+static int vd55g_disable_streams(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *state, u32 pad,
+				 u64 streams_mask)
+{
+	struct vd55g *sensor = to_vd55g(sd);
+	int ret = 0;
+
+	/* Retrieve Expo cluster to enable coldstart of AE */
+	ret = vd55g_read_expo_cluster(sensor);
+
+	vd55g_write(sensor, VD55G_REG_STREAMING, VD55G_STREAMING_STOP_STREAM,
+		    &ret);
+	vd55g_poll_reg(sensor, VD55G_REG_STREAMING, 0, &ret);
+	vd55g_wait_state(sensor, VD55G_SYSTEM_FSM_SW_STBY, &ret);
+
+	if (ret)
+		dev_warn(sensor->dev, "Can't disable stream\n");
+
+	vd55g_grab_ctrls(sensor, false);
+
+	pm_runtime_put_autosuspend(sensor->dev);
+
+	return ret;
+}
+
+static int vd55g_load_and_apply_patch(struct vd55g *sensor,
+				      const struct vd55g_fw_revision_map *fw_info)
+{
+	const struct firmware *fw;
+	const struct vd55g_patch_header *hdr;
+	u16 payload_len;
+	u64 hardware_version;
+	int ret;
+
+	ret = request_firmware(&fw, fw_info->fw_name, sensor->dev);
+
+	if (ret == -ENOENT && fw_info->builtin_fw) {
+		dev_info(sensor->dev,
+			 "External firmware %s not found, using built-in defaults\n",
+			 fw_info->fw_name);
+		fw = fw_info->builtin_fw;
+	} else if (ret) {
+		dev_err(sensor->dev,
+			"Failed to load required firmware %s: %d\n",
+			fw_info->fw_name,
+			ret);
+		return ret;
+	}
+
+	if (fw->size < sizeof(*hdr)) {
+		dev_err(sensor->dev,
+			"Patch file is too small to contain a header\n");
+		return -EINVAL;
+	}
+
+	hdr = (const struct vd55g_patch_header *)fw->data;
+	payload_len = le16_to_cpu(hdr->patch_size);
+
+	if (payload_len > fw->size) {
+		dev_err(sensor->dev,
+			"Patch payload length mismatch in header\n");
+		return -EINVAL;
+	}
+
+	vd55g_write_array(sensor, VD55G_REG_FWPATCH_START_ADDR, fw->size,
+			  fw->data, &ret);
+	vd55g_write(sensor, VD55G_REG_BOOT, VD55G_BOOT_PATCH, &ret);
+	vd55g_poll_reg(sensor, VD55G_REG_BOOT, 0, &ret);
+	if (ret) {
+		dev_err(sensor->dev, "Failed to apply patch\n");
+		return ret;
+	}
+
+	ret = vd55g_cci_read(sensor, REG_FWPATCH_REVISION, &hardware_version,
+			     &ret);
+	if (ret)
+		return ret;
+
+	if ((hardware_version & 0xFFFF) != ((hdr->major << 8) | hdr->minor)) {
+		dev_err(sensor->dev,
+			"Bad patch version expected %d.%d got %d.%d\n",
+			hdr->major, hdr->minor, (u8)(hardware_version >> 8),
+			(u8)(hardware_version & 0xff));
+		return -ENODEV;
+	}
+
+	dev_dbg(sensor->dev, "patch %d.%d applied\n",
+		(u8)(hardware_version >> 8), (u8)(hardware_version & 0xff));
+	return 0;
+}
+
+static int vd55g_get_firmware_name(struct vd55g *sensor, u64 revision,
+				   const struct vd55g_fw_revision_map **fw_info)
+{
+	for (int i = 0; i < sensor->info->num_fw_maps; i++) {
+		const struct vd55g_fw_revision_map *map = &sensor->info->fw_maps[i];
+
+		if (map->revision == revision) {
+			*fw_info = map;
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+static int vd55g_boot(struct vd55g *sensor)
+{
+	int ret = 0;
+	u64 rev;
+	const struct vd55g_fw_revision_map *fw_info;
+
+	ret = vd55g_read(sensor, VD55G_REG_REVISION, &rev, NULL);
+	if (ret)
+		return ret;
+
+	ret = vd55g_get_firmware_name(sensor, rev, &fw_info);
+	if (ret == -ENOENT) {
+		dev_err(sensor->dev,
+			"Unsupported sensor revision 0x%x for sensor %s\n",
+			(u16)rev, sensor->info->name);
+		return -ENODEV;
+	}
+
+	if (fw_info->needs_patch)
+		vd55g_load_and_apply_patch(sensor, fw_info);
+
+	if (!fw_info->needs_patch || sensor->info->needs_patch_boot) {
+		vd55g_write(sensor, VD55G_REG_BOOT, VD55G_BOOT_BOOT, &ret);
+		vd55g_poll_reg(sensor, VD55G_REG_BOOT, 0, &ret);
+		if (ret) {
+			dev_err(sensor->dev, "Failed to boot\n");
+			return ret;
+		}
+	}
+
+	ret = vd55g_wait_state(sensor, VD55G_SYSTEM_FSM_SW_STBY, NULL);
+	if (ret) {
+		dev_err(sensor->dev, "Sensor waiting after boot failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int vd55g_get_selection(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_state *sd_state,
+			       struct v4l2_subdev_selection *sel)
+{
+	struct vd55g *sensor = to_vd55g(sd);
+	const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(sd_state, 0);
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP:
+		sel->r = *crop;
+		return 0;
+	case V4L2_SEL_TGT_NATIVE_SIZE:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.top = 0;
+		sel->r.left = 0;
+		sel->r.width = sensor->info->width;
+		sel->r.height = sensor->info->height;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int vd55g_enum_mbus_code(struct v4l2_subdev *sd,
+				struct v4l2_subdev_state *sd_state,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct vd55g *sensor = to_vd55g(sd);
+	u32 base_code;
+
+	if (!sensor->info->bayer) {
+		if (code->index >= ARRAY_SIZE(vd55g_mbus_formats_mono))
+			return -EINVAL;
+		base_code = vd55g_mbus_formats_mono[code->index];
+	} else {
+		if (code->index >= ARRAY_SIZE(vd55g_mbus_formats_bayer))
+			return -EINVAL;
+		base_code = vd55g_mbus_formats_bayer[code->index][0];
+	}
+	code->code = vd55g_get_fmt_code(sensor, base_code);
+
+	return 0;
+}
+
+static int vd55g_new_format_change_controls(struct vd55g *sensor,
+					    struct v4l2_mbus_framefmt *format,
+					    struct v4l2_rect *crop)
+{
+	struct vd55g_vblank_limits vblank;
+	struct vd55g_frame_timings timings;
+	unsigned int hblank;
+	int ret;
+
+	/* Reset vblank and frame length to default */
+	vd55g_get_vblank_limits(sensor, crop, &vblank);
+	ret = __v4l2_ctrl_modify_range(sensor->vblank_ctrl, vblank.min,
+				       vblank.max, 1, vblank.def);
+	if (ret)
+		return ret;
+
+	/* Max exposure changes with vblank */
+	vd55g_get_frame_timings(sensor, crop, &timings);
+	ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, timings.expo_max,
+				       1, VD55G_EXPO_DEF);
+	if (ret)
+		return ret;
+
+	/* Update pixel rate to reflect new bpp */
+	ret = __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_ctrl,
+				       vd55g_get_pixel_rate(sensor, format));
+	if (ret)
+		return ret;
+
+	/* Update hblank according to new width */
+	hblank = vd55g_get_hblank_min(sensor, format, crop);
+	ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1,
+				       hblank);
+
+	return ret;
+}
+
+static int vd55g_set_pad_fmt(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_state *sd_state,
+			     struct v4l2_subdev_format *sd_fmt)
+{
+	struct vd55g *sensor = to_vd55g(sd);
+	const struct vd55g_mode *new_mode;
+	struct v4l2_mbus_framefmt *format;
+	struct v4l2_rect pad_crop;
+	unsigned int binning;
+
+	new_mode = v4l2_find_nearest_size(sensor->info->modes,
+					  sensor->info->num_modes, width,
+					  height, sd_fmt->format.width,
+					  sd_fmt->format.height);
+
+	vd55g_update_pad_fmt(sensor, new_mode, sd_fmt->format.code,
+			     &sd_fmt->format);
+
+	/*
+	 * Use binning to maximize the crop rectangle size, and centre it in the
+	 * sensor.
+	 */
+	binning = min(sensor->info->width / sd_fmt->format.width,
+		      sensor->info->height / sd_fmt->format.height);
+	binning = min(binning, 2U);
+	pad_crop.width = sd_fmt->format.width * binning;
+	pad_crop.height = sd_fmt->format.height * binning;
+	pad_crop.left = (sensor->info->width - pad_crop.width) / 2;
+	pad_crop.top = (sensor->info->height - pad_crop.height) / 2;
+
+	format = v4l2_subdev_state_get_format(sd_state, sd_fmt->pad);
+
+	*format = sd_fmt->format;
+
+	*v4l2_subdev_state_get_crop(sd_state, sd_fmt->pad) = pad_crop;
+	if (sd_fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+		return vd55g_new_format_change_controls(sensor, &sd_fmt->format,
+							&pad_crop);
+
+	return 0;
+}
+
+static int vd55g_init_state(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_state *sd_state)
+{
+	struct vd55g *sensor = to_vd55g(sd);
+	struct v4l2_subdev_format fmt = { 0 };
+	struct v4l2_subdev_route routes[] = {
+		{ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE }
+	};
+	struct v4l2_subdev_krouting routing = {
+		.num_routes = ARRAY_SIZE(routes),
+		.routes = routes,
+	};
+	int ret;
+
+	/* Needed by v4l2_subdev_s_stream_helper(), even with 1 stream only */
+	ret = v4l2_subdev_set_routing(sd, sd_state, &routing);
+	if (ret)
+		return ret;
+
+	vd55g_update_pad_fmt(sensor, &sensor->info->modes[VD55G_MODE_DEF],
+			     vd55g_get_fmt_code(sensor, VD55G_MBUS_CODE_DEF),
+			     &fmt.format);
+
+	return vd55g_set_pad_fmt(sd, sd_state, &fmt);
+}
+
+static int vd55g_enum_frame_size(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *sd_state,
+				 struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct vd55g *sensor = to_vd55g(sd);
+	u32 code;
+
+	if (fse->index >= sensor->info->num_modes)
+		return -EINVAL;
+
+	code = vd55g_get_fmt_code(sensor, fse->code);
+	if (fse->code != code)
+		return -EINVAL;
+
+	fse->min_width = sensor->info->modes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = sensor->info->modes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops vd55g_internal_ops = {
+	.init_state = vd55g_init_state,
+};
+
+static const struct v4l2_subdev_pad_ops vd55g_pad_ops = {
+	.enum_mbus_code = vd55g_enum_mbus_code,
+	.get_fmt = v4l2_subdev_get_fmt,
+	.set_fmt = vd55g_set_pad_fmt,
+	.get_selection = vd55g_get_selection,
+	.enum_frame_size = vd55g_enum_frame_size,
+	.enable_streams = vd55g_enable_streams,
+	.disable_streams = vd55g_disable_streams,
+};
+
+static const struct v4l2_subdev_video_ops vd55g_video_ops = {
+	.s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_ops vd55g_subdev_ops = {
+	.video = &vd55g_video_ops,
+	.pad = &vd55g_pad_ops,
+};
+
+static int vd55g_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct vd55g *sensor = ctrl_to_vd55g(ctrl);
+	int ret = 0;
+
+	/* Interact with HW only when it is powered ON */
+	if (!pm_runtime_get_if_in_use(sensor->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE_AUTO:
+		ret = vd55g_read_expo_cluster(sensor);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	pm_runtime_put_autosuspend(sensor->dev);
+
+	return ret;
+}
+
+static int vd55g_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct vd55g *sensor = ctrl_to_vd55g(ctrl);
+	struct v4l2_subdev_state *state =
+		v4l2_subdev_get_locked_active_state(&sensor->sd);
+	struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
+	struct v4l2_mbus_framefmt *format =
+		v4l2_subdev_state_get_format(state, 0);
+	unsigned int hblank = vd55g_get_hblank_min(sensor, format, crop);
+	struct vd55g_frame_timings timings;
+	bool is_auto = false;
+	int ret = 0;
+
+	if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+		return 0;
+
+	/* Update controls state, range, etc. whatever the state of the HW */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+	case V4L2_CID_FLASH_LED_MODE:
+		vd55g_get_frame_timings(sensor, crop, &timings);
+		ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0,
+					       timings.expo_max, 1,
+					       VD55G_EXPO_DEF);
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		is_auto = (ctrl->val == V4L2_EXPOSURE_AUTO);
+		__v4l2_ctrl_grab(sensor->ae_lock_ctrl, !is_auto);
+		__v4l2_ctrl_grab(sensor->ae_bias_ctrl, !is_auto);
+		break;
+	case V4L2_CID_HDR_SENSOR_MODE:
+		/* Discriminate if the userspace changed the control value */
+		if (ctrl->val != ctrl->cur.val) {
+			/* Max horizontal blanking changes with hdr mode */
+			ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl,
+						       hblank, hblank, 1,
+						       hblank);
+		}
+		break;
+	default:
+		break;
+	}
+
+	/* Don't modify hardware if controls modification failed */
+	if (ret)
+		return ret;
+
+	/* Interact with HW only when it is powered ON */
+	if (!pm_runtime_get_if_in_use(sensor->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_HFLIP:
+		ret = vd55g_write(sensor, VD55G_REG_ORIENTATION,
+				  sensor->hflip_ctrl->val |
+					  (sensor->vflip_ctrl->val << 1),
+				  NULL);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = vd55g_update_patgen(sensor, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE_AUTO:
+		ret = vd55g_update_expo_cluster(sensor, is_auto);
+		break;
+	case V4L2_CID_3A_LOCK:
+		ret = vd55g_lock_exposure(sensor, ctrl->val);
+		break;
+	case V4L2_CID_AUTO_EXPOSURE_BIAS:
+		/*
+		 * We use auto exposure target percentage register to control
+		 * exposure bias for more precision.
+		 */
+		ret = vd55g_update_exposure_target(sensor, ctrl->val);
+		break;
+	case V4L2_CID_VBLANK:
+		ret = vd55g_update_frame_length(sensor, timings.frame_length);
+		break;
+	case V4L2_CID_FLASH_LED_MODE:
+		ret = vd55g_update_gpios(sensor, sensor->ext_leds_mask);
+		break;
+	case V4L2_CID_HDR_SENSOR_MODE:
+		ret = vd55g_update_hdr_mode(sensor);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	pm_runtime_put_autosuspend(sensor->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops vd55g_ctrl_ops = {
+	.g_volatile_ctrl = vd55g_g_volatile_ctrl,
+	.s_ctrl = vd55g_s_ctrl,
+};
+
+static int vd55g_init_ctrls(struct vd55g *sensor)
+{
+	const struct v4l2_ctrl_ops *ops = &vd55g_ctrl_ops;
+	struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler;
+	struct v4l2_ctrl *ctrl;
+	struct v4l2_fwnode_device_properties fwnode_props;
+	struct vd55g_vblank_limits vblank;
+	unsigned int hblank;
+	struct v4l2_subdev_state *state =
+		v4l2_subdev_lock_and_get_active_state(&sensor->sd);
+	struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
+	struct v4l2_mbus_framefmt *format =
+		v4l2_subdev_state_get_format(state, 0);
+	s32 pixel_rate = vd55g_get_pixel_rate(sensor, format);
+	int ret;
+
+	v4l2_ctrl_handler_init(hdl, 16);
+
+	/* Flip cluster */
+	sensor->hflip_ctrl =
+		v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+	if (sensor->hflip_ctrl)
+		sensor->hflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+	sensor->vflip_ctrl =
+		v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+	if (sensor->vflip_ctrl)
+		sensor->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+	v4l2_ctrl_cluster(2, &sensor->hflip_ctrl);
+
+	/* Exposition cluster */
+	sensor->ae_ctrl = v4l2_ctrl_new_std_menu(hdl, ops,
+						 V4L2_CID_EXPOSURE_AUTO, 1,
+						 ~0x3, V4L2_EXPOSURE_AUTO);
+	sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
+					       0, 0x1c, 1, VD55G_AGAIN_DEF);
+	sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
+					       256, 0xffff, 1, VD55G_DGAIN_DEF);
+	sensor->expo_ctrl =
+		v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 0,
+				  VD55G_FRAME_LENGTH_DEF - VD55G_EXPO_MAX_TERM,
+				  1, VD55G_EXPO_DEF);
+	v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl, V4L2_EXPOSURE_MANUAL, true);
+
+	sensor->patgen_ctrl =
+		v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
+					     ARRAY_SIZE(vd55g_tp_menu) - 1, 0,
+					     0, vd55g_tp_menu);
+	ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, 0, 0,
+				      &sensor->link_freq);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	sensor->pixel_rate_ctrl = v4l2_ctrl_new_std(hdl, ops,
+						    V4L2_CID_PIXEL_RATE, 1,
+						    INT_MAX, 1, pixel_rate);
+	if (sensor->pixel_rate_ctrl)
+		sensor->pixel_rate_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	sensor->ae_lock_ctrl =
+		v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, 0, 1, 0, 0);
+	sensor->ae_bias_ctrl =
+		v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_AUTO_EXPOSURE_BIAS,
+				       ARRAY_SIZE(vd55g_ev_bias_menu) - 1,
+				       ARRAY_SIZE(vd55g_ev_bias_menu) / 2,
+				       vd55g_ev_bias_menu);
+	if (sensor->info->hdr) {
+		sensor->hdr_ctrl =
+			v4l2_ctrl_new_std_menu_items(hdl, ops,
+						     V4L2_CID_HDR_SENSOR_MODE,
+						     ARRAY_SIZE(vd55g1_hdr_menu) - 1,
+						     0, VD55G1_NO_HDR,
+						     vd55g1_hdr_menu);
+	}
+	hblank = vd55g_get_hblank_min(sensor, format, crop);
+	sensor->hblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
+						hblank, hblank, 1, hblank);
+	if (sensor->hblank_ctrl)
+		sensor->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	vd55g_get_vblank_limits(sensor, crop, &vblank);
+	sensor->vblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+						vblank.min, vblank.max, 1,
+						vblank.def);
+
+	/* Additional controls based on device tree properties */
+	if (sensor->ext_leds_mask) {
+		sensor->led_ctrl =
+			v4l2_ctrl_new_std_menu(hdl, ops,
+					       V4L2_CID_FLASH_LED_MODE,
+					       V4L2_FLASH_LED_MODE_FLASH, 0,
+					       V4L2_FLASH_LED_MODE_NONE);
+	}
+
+	ret = v4l2_fwnode_device_parse(sensor->dev, &fwnode_props);
+	if (ret)
+		goto free_ctrls;
+
+	ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props);
+	if (ret)
+		goto free_ctrls;
+
+	sensor->sd.ctrl_handler = hdl;
+	goto unlock_state;
+
+free_ctrls:
+	v4l2_ctrl_handler_free(hdl);
+unlock_state:
+	v4l2_subdev_unlock_state(state);
+	return ret;
+}
+
+static int vd55g_detect(struct vd55g *sensor)
+{
+	const struct vd55g_chip_info *info;
+	u64 id;
+	int ret;
+
+	info = device_get_match_data(sensor->dev);
+
+	sensor->info = info;
+
+	ret = vd55g_wait_state(sensor, VD55G_SYSTEM_FSM_READY_TO_BOOT, NULL);
+	if (ret)
+		return ret;
+
+	ret = vd55g_read(sensor, VD55G_REG_MODEL_ID, &id, NULL);
+	if (ret)
+		return ret;
+
+	if (id != sensor->info->id) {
+		dev_err(sensor->dev,
+			"Expected %s (0x%x), but detected mismatched sensor id 0x%x\n",
+			sensor->info->name, (u32)sensor->info->id, (u32)id);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int vd55g_power_on(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct vd55g *sensor = to_vd55g(sd);
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(vd55g_supply_name),
+				    sensor->supplies);
+	if (ret) {
+		dev_err(dev, "Failed to enable regulators %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(sensor->xclk);
+	if (ret) {
+		dev_err(dev, "Failed to enable clock %d\n", ret);
+		goto disable_bulk;
+	}
+
+	gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+	usleep_range(5000, 10000);
+	ret = vd55g_detect(sensor);
+	if (ret) {
+		dev_err(dev, "Sensor detect failed %d\n", ret);
+		goto disable_clock;
+	}
+
+	ret = vd55g_prepare_clock_tree(sensor);
+	if (ret)
+		goto disable_clock;
+
+	/* Setup clock now to advance through system FSM states */
+	vd55g_write(sensor, VD55G_REG_EXT_CLOCK, sensor->xclk_freq, &ret);
+
+	ret = vd55g_boot(sensor);
+	if (ret) {
+		dev_err(dev, "Sensor boot failed %d\n", ret);
+		goto disable_clock;
+	}
+
+	return 0;
+
+disable_clock:
+	gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+	clk_disable_unprepare(sensor->xclk);
+disable_bulk:
+	regulator_bulk_disable(ARRAY_SIZE(vd55g_supply_name), sensor->supplies);
+
+	return ret;
+}
+
+static int vd55g_power_off(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct vd55g *sensor = to_vd55g(sd);
+
+	gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+	clk_disable_unprepare(sensor->xclk);
+	regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+
+	return 0;
+}
+
+static int vd55g_check_csi_conf(struct vd55g *sensor,
+				struct fwnode_handle *endpoint)
+{
+	struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
+	u8 n_lanes;
+	int ret;
+
+	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
+	fwnode_handle_put(endpoint);
+	if (ret)
+		return ret;
+
+	/* Check lanes number */
+	n_lanes = ep.bus.mipi_csi2.num_data_lanes;
+	if (n_lanes != 1) {
+		dev_err(sensor->dev, "Sensor only supports 1 lane, found %d\n",
+			n_lanes);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* Clock lane must be first */
+	if (ep.bus.mipi_csi2.clock_lane != 0) {
+		dev_err(sensor->dev, "Clock lane must be mapped to lane 0\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* Handle polarities in sensor configuration */
+	sensor->oif_ctrl = (ep.bus.mipi_csi2.lane_polarities[0] << 3) |
+			   (ep.bus.mipi_csi2.lane_polarities[1] << 6);
+
+	/* Check the link frequency set in device tree */
+	if (!ep.nr_of_link_frequencies) {
+		dev_err(sensor->dev,
+			"link-frequency property not found in DT\n");
+		ret = -EINVAL;
+		goto done;
+	}
+	if (ep.nr_of_link_frequencies != 1) {
+		dev_err(sensor->dev,
+			"Multiple link frequencies not supported\n");
+		ret = -EINVAL;
+		goto done;
+	}
+	sensor->link_freq = ep.link_frequencies[0];
+
+done:
+	v4l2_fwnode_endpoint_free(&ep);
+
+	return ret;
+}
+
+static int vd55g_parse_dt_gpios_array(struct vd55g *sensor, char *prop_name,
+				      u32 *array, int *nb)
+{
+	unsigned int i;
+	int ret;
+
+	*nb = device_property_count_u32(sensor->dev, prop_name);
+	if (*nb == -EINVAL) {
+		/* Property not found */
+		*nb = 0;
+		return 0;
+	}
+
+	ret = device_property_read_u32_array(sensor->dev, prop_name, array,
+					     *nb);
+	if (ret) {
+		dev_err(sensor->dev, "Failed to read %s prop\n", prop_name);
+		return ret;
+	}
+	for (i = 0; i < *nb; i++) {
+		if (array[i] >= VD55G_NB_GPIOS) {
+			dev_err(sensor->dev, "Invalid GPIO number %d\n",
+				array[i]);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int vd55g_parse_dt_gpios(struct vd55g *sensor)
+{
+	u32 led_gpios[VD55G_NB_GPIOS];
+	int nb_gpios_leds;
+	unsigned int i;
+	int ret;
+
+	/* Initialize GPIOs to default */
+	for (i = 0; i < VD55G_NB_GPIOS; i++)
+		sensor->gpios[i] = VD55G_GPIO_MODE_IN;
+	sensor->ext_leds_mask = 0;
+
+	/* Take into account optional 'st,leds' output for GPIOs */
+	ret = vd55g_parse_dt_gpios_array(sensor, "st,leds", led_gpios,
+					 &nb_gpios_leds);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < nb_gpios_leds; i++) {
+		sensor->gpios[led_gpios[i]] = VD55G_GPIO_MODE_STROBE;
+		set_bit(led_gpios[i], &sensor->ext_leds_mask);
+	}
+
+	if (nb_gpios_leds == 0) {
+		sensor->gpios[1] = VD55G_GPIO_MODE_STROBE;
+		set_bit(1, &sensor->ext_leds_mask);
+	}
+
+	return 0;
+}
+
+static int vd55g_parse_dt(struct vd55g *sensor)
+{
+	struct fwnode_handle *endpoint;
+	int ret;
+
+	endpoint =
+		fwnode_graph_get_next_endpoint(dev_fwnode(sensor->dev), NULL);
+	if (!endpoint)
+		return dev_err_probe(sensor->dev, -EPROBE_DEFER,
+				     "waiting for fwnode graph endpoint\n");
+
+	ret = vd55g_check_csi_conf(sensor, endpoint);
+	if (ret)
+		return ret;
+
+	return vd55g_parse_dt_gpios(sensor);
+}
+
+static int vd55g_subdev_init(struct vd55g *sensor)
+{
+	int ret;
+
+	/* Init sub device */
+	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	sensor->sd.internal_ops = &vd55g_internal_ops;
+
+	/* Init source pad */
+	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
+	if (ret) {
+		dev_err(sensor->dev, "Failed to init media entity: %d\n", ret);
+		return ret;
+	}
+
+	sensor->sd.state_lock = sensor->ctrl_handler.lock;
+	ret = v4l2_subdev_init_finalize(&sensor->sd);
+	if (ret) {
+		dev_err(sensor->dev, "Subdev init error: %d\n", ret);
+		goto err_ctrls;
+	}
+
+	/*
+	 * Initialize controls after v4l2_subdev_init_finalize() to make sure
+	 * active state is set
+	 */
+	ret = vd55g_init_ctrls(sensor);
+	if (ret) {
+		dev_err(sensor->dev, "Controls initialization failed %d\n",
+			ret);
+		goto err_media;
+	}
+
+	return 0;
+
+err_ctrls:
+	v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
+
+err_media:
+	media_entity_cleanup(&sensor->sd.entity);
+	return ret;
+}
+
+static void vd55g_subdev_cleanup(struct vd55g *sensor)
+{
+	v4l2_async_unregister_subdev(&sensor->sd);
+	v4l2_subdev_cleanup(&sensor->sd);
+	media_entity_cleanup(&sensor->sd.entity);
+	v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
+}
+
+static int vd55g_get_regulators(struct vd55g *sensor)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vd55g_supply_name); i++)
+		sensor->supplies[i].supply = vd55g_supply_name[i];
+
+	return devm_regulator_bulk_get(sensor->dev,
+				       ARRAY_SIZE(vd55g_supply_name),
+				       sensor->supplies);
+}
+
+static int vd55g_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct vd55g *sensor;
+	int ret;
+
+	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+	sensor->dev = &client->dev;
+
+	v4l2_i2c_subdev_init(&sensor->sd, client, &vd55g_subdev_ops);
+
+	ret = vd55g_parse_dt(sensor);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to parse Device Tree\n");
+
+	/* Get (and check) resources : power regs, ext clock, reset gpio */
+	ret = vd55g_get_regulators(sensor);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+	sensor->xclk = devm_v4l2_sensor_clk_get(dev, NULL);
+	if (IS_ERR(sensor->xclk))
+		return dev_err_probe(dev, PTR_ERR(sensor->xclk),
+				     "Failed to get xclk\n");
+
+	sensor->xclk_freq = clk_get_rate(sensor->xclk);
+
+	sensor->reset_gpio =
+		devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(sensor->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
+				     "Failed to get reset gpio\n");
+
+	sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
+	if (IS_ERR(sensor->regmap))
+		return dev_err_probe(dev, PTR_ERR(sensor->regmap),
+				     "Failed to init regmap\n");
+
+	/* Detect if sensor is present and if its revision is supported */
+	ret = vd55g_power_on(dev);
+	if (ret)
+		return ret;
+
+	/* Enable pm_runtime and power off the sensor */
+	pm_runtime_set_active(dev);
+	pm_runtime_get_noresume(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_set_autosuspend_delay(dev, 4000);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_put_autosuspend(dev);
+
+	ret = vd55g_subdev_init(sensor);
+	if (ret) {
+		dev_err(dev, "V4l2 init failed: %d\n", ret);
+		goto err_power_off;
+	}
+
+	ret = v4l2_async_register_subdev(&sensor->sd);
+	if (ret) {
+		dev_err(dev, "async subdev register failed %d\n", ret);
+		goto err_subdev;
+	}
+
+	return 0;
+
+err_subdev:
+	vd55g_subdev_cleanup(sensor);
+err_power_off:
+	pm_runtime_disable(dev);
+	pm_runtime_put_noidle(dev);
+	pm_runtime_dont_use_autosuspend(dev);
+	vd55g_power_off(dev);
+
+	return ret;
+}
+
+static void vd55g_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct vd55g *sensor = to_vd55g(sd);
+
+	vd55g_subdev_cleanup(sensor);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		vd55g_power_off(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_dont_use_autosuspend(&client->dev);
+}
+
+static const struct acpi_device_id vd55g_acpi_match[] = {
+	{ "SMO55F0", (kernel_ulong_t)&vd55g0_chip_info },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, vd55g_acpi_match);
+
+static const struct of_device_id vd55g_dt_ids[] = {
+	{ .compatible = "st,vd55g0", .data = &vd55g0_chip_info },
+	{ .compatible = "st,vd55g1", .data = &vd55g1_chip_info },
+	{ .compatible = "st,vd65g4", .data = &vd65g4_chip_info },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vd55g_dt_ids);
+
+static const struct dev_pm_ops vd55g_pm_ops = {
+	SET_RUNTIME_PM_OPS(vd55g_power_off, vd55g_power_on, NULL)
+};
+
+static struct i2c_driver vd55g_i2c_driver = {
+	.driver = {
+		.name  = "vd55g",
+		.acpi_match_table = vd55g_acpi_match,
+		.of_match_table = vd55g_dt_ids,
+		.pm = &vd55g_pm_ops,
+		},
+	.probe = vd55g_probe,
+	.remove = vd55g_remove,
+};
+
+module_i2c_driver(vd55g_i2c_driver);
+
+MODULE_AUTHOR("Peter Marshall <pm@petermarshall.ca>");
+MODULE_AUTHOR("Benjamin Mugnier <benjamin.mugnier@foss.st.com>");
+MODULE_AUTHOR("Sylvain Petinot <sylvain.petinot@foss.st.com>");
+MODULE_DESCRIPTION("VD55G camera subdev driver");
+MODULE_FIRMWARE("vd55g0-cut1.bin");
+MODULE_FIRMWARE("vd55g0-cut2.bin");
+MODULE_FIRMWARE("vd55g1.bin");
+MODULE_LICENSE("GPL");
-- 
2.55.0


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

* [PATCH 6/7] media: i2c: st-vd55g: Remove legacy vd55g1 remnants
  2026-09-02 20:45 [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Peter Marshall
                   ` (4 preceding siblings ...)
  2026-09-02 20:45 ` [PATCH 5/7] media: i2c: st-vd55g: Add generic driver and VD55G0 support Peter Marshall
@ 2026-09-02 20:45 ` Peter Marshall
  2026-09-03 13:43   ` Krzysztof Kozlowski
  2026-09-02 20:45 ` [PATCH 7/7] MAINTAINERS: Update ST VD55G camera driver entry Peter Marshall
  2026-09-04 12:06 ` [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Benjamin Mugnier
  7 siblings, 1 reply; 14+ messages in thread
From: Peter Marshall @ 2026-09-02 20:45 UTC (permalink / raw)
  To: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot
  Cc: linux-kernel, devicetree, linux-media, platform-driver-x86,
	Peter Marshall

vd55g1 is superseded by the generic vd55g driver supporting the same
hardware and feature set. Remove it.

Signed-off-by: Peter Marshall <pm@petermarshall.ca>
---
 drivers/media/i2c/Makefile |    1 -
 drivers/media/i2c/vd55g1.c | 1767 ------------------------------------
 2 files changed, 1768 deletions(-)
 delete mode 100644 drivers/media/i2c/vd55g1.c

diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index d87b64893..b40b6020e 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -165,7 +165,6 @@ obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o
 obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_VD55G) += vd55g.o
-obj-$(CONFIG_VIDEO_VD55G1) += vd55g1.o
 obj-$(CONFIG_VIDEO_VD56G3) += vd56g3.o
 obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o
 obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c
deleted file mode 100644
index 1ae42e448..000000000
--- a/drivers/media/i2c/vd55g1.c
+++ /dev/null
@@ -1,1767 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Driver for VD55G1 global shutter sensor family driver
- *
- * Copyright (C) 2025 STMicroelectronics SA
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-#include <linux/i2c.h>
-#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/pm_runtime.h>
-#include <linux/property.h>
-#include <linux/regmap.h>
-#include <linux/regulator/consumer.h>
-#include <linux/unaligned.h>
-#include <linux/units.h>
-
-#include <media/mipi-csi2.h>
-#include <media/v4l2-async.h>
-#include <media/v4l2-cci.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-fwnode.h>
-#include <media/v4l2-subdev.h>
-
-/* Register Map */
-#define VD55G1_REG_MODEL_ID				CCI_REG32_LE(0x0000)
-#define VD55G1_MODEL_ID_VD55G1				0x53354731 /* Mono */
-#define VD55G1_MODEL_ID_VD65G4				0x53354733 /* RGB */
-#define VD55G1_REG_REVISION				CCI_REG16_LE(0x0004)
-#define VD55G1_REVISION_CCB				0x2020
-#define VD55G1_REVISION_BAYER				0x3030
-#define VD55G1_REG_FWPATCH_REVISION			CCI_REG16_LE(0x0012)
-#define VD55G1_REG_FWPATCH_START_ADDR			CCI_REG8(0x2000)
-#define VD55G1_REG_SYSTEM_FSM				CCI_REG8(0x001c)
-#define VD55G1_SYSTEM_FSM_READY_TO_BOOT			0x01
-#define VD55G1_SYSTEM_FSM_SW_STBY			0x02
-#define VD55G1_SYSTEM_FSM_STREAMING			0x03
-#define VD55G1_REG_BOOT					CCI_REG8(0x0200)
-#define VD55G1_BOOT_BOOT				1
-#define VD55G1_BOOT_PATCH_AND_BOOT			2
-#define VD55G1_REG_STBY					CCI_REG8(0x0201)
-#define VD55G1_STBY_START_STREAM			1
-#define VD55G1_REG_STREAMING				CCI_REG8(0x0202)
-#define VD55G1_STREAMING_STOP_STREAM			1
-#define VD55G1_REG_EXT_CLOCK				CCI_REG32_LE(0x0220)
-#define VD55G1_REG_LINE_LENGTH				CCI_REG16_LE(0x0300)
-#define VD55G1_REG_ORIENTATION				CCI_REG8(0x0302)
-#define VD55G1_REG_FORMAT_CTRL				CCI_REG8(0x030a)
-#define VD55G1_REG_OIF_CTRL				CCI_REG16_LE(0x030c)
-#define VD55G1_REG_ISL_ENABLE				CCI_REG16_LE(0x326)
-#define VD55G1_REG_OIF_IMG_CTRL				CCI_REG8(0x030f)
-#define VD55G1_REG_MIPI_DATA_RATE			CCI_REG32_LE(0x0224)
-#define VD55G1_REG_PATGEN_CTRL				CCI_REG16_LE(0x0304)
-#define VD55G1_PATGEN_TYPE_SHIFT			4
-#define VD55G1_PATGEN_ENABLE				BIT(0)
-#define VD55G1_REG_MANUAL_ANALOG_GAIN			CCI_REG8(0x0501)
-#define VD55G1_REG_MANUAL_COARSE_EXPOSURE		CCI_REG16_LE(0x0502)
-#define VD55G1_REG_MANUAL_DIGITAL_GAIN			CCI_REG16_LE(0x0504)
-#define VD55G1_REG_APPLIED_COARSE_EXPOSURE		CCI_REG16_LE(0x00e8)
-#define VD55G1_REG_APPLIED_ANALOG_GAIN			CCI_REG16_LE(0x00ea)
-#define VD55G1_REG_APPLIED_DIGITAL_GAIN			CCI_REG16_LE(0x00ec)
-#define VD55G1_REG_AE_FORCE_COLDSTART			CCI_REG8(0x0308)
-#define VD55G1_REG_AE_COLDSTART_EXP_TIME		CCI_REG32_LE(0x0374)
-#define VD55G1_REG_READOUT_CTRL				CCI_REG8(0x052e)
-#define VD55G1_READOUT_CTRL_BIN_MODE_NORMAL		0
-#define VD55G1_READOUT_CTRL_BIN_MODE_DIGITAL_X2		1
-#define VD55G1_REG_DUSTER_CTRL				CCI_REG8(0x03ae)
-#define VD55G1_DUSTER_ENABLE				BIT(0)
-#define VD55G1_DUSTER_DISABLE				0
-#define VD55G1_DUSTER_DYN_ENABLE			BIT(1)
-#define VD55G1_DUSTER_RING_ENABLE			BIT(4)
-#define VD55G1_REG_AE_TARGET_PERCENTAGE			CCI_REG8(0x0486)
-#define VD55G1_REG_NEXT_CTX				CCI_REG16_LE(0x03e4)
-#define VD55G1_REG_EXPOSURE_USE_CASES			CCI_REG8(0x0312)
-#define VD55G1_EXPOSURE_USE_CASES_MULTI_CONTEXT		BIT(2)
-#define VD55G1_REG_EXPOSURE_MAX_COARSE			CCI_REG16_LE(0x0372)
-#define VD55G1_EXPOSURE_MAX_COARSE_DEF			0x7fff
-#define VD55G1_EXPOSURE_MAX_COARSE_SUB			446
-#define VD55G1_REG_CTX_REPEAT_COUNT_CTX0		CCI_REG16_LE(0x03dc)
-#define VD55G1_REG_CTX_REPEAT_COUNT_CTX1		CCI_REG16_LE(0x03de)
-
-#define VD55G1_REG_EXP_MODE(ctx) \
-	CCI_REG8(0x0500 + VD55G1_CTX_OFFSET * (ctx))
-#define VD55G1_REG_FRAME_LENGTH(ctx) \
-	CCI_REG32_LE(0x050c + VD55G1_CTX_OFFSET * (ctx))
-#define VD55G1_REG_X_START(ctx) \
-	CCI_REG16_LE(0x0514 + VD55G1_CTX_OFFSET * (ctx))
-#define VD55G1_REG_X_WIDTH(ctx) \
-	CCI_REG16_LE(0x0516 + VD55G1_CTX_OFFSET * (ctx))
-#define VD55G1_REG_Y_START(ctx) \
-	CCI_REG16_LE(0x0510 + VD55G1_CTX_OFFSET * (ctx))
-#define VD55G1_REG_Y_HEIGHT(ctx) \
-	CCI_REG16_LE(0x0512 + VD55G1_CTX_OFFSET * (ctx))
-#define VD55G1_REG_GPIO_0_CTRL(ctx) \
-	CCI_REG8(0x051d + VD55G1_CTX_OFFSET * (ctx))
-#define VD55G1_GPIO_MODE_FSYNC_OUT			0x00
-#define VD55G1_GPIO_MODE_IN				0x01
-#define VD55G1_GPIO_MODE_STROBE				0x02
-#define VD55G1_REG_VT_MODE(ctx) \
-	CCI_REG8(0x0536 + VD55G1_CTX_OFFSET * (ctx))
-#define VD55G1_VT_MODE_NORMAL				0
-#define VD55G1_VT_MODE_SUBTRACTION			1
-#define VD55G1_REG_MASK_FRAME_CTRL(ctx) \
-	CCI_REG8(0x0537 + VD55G1_CTX_OFFSET * (ctx))
-#define VD55G1_MASK_FRAME_CTRL_OUTPUT			0
-#define VD55G1_MASK_FRAME_CTRL_MASK			1
-#define VD55G1_REG_EXPOSURE_INSTANCE(ctx) \
-	CCI_REG32_LE(0x52D + VD55G1_CTX_OFFSET * (ctx))
-
-#define VD55G1_WIDTH					804
-#define VD55G1_HEIGHT					704
-#define VD55G1_MODE_DEF					0
-#define VD55G1_NB_GPIOS					4
-#define VD55G1_MBUS_CODE_DEF				0
-#define VD55G1_DGAIN_DEF				256
-#define VD55G1_AGAIN_DEF				19
-#define VD55G1_EXPO_MAX_TERM				64
-#define VD55G1_EXPO_DEF					500
-#define VD55G1_LINE_LENGTH_MIN				1128
-#define VD55G1_LINE_LENGTH_SUB_MIN			1344
-#define VD55G1_VBLANK_MIN				86
-#define VD55G1_VBLANK_MAX				0xffff
-#define VD55G1_FRAME_LENGTH_DEF				1860 /* 60 fps */
-#define VD55G1_MIPI_MARGIN				900
-#define VD55G1_CTX_OFFSET				0x50
-#define VD55G1_FWPATCH_REVISION_MAJOR			2
-#define VD55G1_FWPATCH_REVISION_MINOR			9
-#define VD55G1_XCLK_FREQ_MIN				(6 * HZ_PER_MHZ)
-#define VD55G1_XCLK_FREQ_MAX				(27 * HZ_PER_MHZ)
-#define VD55G1_MIPI_RATE_MIN				(250 * MEGA)
-#define VD55G1_MIPI_RATE_MAX				(1200 * MEGA)
-
-#define VD55G1_MODEL_ID_NAME(id) \
-	((id) == VD55G1_MODEL_ID_VD55G1 ? "vd55g1" : "vd65g4")
-
-#include "vd55g-fw.h"
-
-static const char * const vd55g1_tp_menu[] = {
-	"Disabled",
-	"Diagonal Grey Scale",
-	"Pseudo-random Noise",
-};
-
-static const s64 vd55g1_ev_bias_menu[] = {
-	-3000, -2500, -2000, -1500, -1000, -500,
-	    0,
-	  500,  1000,  1500,  2000,  2500, 3000,
-};
-
-static const char * const vd55g1_hdr_menu[] = {
-	"No HDR",
-	/*
-	 * This mode acquires 2 frames on the sensor, the first one is ditched
-	 * out and only used for auto exposure data, the second one is output to
-	 * the host
-	 */
-	"Internal subtraction",
-};
-
-static const char * const vd55g1_supply_name[] = {
-	"vcore",
-	"vddio",
-	"vana",
-};
-
-enum vd55g1_hdr_mode {
-	VD55G1_NO_HDR,
-	VD55G1_HDR_SUB,
-};
-
-struct vd55g1_mode {
-	u32 width;
-	u32 height;
-};
-
-static const u32 vd55g1_mbus_formats_mono[] = {
-	MEDIA_BUS_FMT_Y8_1X8,
-	MEDIA_BUS_FMT_Y10_1X10,
-};
-
-/* Format order is : no flip, hflip, vflip, both */
-static const u32 vd55g1_mbus_formats_bayer[][4] = {
-	{
-		MEDIA_BUS_FMT_SRGGB8_1X8,
-		MEDIA_BUS_FMT_SGRBG8_1X8,
-		MEDIA_BUS_FMT_SGBRG8_1X8,
-		MEDIA_BUS_FMT_SBGGR8_1X8,
-	},
-	{
-		MEDIA_BUS_FMT_SRGGB10_1X10,
-		MEDIA_BUS_FMT_SGRBG10_1X10,
-		MEDIA_BUS_FMT_SGBRG10_1X10,
-		MEDIA_BUS_FMT_SBGGR10_1X10,
-	},
-};
-
-static const struct vd55g1_mode vd55g1_supported_modes[] = {
-	{
-		.width = VD55G1_WIDTH,
-		.height = VD55G1_HEIGHT,
-	},
-	{
-		.width = 800,
-		.height = VD55G1_HEIGHT,
-	},
-	{
-		.width = 800,
-		.height = 600,
-	},
-	{
-		.width = 640,
-		.height = 480,
-	},
-	{
-		.width = 320,
-		.height = 240,
-	},
-};
-
-enum vd55g1_expo_state {
-	VD55G1_EXP_AUTO,
-	VD55G1_EXP_FREEZE,
-	VD55G1_EXP_MANUAL,
-	VD55G1_EXP_SINGLE_STEP,
-	VD55G1_EXP_BYPASS,
-};
-
-struct vd55g1_vblank_limits {
-	u16 min;
-	u16 def;
-	u16 max;
-};
-
-struct vd55g1 {
-	struct device *dev;
-	unsigned int id;
-	struct v4l2_subdev sd;
-	struct media_pad pad;
-	struct regulator_bulk_data supplies[ARRAY_SIZE(vd55g1_supply_name)];
-	struct gpio_desc *reset_gpio;
-	struct clk *xclk;
-	struct regmap *regmap;
-	u32 xclk_freq;
-	u16 oif_ctrl;
-	u8 gpios[VD55G1_NB_GPIOS];
-	unsigned long ext_leds_mask;
-	u32 mipi_rate;
-	u32 pixel_clock;
-	u64 link_freq;
-	struct v4l2_ctrl_handler ctrl_handler;
-	struct v4l2_ctrl *pixel_rate_ctrl;
-	struct v4l2_ctrl *vblank_ctrl;
-	struct v4l2_ctrl *hblank_ctrl;
-	struct {
-		struct v4l2_ctrl *hflip_ctrl;
-		struct v4l2_ctrl *vflip_ctrl;
-	};
-	struct v4l2_ctrl *patgen_ctrl;
-	struct {
-		struct v4l2_ctrl *ae_ctrl;
-		struct v4l2_ctrl *expo_ctrl;
-		struct v4l2_ctrl *again_ctrl;
-		struct v4l2_ctrl *dgain_ctrl;
-	};
-	struct v4l2_ctrl *ae_lock_ctrl;
-	struct v4l2_ctrl *ae_bias_ctrl;
-	struct v4l2_ctrl *led_ctrl;
-	struct v4l2_ctrl *hdr_ctrl;
-};
-
-static inline struct vd55g1 *to_vd55g1(struct v4l2_subdev *sd)
-{
-	return container_of_const(sd, struct vd55g1, sd);
-}
-
-static inline struct vd55g1 *ctrl_to_vd55g1(struct v4l2_ctrl *ctrl)
-{
-	struct v4l2_subdev *sd = &container_of_const(ctrl->handler,
-						     struct vd55g1,
-						     ctrl_handler)->sd;
-
-	return to_vd55g1(sd);
-}
-
-static unsigned int vd55g1_get_fmt_bpp(u32 code)
-{
-	switch (code) {
-	case MEDIA_BUS_FMT_Y8_1X8:
-	case MEDIA_BUS_FMT_SRGGB8_1X8:
-	case MEDIA_BUS_FMT_SGRBG8_1X8:
-	case MEDIA_BUS_FMT_SGBRG8_1X8:
-	case MEDIA_BUS_FMT_SBGGR8_1X8:
-	default:
-		return 8;
-
-	case MEDIA_BUS_FMT_Y10_1X10:
-	case MEDIA_BUS_FMT_SRGGB10_1X10:
-	case MEDIA_BUS_FMT_SGRBG10_1X10:
-	case MEDIA_BUS_FMT_SGBRG10_1X10:
-	case MEDIA_BUS_FMT_SBGGR10_1X10:
-		return 10;
-	}
-}
-
-static unsigned int vd55g1_get_fmt_data_type(u32 code)
-{
-	switch (code) {
-	case MEDIA_BUS_FMT_Y8_1X8:
-	case MEDIA_BUS_FMT_SRGGB8_1X8:
-	case MEDIA_BUS_FMT_SGRBG8_1X8:
-	case MEDIA_BUS_FMT_SGBRG8_1X8:
-	case MEDIA_BUS_FMT_SBGGR8_1X8:
-	default:
-		return MIPI_CSI2_DT_RAW8;
-
-	case MEDIA_BUS_FMT_Y10_1X10:
-	case MEDIA_BUS_FMT_SRGGB10_1X10:
-	case MEDIA_BUS_FMT_SGRBG10_1X10:
-	case MEDIA_BUS_FMT_SGBRG10_1X10:
-	case MEDIA_BUS_FMT_SBGGR10_1X10:
-		return MIPI_CSI2_DT_RAW10;
-	}
-}
-
-static u32 vd55g1_get_fmt_code(struct vd55g1 *sensor, u32 code)
-{
-	unsigned int i, j;
-
-	if (sensor->id == VD55G1_MODEL_ID_VD55G1)
-		return code;
-
-	for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_formats_bayer); i++) {
-		for (j = 0; j < ARRAY_SIZE(vd55g1_mbus_formats_bayer[i]); j++) {
-			if (vd55g1_mbus_formats_bayer[i][j] == code)
-				goto adapt_bayer_pattern;
-		}
-	}
-	dev_warn(sensor->dev, "Unsupported mbus format\n");
-
-	return code;
-
-adapt_bayer_pattern:
-	j = 0;
-	/* In first init_state() call, controls might not be initialized yet */
-	if (sensor->hflip_ctrl && sensor->vflip_ctrl) {
-		j = (sensor->hflip_ctrl->val ? 1 : 0) +
-			(sensor->vflip_ctrl->val ? 2 : 0);
-	}
-
-	return vd55g1_mbus_formats_bayer[i][j];
-}
-
-static s32 vd55g1_get_pixel_rate(struct vd55g1 *sensor,
-				 struct v4l2_mbus_framefmt *format)
-{
-	return sensor->mipi_rate / vd55g1_get_fmt_bpp(format->code);
-}
-
-static unsigned int vd55g1_get_hblank_min(struct vd55g1 *sensor,
-					  struct v4l2_mbus_framefmt *format,
-					  struct v4l2_rect *crop)
-{
-	u32 mipi_req_line_time;
-	u32 mipi_req_line_length;
-	u32 min_line_length;
-
-	/* MIPI required time */
-	mipi_req_line_time = (crop->width *
-			      vd55g1_get_fmt_bpp(format->code) +
-			      VD55G1_MIPI_MARGIN) /
-			      (sensor->mipi_rate / MEGA);
-	mipi_req_line_length = mipi_req_line_time * sensor->pixel_clock /
-			       HZ_PER_MHZ;
-
-	/* Absolute time required for ADCs to convert pixels */
-	min_line_length = VD55G1_LINE_LENGTH_MIN;
-	if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB)
-		min_line_length = VD55G1_LINE_LENGTH_SUB_MIN;
-
-	/* Respect both constraint */
-	min_line_length = max(min_line_length, mipi_req_line_length);
-
-	return min_line_length - crop->width;
-}
-
-static void vd55g1_get_vblank_limits(struct vd55g1 *sensor,
-				     struct v4l2_rect *crop,
-				     struct vd55g1_vblank_limits *limits)
-{
-	limits->min = VD55G1_VBLANK_MIN;
-	limits->def = VD55G1_FRAME_LENGTH_DEF - crop->height;
-	limits->max = VD55G1_VBLANK_MAX - crop->height;
-}
-
-#define vd55g1_read(sensor, reg, val, err) \
-	cci_read((sensor)->regmap, reg, val, err)
-
-#define vd55g1_write(sensor, reg, val, err) \
-	cci_write((sensor)->regmap, reg, val, err)
-
-static int vd55g1_write_array(struct vd55g1 *sensor, u32 reg, unsigned int len,
-			      const u8 *array, int *err)
-{
-	unsigned int chunk_sz = 1024;
-	unsigned int sz;
-	int ret = 0;
-
-	if (err && *err)
-		return *err;
-
-	/*
-	 * This loop isn't necessary but in certains conditions (platforms, cpu
-	 * load, etc.) it has been observed that the bulk write could timeout.
-	 */
-	while (len) {
-		sz = min(len, chunk_sz);
-		ret = regmap_bulk_write(sensor->regmap, reg, array, sz);
-		if (ret < 0)
-			goto out;
-		len -= sz;
-		reg += sz;
-		array += sz;
-	}
-
-out:
-	if (ret && err)
-		*err = ret;
-
-	return ret;
-}
-
-static int vd55g1_poll_reg(struct vd55g1 *sensor, u32 reg, u8 poll_val,
-			   int *err)
-{
-	unsigned int val = 0;
-	int ret;
-
-	if (err && *err)
-		return *err;
-
-	ret = regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val,
-				       (val == poll_val), 2000,
-				       500 * USEC_PER_MSEC);
-
-	if (ret && err)
-		*err = ret;
-
-	return ret;
-}
-
-static int vd55g1_wait_state(struct vd55g1 *sensor, int state, int *err)
-{
-	return vd55g1_poll_reg(sensor, VD55G1_REG_SYSTEM_FSM, state, err);
-}
-
-static int vd55g1_prepare_clock_tree(struct vd55g1 *sensor)
-{
-	u32 sys_clk, mipi_div, pixel_div;
-
-	if (sensor->xclk_freq < VD55G1_XCLK_FREQ_MIN ||
-	    sensor->xclk_freq > VD55G1_XCLK_FREQ_MAX) {
-		dev_err(sensor->dev,
-			"Only %luMhz-%luMhz clock range supported. Provided %lu MHz\n",
-			VD55G1_XCLK_FREQ_MIN / HZ_PER_MHZ,
-			VD55G1_XCLK_FREQ_MAX / HZ_PER_MHZ,
-			sensor->xclk_freq / HZ_PER_MHZ);
-		return -EINVAL;
-	}
-
-	/* MIPI bus is double data rate */
-	sensor->mipi_rate = sensor->link_freq * 2;
-
-	if (sensor->mipi_rate < VD55G1_MIPI_RATE_MIN ||
-	    sensor->mipi_rate > VD55G1_MIPI_RATE_MAX) {
-		dev_err(sensor->dev,
-			"Only %luMbps-%luMbps data rate range supported. Provided %lu Mbps\n",
-			VD55G1_MIPI_RATE_MIN / MEGA,
-			VD55G1_MIPI_RATE_MAX / MEGA,
-			sensor->mipi_rate / MEGA);
-		return -EINVAL;
-	}
-
-	if (sensor->mipi_rate <= 300 * MEGA)
-		mipi_div = 4;
-	else if (sensor->mipi_rate <= 600 * MEGA)
-		mipi_div = 2;
-	else
-		mipi_div = 1;
-
-	sys_clk = sensor->mipi_rate * mipi_div;
-
-	if (sys_clk <= 780 * HZ_PER_MHZ)
-		pixel_div = 5;
-	else if (sys_clk <= 900 * HZ_PER_MHZ)
-		pixel_div = 6;
-	else
-		pixel_div = 8;
-
-	sensor->pixel_clock = sys_clk / pixel_div;
-
-	return 0;
-}
-
-static int vd55g1_update_patgen(struct vd55g1 *sensor, u32 patgen_index)
-{
-	static const u8 index2val[] = {
-		0x0, 0x22, 0x28
-	};
-	u32 pattern = index2val[patgen_index];
-	u32 reg = pattern << VD55G1_PATGEN_TYPE_SHIFT;
-	u8 duster = VD55G1_DUSTER_RING_ENABLE | VD55G1_DUSTER_DYN_ENABLE |
-		    VD55G1_DUSTER_ENABLE;
-	int ret = 0;
-
-	BUILD_BUG_ON(ARRAY_SIZE(index2val) != ARRAY_SIZE(vd55g1_tp_menu));
-
-	if (pattern != 0) {
-		reg |= VD55G1_PATGEN_ENABLE;
-		/* Take care of duster to not mess up the test pattern output */
-		duster = VD55G1_DUSTER_DISABLE;
-	}
-
-	vd55g1_write(sensor, VD55G1_REG_DUSTER_CTRL, duster, &ret);
-	vd55g1_write(sensor, VD55G1_REG_PATGEN_CTRL, reg, &ret);
-
-	return ret;
-}
-
-static int vd55g1_update_expo_cluster(struct vd55g1 *sensor, bool is_auto)
-{
-	enum vd55g1_expo_state expo_state = is_auto ? VD55G1_EXP_AUTO :
-						      VD55G1_EXP_MANUAL;
-	int ret = 0;
-
-	if (sensor->ae_ctrl->is_new)
-		vd55g1_write(sensor, VD55G1_REG_EXP_MODE(0), expo_state, &ret);
-
-	if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB &&
-	    sensor->hdr_ctrl->is_new) {
-		vd55g1_write(sensor, VD55G1_REG_EXP_MODE(1), VD55G1_EXP_BYPASS,
-			     &ret);
-		if (ret)
-			return ret;
-	}
-
-	if (!is_auto && sensor->expo_ctrl->is_new)
-		vd55g1_write(sensor, VD55G1_REG_MANUAL_COARSE_EXPOSURE,
-			     sensor->expo_ctrl->val, &ret);
-
-	if (!is_auto && sensor->again_ctrl->is_new)
-		vd55g1_write(sensor, VD55G1_REG_MANUAL_ANALOG_GAIN,
-			     sensor->again_ctrl->val, &ret);
-
-	if (!is_auto && sensor->dgain_ctrl->is_new)
-		vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN,
-			     sensor->dgain_ctrl->val, &ret);
-
-	return ret;
-}
-
-static int vd55g1_lock_exposure(struct vd55g1 *sensor, u32 lock_val)
-{
-	bool ae_lock = lock_val & V4L2_LOCK_EXPOSURE;
-	enum vd55g1_expo_state expo_state = ae_lock ? VD55G1_EXP_FREEZE :
-						      VD55G1_EXP_AUTO;
-	int ret = 0;
-
-	if (sensor->ae_ctrl->val == V4L2_EXPOSURE_AUTO)
-		vd55g1_write(sensor, VD55G1_REG_EXP_MODE(0), expo_state, &ret);
-
-	return ret;
-}
-
-static int vd55g1_read_expo_cluster(struct vd55g1 *sensor)
-{
-	u64 exposure = 0;
-	u64 again = 0;
-	u64 dgain = 0;
-	int ret = 0;
-
-	vd55g1_read(sensor, VD55G1_REG_APPLIED_COARSE_EXPOSURE, &exposure,
-		    &ret);
-	vd55g1_read(sensor, VD55G1_REG_APPLIED_ANALOG_GAIN, &again, &ret);
-	vd55g1_read(sensor, VD55G1_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret);
-	if (ret)
-		return ret;
-
-	sensor->expo_ctrl->cur.val = exposure;
-	sensor->again_ctrl->cur.val = again;
-	sensor->dgain_ctrl->cur.val = dgain;
-
-	return 0;
-}
-
-static int vd55g1_update_frame_length(struct vd55g1 *sensor,
-				      unsigned int frame_length)
-{
-	int ret = 0;
-
-	if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB)
-		vd55g1_write(sensor, VD55G1_REG_FRAME_LENGTH(1), frame_length,
-			     &ret);
-	vd55g1_write(sensor, VD55G1_REG_FRAME_LENGTH(0), frame_length, &ret);
-
-	return ret;
-}
-
-static int vd55g1_update_exposure_target(struct vd55g1 *sensor, int index)
-{
-	/*
-	 * Find auto exposure target with: default target exposure * 2^EV
-	 * Defaut target exposure being 27 for the sensor.
-	 */
-	static const unsigned int index2exposure_target[] = {
-		3, 5, 7, 10, 14, 19, 27, 38, 54, 76, 108, 153, 216,
-	};
-	int exposure_target = index2exposure_target[index];
-
-	return vd55g1_write(sensor, VD55G1_REG_AE_TARGET_PERCENTAGE,
-			    exposure_target, NULL);
-}
-
-static int vd55g1_apply_cold_start(struct vd55g1 *sensor,
-				   struct v4l2_rect *crop)
-{
-	/*
-	 * Cold start register is a single register expressed as exposure time
-	 * in us. This differ from status registers being a combination of
-	 * exposure, digital gain, and analog gain, requiring the following
-	 * format conversion.
-	 */
-	unsigned int line_length = crop->width + sensor->hblank_ctrl->val;
-	unsigned int line_time_us = DIV_ROUND_UP(line_length * MEGA,
-						 sensor->pixel_clock);
-	u8 d_gain = DIV_ROUND_CLOSEST(sensor->dgain_ctrl->val, 1 << 8);
-	u8 a_gain = DIV_ROUND_CLOSEST(32, (32 - sensor->again_ctrl->val));
-	unsigned int expo_us = sensor->expo_ctrl->val * d_gain * a_gain *
-			       line_time_us;
-	int ret = 0;
-
-	vd55g1_write(sensor, VD55G1_REG_AE_FORCE_COLDSTART, 1, &ret);
-	vd55g1_write(sensor, VD55G1_REG_AE_COLDSTART_EXP_TIME, expo_us, &ret);
-
-	return ret;
-}
-
-static void vd55g1_update_pad_fmt(struct vd55g1 *sensor,
-				  const struct vd55g1_mode *mode, u32 code,
-				  struct v4l2_mbus_framefmt *fmt)
-{
-	fmt->code = vd55g1_get_fmt_code(sensor, code);
-	fmt->width = mode->width;
-	fmt->height = mode->height;
-	fmt->colorspace = V4L2_COLORSPACE_RAW;
-	fmt->field = V4L2_FIELD_NONE;
-	fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
-	fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
-	fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
-}
-
-static int vd55g1_update_hdr_mode(struct vd55g1 *sensor)
-{
-	int ret = 0;
-
-	switch (sensor->hdr_ctrl->val) {
-	case VD55G1_NO_HDR:
-		vd55g1_write(sensor, VD55G1_REG_EXPOSURE_MAX_COARSE,
-			     VD55G1_EXPOSURE_MAX_COARSE_DEF, &ret);
-		vd55g1_write(sensor, VD55G1_REG_EXPOSURE_USE_CASES, 0, &ret);
-		vd55g1_write(sensor, VD55G1_REG_NEXT_CTX, 0x0, &ret);
-
-		vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX0, 0, &ret);
-
-		vd55g1_write(sensor, VD55G1_REG_VT_MODE(0),
-			     VD55G1_VT_MODE_NORMAL, &ret);
-		vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(0),
-			     VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret);
-		break;
-	case VD55G1_HDR_SUB:
-		vd55g1_write(sensor, VD55G1_REG_EXPOSURE_MAX_COARSE,
-			     VD55G1_EXPOSURE_MAX_COARSE_SUB, &ret);
-		vd55g1_write(sensor, VD55G1_REG_EXPOSURE_USE_CASES,
-			     VD55G1_EXPOSURE_USE_CASES_MULTI_CONTEXT, &ret);
-		vd55g1_write(sensor, VD55G1_REG_NEXT_CTX, 0x0001, &ret);
-
-		vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX0, 1, &ret);
-		vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX1, 1, &ret);
-
-		vd55g1_write(sensor, VD55G1_REG_VT_MODE(0),
-			     VD55G1_VT_MODE_NORMAL, &ret);
-		vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(0),
-			     VD55G1_MASK_FRAME_CTRL_MASK, &ret);
-		vd55g1_write(sensor, VD55G1_REG_EXPOSURE_INSTANCE(0), 0, &ret);
-		vd55g1_write(sensor, VD55G1_REG_VT_MODE(1),
-			     VD55G1_VT_MODE_SUBTRACTION, &ret);
-		vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(1),
-			     VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret);
-		vd55g1_write(sensor, VD55G1_REG_EXPOSURE_INSTANCE(1), 1, &ret);
-		break;
-	default:
-		ret = -EINVAL;
-	}
-
-	return ret;
-}
-
-static int vd55g1_set_framefmt(struct vd55g1 *sensor,
-			       struct v4l2_mbus_framefmt *format,
-			       struct v4l2_rect *crop)
-{
-	u8 binning;
-	int ret = 0;
-
-	vd55g1_write(sensor, VD55G1_REG_FORMAT_CTRL,
-		     vd55g1_get_fmt_bpp(format->code), &ret);
-	vd55g1_write(sensor, VD55G1_REG_OIF_IMG_CTRL,
-		     vd55g1_get_fmt_data_type(format->code), &ret);
-
-	switch (crop->width / format->width) {
-	case 1:
-	default:
-		binning = VD55G1_READOUT_CTRL_BIN_MODE_NORMAL;
-		break;
-	case 2:
-		binning = VD55G1_READOUT_CTRL_BIN_MODE_DIGITAL_X2;
-		break;
-	}
-	vd55g1_write(sensor, VD55G1_REG_READOUT_CTRL, binning, &ret);
-
-	vd55g1_write(sensor, VD55G1_REG_X_START(0), crop->left, &ret);
-	vd55g1_write(sensor, VD55G1_REG_X_WIDTH(0), crop->width, &ret);
-	vd55g1_write(sensor, VD55G1_REG_Y_START(0), crop->top, &ret);
-	vd55g1_write(sensor, VD55G1_REG_Y_HEIGHT(0), crop->height, &ret);
-
-	vd55g1_write(sensor, VD55G1_REG_X_START(1), crop->left, &ret);
-	vd55g1_write(sensor, VD55G1_REG_X_WIDTH(1), crop->width, &ret);
-	vd55g1_write(sensor, VD55G1_REG_Y_START(1), crop->top, &ret);
-	vd55g1_write(sensor, VD55G1_REG_Y_HEIGHT(1), crop->height, &ret);
-
-	return ret;
-}
-
-static int vd55g1_update_gpios(struct vd55g1 *sensor, unsigned long gpio_mask)
-{
-	unsigned long io;
-	u8 gpio_val;
-	int ret = 0;
-
-	for_each_set_bit(io, &gpio_mask, VD55G1_NB_GPIOS) {
-		gpio_val = sensor->gpios[io];
-
-		if (gpio_val == VD55G1_GPIO_MODE_STROBE &&
-		    sensor->led_ctrl->val == V4L2_FLASH_LED_MODE_NONE) {
-			gpio_val = VD55G1_GPIO_MODE_IN;
-			if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) {
-				/* Make its context 1 counterpart strobe too */
-				vd55g1_write(sensor,
-					     VD55G1_REG_GPIO_0_CTRL(1) + io,
-					     gpio_val, &ret);
-			}
-		}
-
-		ret = vd55g1_write(sensor, VD55G1_REG_GPIO_0_CTRL(0) + io,
-				   gpio_val, &ret);
-	}
-
-	return ret;
-}
-
-static int vd55g1_ro_ctrls_setup(struct vd55g1 *sensor, struct v4l2_rect *crop)
-{
-	return vd55g1_write(sensor, VD55G1_REG_LINE_LENGTH,
-			    crop->width + sensor->hblank_ctrl->val, NULL);
-}
-
-static void vd55g1_grab_ctrls(struct vd55g1 *sensor, bool enable)
-{
-	/* These settings cannot change during stream */
-	v4l2_ctrl_grab(sensor->hflip_ctrl, enable);
-	v4l2_ctrl_grab(sensor->vflip_ctrl, enable);
-	v4l2_ctrl_grab(sensor->patgen_ctrl, enable);
-	v4l2_ctrl_grab(sensor->hdr_ctrl, enable);
-}
-
-static int vd55g1_enable_streams(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_state *state, u32 pad,
-				 u64 streams_mask)
-{
-	struct vd55g1 *sensor = to_vd55g1(sd);
-	struct v4l2_rect *crop =
-		v4l2_subdev_state_get_crop(state, 0);
-	struct v4l2_mbus_framefmt *format =
-		v4l2_subdev_state_get_format(state, 0);
-	int ret;
-
-	ret = pm_runtime_resume_and_get(sensor->dev);
-	if (ret < 0)
-		return ret;
-
-	/* Configure output */
-	vd55g1_write(sensor, VD55G1_REG_MIPI_DATA_RATE,
-		     sensor->mipi_rate, &ret);
-	vd55g1_write(sensor, VD55G1_REG_OIF_CTRL, sensor->oif_ctrl, &ret);
-	vd55g1_write(sensor, VD55G1_REG_ISL_ENABLE, 0, &ret);
-	if (ret)
-		goto err_rpm_put;
-
-	ret = vd55g1_set_framefmt(sensor, format, crop);
-	if (ret)
-		goto err_rpm_put;
-
-	/* Setup default GPIO values; could be overridden by V4L2 ctrl setup */
-	ret = vd55g1_update_gpios(sensor, GENMASK(VD55G1_NB_GPIOS - 1, 0));
-	if (ret)
-		goto err_rpm_put;
-
-	ret = vd55g1_apply_cold_start(sensor, crop);
-	if (ret)
-		goto err_rpm_put;
-
-	/* Apply settings from V4L2 ctrls */
-	ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
-	if (ret)
-		goto err_rpm_put;
-
-	/* Also apply settings from read-only V4L2 ctrls */
-	ret = vd55g1_ro_ctrls_setup(sensor, crop);
-	if (ret)
-		goto err_rpm_put;
-
-	/* Start streaming */
-	vd55g1_write(sensor, VD55G1_REG_STBY, VD55G1_STBY_START_STREAM, &ret);
-	vd55g1_poll_reg(sensor, VD55G1_REG_STBY, 0, &ret);
-	vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_STREAMING, &ret);
-	if (ret)
-		goto err_rpm_put;
-
-	vd55g1_grab_ctrls(sensor, true);
-
-	return 0;
-
-err_rpm_put:
-	pm_runtime_put(sensor->dev);
-	return -EINVAL;
-}
-
-static int vd55g1_disable_streams(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_state *state, u32 pad,
-				  u64 streams_mask)
-{
-	struct vd55g1 *sensor = to_vd55g1(sd);
-	int ret = 0;
-
-	/* Retrieve Expo cluster to enable coldstart of AE */
-	ret = vd55g1_read_expo_cluster(sensor);
-
-	vd55g1_write(sensor, VD55G1_REG_STREAMING, VD55G1_STREAMING_STOP_STREAM,
-		     &ret);
-	vd55g1_poll_reg(sensor, VD55G1_REG_STREAMING, 0, &ret);
-	vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, &ret);
-
-	if (ret)
-		dev_warn(sensor->dev, "Can't disable stream\n");
-
-	vd55g1_grab_ctrls(sensor, false);
-
-	pm_runtime_put_autosuspend(sensor->dev);
-
-	return ret;
-}
-
-static int vd55g1_patch(struct vd55g1 *sensor)
-{
-	u64 patch;
-	int ret = 0;
-
-	/* vd55g1 needs a patch while vd65g4 does not */
-	if (sensor->id == VD55G1_MODEL_ID_VD55G1) {
-		vd55g1_write_array(sensor, VD55G1_REG_FWPATCH_START_ADDR,
-				   sizeof(vd55g1_patch_array),
-				   vd55g1_patch_array, &ret);
-		vd55g1_write(sensor, VD55G1_REG_BOOT,
-			     VD55G1_BOOT_PATCH_AND_BOOT, &ret);
-		vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret);
-		if (ret) {
-			dev_err(sensor->dev, "Failed to apply patch\n");
-			return ret;
-		}
-
-		vd55g1_read(sensor, VD55G1_REG_FWPATCH_REVISION, &patch, &ret);
-		if (patch != (VD55G1_FWPATCH_REVISION_MAJOR << 8) +
-		    VD55G1_FWPATCH_REVISION_MINOR) {
-			dev_err(sensor->dev, "Bad patch version expected %d.%d got %d.%d\n",
-				VD55G1_FWPATCH_REVISION_MAJOR,
-				VD55G1_FWPATCH_REVISION_MINOR,
-				(u8)(patch >> 8), (u8)(patch & 0xff));
-			return -ENODEV;
-		}
-		dev_dbg(sensor->dev, "patch %d.%d applied\n",
-			(u8)(patch >> 8), (u8)(patch & 0xff));
-
-	} else {
-		vd55g1_write(sensor, VD55G1_REG_BOOT, VD55G1_BOOT_BOOT, &ret);
-		vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret);
-		if (ret) {
-			dev_err(sensor->dev, "Failed to boot\n");
-			return ret;
-		}
-	}
-
-	ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, NULL);
-	if (ret) {
-		dev_err(sensor->dev, "Sensor waiting after boot failed\n");
-		return ret;
-	}
-
-	return 0;
-}
-
-static int vd55g1_get_selection(struct v4l2_subdev *sd,
-				struct v4l2_subdev_state *sd_state,
-				struct v4l2_subdev_selection *sel)
-{
-	const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(sd_state, 0);
-
-	switch (sel->target) {
-	case V4L2_SEL_TGT_CROP:
-		sel->r = *crop;
-		return 0;
-	case V4L2_SEL_TGT_NATIVE_SIZE:
-	case V4L2_SEL_TGT_CROP_DEFAULT:
-	case V4L2_SEL_TGT_CROP_BOUNDS:
-		sel->r.top = 0;
-		sel->r.left = 0;
-		sel->r.width = VD55G1_WIDTH;
-		sel->r.height = VD55G1_HEIGHT;
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
-static int vd55g1_enum_mbus_code(struct v4l2_subdev *sd,
-				 struct v4l2_subdev_state *sd_state,
-				 struct v4l2_subdev_mbus_code_enum *code)
-{
-	struct vd55g1 *sensor = to_vd55g1(sd);
-	u32 base_code;
-
-	if (sensor->id == VD55G1_MODEL_ID_VD55G1) {
-		if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_mono))
-			return -EINVAL;
-		base_code = vd55g1_mbus_formats_mono[code->index];
-	} else {
-		if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_bayer))
-			return -EINVAL;
-		base_code = vd55g1_mbus_formats_bayer[code->index][0];
-	}
-	code->code = vd55g1_get_fmt_code(sensor, base_code);
-
-	return 0;
-}
-
-static int vd55g1_new_format_change_controls(struct vd55g1 *sensor,
-					     struct v4l2_mbus_framefmt *format,
-					     struct v4l2_rect *crop)
-{
-	struct vd55g1_vblank_limits vblank;
-	unsigned int hblank;
-	unsigned int frame_length = 0;
-	unsigned int expo_max;
-	int ret;
-
-	/* Reset vblank and frame length to default */
-	vd55g1_get_vblank_limits(sensor, crop, &vblank);
-	ret = __v4l2_ctrl_modify_range(sensor->vblank_ctrl, vblank.min,
-				       vblank.max, 1, vblank.def);
-	if (ret)
-		return ret;
-
-	/* Max exposure changes with vblank */
-	frame_length = crop->height + sensor->vblank_ctrl->val;
-	expo_max = frame_length - VD55G1_EXPO_MAX_TERM;
-	ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max, 1,
-				       VD55G1_EXPO_DEF);
-	if (ret)
-		return ret;
-
-	/* Update pixel rate to reflect new bpp */
-	ret = __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_ctrl,
-				       vd55g1_get_pixel_rate(sensor, format));
-	if (ret)
-		return ret;
-
-	/* Update hblank according to new width */
-	hblank = vd55g1_get_hblank_min(sensor, format, crop);
-	ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1,
-				       hblank);
-
-	return ret;
-}
-
-static int vd55g1_set_pad_fmt(struct v4l2_subdev *sd,
-			      struct v4l2_subdev_state *sd_state,
-			      struct v4l2_subdev_format *sd_fmt)
-{
-	struct vd55g1 *sensor = to_vd55g1(sd);
-	const struct vd55g1_mode *new_mode;
-	struct v4l2_mbus_framefmt *format;
-	struct v4l2_rect pad_crop;
-	unsigned int binning;
-
-	new_mode = v4l2_find_nearest_size(vd55g1_supported_modes,
-					  ARRAY_SIZE(vd55g1_supported_modes),
-					  width, height, sd_fmt->format.width,
-					  sd_fmt->format.height);
-
-	vd55g1_update_pad_fmt(sensor, new_mode, sd_fmt->format.code,
-			      &sd_fmt->format);
-
-	/*
-	 * Use binning to maximize the crop rectangle size, and centre it in the
-	 * sensor.
-	 */
-	binning = min(VD55G1_WIDTH / sd_fmt->format.width,
-		      VD55G1_HEIGHT / sd_fmt->format.height);
-	binning = min(binning, 2U);
-	pad_crop.width = sd_fmt->format.width * binning;
-	pad_crop.height = sd_fmt->format.height * binning;
-	pad_crop.left = (VD55G1_WIDTH - pad_crop.width) / 2;
-	pad_crop.top = (VD55G1_HEIGHT - pad_crop.height) / 2;
-
-	format = v4l2_subdev_state_get_format(sd_state, sd_fmt->pad);
-
-	*format = sd_fmt->format;
-
-	*v4l2_subdev_state_get_crop(sd_state, sd_fmt->pad) = pad_crop;
-	if (sd_fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
-		return vd55g1_new_format_change_controls(sensor,
-							 &sd_fmt->format,
-							 &pad_crop);
-
-	return 0;
-}
-
-static int vd55g1_init_state(struct v4l2_subdev *sd,
-			     struct v4l2_subdev_state *sd_state)
-{
-	struct vd55g1 *sensor = to_vd55g1(sd);
-	struct v4l2_subdev_format fmt = { 0 };
-	struct v4l2_subdev_route routes[] = {
-		{ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE }
-	};
-	struct v4l2_subdev_krouting routing = {
-	    .num_routes = ARRAY_SIZE(routes),
-	    .routes = routes,
-	};
-	int ret;
-
-	/* Needed by v4l2_subdev_s_stream_helper(), even with 1 stream only */
-	ret = v4l2_subdev_set_routing(sd, sd_state, &routing);
-	if (ret)
-		return ret;
-
-	vd55g1_update_pad_fmt(sensor, &vd55g1_supported_modes[VD55G1_MODE_DEF],
-			      vd55g1_get_fmt_code(sensor, VD55G1_MBUS_CODE_DEF),
-			      &fmt.format);
-
-	return vd55g1_set_pad_fmt(sd, sd_state, &fmt);
-}
-
-static int vd55g1_enum_frame_size(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_state *sd_state,
-				  struct v4l2_subdev_frame_size_enum *fse)
-{
-	struct vd55g1 *sensor = to_vd55g1(sd);
-	u32 code;
-
-	if (fse->index >= ARRAY_SIZE(vd55g1_supported_modes))
-		return -EINVAL;
-
-	code = vd55g1_get_fmt_code(sensor, fse->code);
-	if (fse->code != code)
-		return -EINVAL;
-
-	fse->min_width = vd55g1_supported_modes[fse->index].width;
-	fse->max_width = fse->min_width;
-	fse->min_height = vd55g1_supported_modes[fse->index].height;
-	fse->max_height = fse->min_height;
-
-	return 0;
-}
-
-static const struct v4l2_subdev_internal_ops vd55g1_internal_ops = {
-	.init_state = vd55g1_init_state,
-};
-
-static const struct v4l2_subdev_pad_ops vd55g1_pad_ops = {
-	.enum_mbus_code = vd55g1_enum_mbus_code,
-	.get_fmt = v4l2_subdev_get_fmt,
-	.set_fmt = vd55g1_set_pad_fmt,
-	.get_selection = vd55g1_get_selection,
-	.enum_frame_size = vd55g1_enum_frame_size,
-	.enable_streams = vd55g1_enable_streams,
-	.disable_streams = vd55g1_disable_streams,
-};
-
-static const struct v4l2_subdev_video_ops vd55g1_video_ops = {
-	.s_stream = v4l2_subdev_s_stream_helper,
-};
-
-static const struct v4l2_subdev_ops vd55g1_subdev_ops = {
-	.video = &vd55g1_video_ops,
-	.pad = &vd55g1_pad_ops,
-};
-
-static int vd55g1_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
-	struct vd55g1 *sensor = ctrl_to_vd55g1(ctrl);
-	int ret = 0;
-
-	/* Interact with HW only when it is powered ON */
-	if (!pm_runtime_get_if_in_use(sensor->dev))
-		return 0;
-
-	switch (ctrl->id) {
-	case V4L2_CID_EXPOSURE_AUTO:
-		ret = vd55g1_read_expo_cluster(sensor);
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-
-	pm_runtime_put_autosuspend(sensor->dev);
-
-	return ret;
-}
-
-static int vd55g1_s_ctrl(struct v4l2_ctrl *ctrl)
-{
-	struct vd55g1 *sensor = ctrl_to_vd55g1(ctrl);
-	unsigned int frame_length = 0;
-	unsigned int expo_max;
-	struct v4l2_subdev_state *state =
-		v4l2_subdev_get_locked_active_state(&sensor->sd);
-	struct v4l2_rect *crop =
-		v4l2_subdev_state_get_crop(state, 0);
-	struct v4l2_mbus_framefmt *format =
-		v4l2_subdev_state_get_format(state, 0);
-	unsigned int hblank = vd55g1_get_hblank_min(sensor, format, crop);
-	bool is_auto = false;
-	int ret = 0;
-
-	if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
-		return 0;
-
-	/* Update controls state, range, etc. whatever the state of the HW */
-	switch (ctrl->id) {
-	case V4L2_CID_VBLANK:
-		frame_length = crop->height + ctrl->val;
-		expo_max = frame_length - VD55G1_EXPO_MAX_TERM;
-		ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max,
-					       1, VD55G1_EXPO_DEF);
-		break;
-	case V4L2_CID_EXPOSURE_AUTO:
-		is_auto = (ctrl->val == V4L2_EXPOSURE_AUTO);
-		__v4l2_ctrl_grab(sensor->ae_lock_ctrl, !is_auto);
-		__v4l2_ctrl_grab(sensor->ae_bias_ctrl, !is_auto);
-		break;
-	case V4L2_CID_HDR_SENSOR_MODE:
-		/* Discriminate if the userspace changed the control value */
-		if (ctrl->val != ctrl->cur.val) {
-			/* Max horizontal blanking changes with hdr mode */
-			ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl,
-						       hblank, hblank, 1,
-						       hblank);
-		}
-		break;
-	default:
-		break;
-	}
-
-	/* Don't modify hardware if controls modification failed */
-	if (ret)
-		return ret;
-
-	/* Interact with HW only when it is powered ON */
-	if (!pm_runtime_get_if_in_use(sensor->dev))
-		return 0;
-
-	switch (ctrl->id) {
-	case V4L2_CID_HFLIP:
-		ret = vd55g1_write(sensor, VD55G1_REG_ORIENTATION,
-				   sensor->hflip_ctrl->val |
-					   (sensor->vflip_ctrl->val << 1),
-				   NULL);
-		break;
-	case V4L2_CID_TEST_PATTERN:
-		ret = vd55g1_update_patgen(sensor, ctrl->val);
-		break;
-	case V4L2_CID_EXPOSURE_AUTO:
-		ret = vd55g1_update_expo_cluster(sensor, is_auto);
-		break;
-	case V4L2_CID_3A_LOCK:
-		ret = vd55g1_lock_exposure(sensor, ctrl->val);
-		break;
-	case V4L2_CID_AUTO_EXPOSURE_BIAS:
-		/*
-		 * We use auto exposure target percentage register to control
-		 * exposure bias for more precision.
-		 */
-		ret = vd55g1_update_exposure_target(sensor, ctrl->val);
-		break;
-	case V4L2_CID_VBLANK:
-		ret = vd55g1_update_frame_length(sensor, frame_length);
-		break;
-	case V4L2_CID_FLASH_LED_MODE:
-		ret = vd55g1_update_gpios(sensor, sensor->ext_leds_mask);
-		break;
-	case V4L2_CID_HDR_SENSOR_MODE:
-		ret = vd55g1_update_hdr_mode(sensor);
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-
-	pm_runtime_put_autosuspend(sensor->dev);
-
-	return ret;
-}
-
-static const struct v4l2_ctrl_ops vd55g1_ctrl_ops = {
-	.g_volatile_ctrl = vd55g1_g_volatile_ctrl,
-	.s_ctrl = vd55g1_s_ctrl,
-};
-
-static int vd55g1_init_ctrls(struct vd55g1 *sensor)
-{
-	const struct v4l2_ctrl_ops *ops = &vd55g1_ctrl_ops;
-	struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler;
-	struct v4l2_ctrl *ctrl;
-	struct v4l2_fwnode_device_properties fwnode_props;
-	struct vd55g1_vblank_limits vblank;
-	unsigned int hblank;
-	struct v4l2_subdev_state *state =
-		v4l2_subdev_lock_and_get_active_state(&sensor->sd);
-	struct v4l2_rect *crop =
-		v4l2_subdev_state_get_crop(state, 0);
-	struct v4l2_mbus_framefmt *format =
-		v4l2_subdev_state_get_format(state, 0);
-	s32 pixel_rate = vd55g1_get_pixel_rate(sensor, format);
-	int ret;
-
-	v4l2_ctrl_handler_init(hdl, 16);
-
-	/* Flip cluster */
-	sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
-					       0, 1, 1, 0);
-	if (sensor->hflip_ctrl)
-		sensor->hflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
-	sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
-					       0, 1, 1, 0);
-	if (sensor->vflip_ctrl)
-		sensor->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
-	v4l2_ctrl_cluster(2, &sensor->hflip_ctrl);
-
-	/* Exposition cluster */
-	sensor->ae_ctrl = v4l2_ctrl_new_std_menu(hdl, ops,
-						 V4L2_CID_EXPOSURE_AUTO, 1,
-						 ~0x3, V4L2_EXPOSURE_AUTO);
-	sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
-					       0, 0x1c, 1, VD55G1_AGAIN_DEF);
-	sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
-					       256, 0xffff, 1,
-					       VD55G1_DGAIN_DEF);
-	sensor->expo_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 0,
-					      VD55G1_FRAME_LENGTH_DEF -
-					      VD55G1_EXPO_MAX_TERM,
-					      1, VD55G1_EXPO_DEF);
-	v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl, V4L2_EXPOSURE_MANUAL, true);
-
-	sensor->patgen_ctrl =
-		v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
-					     ARRAY_SIZE(vd55g1_tp_menu) - 1, 0,
-					     0, vd55g1_tp_menu);
-	ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ,
-				      0, 0, &sensor->link_freq);
-	if (ctrl)
-		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-	sensor->pixel_rate_ctrl = v4l2_ctrl_new_std(hdl, ops,
-						    V4L2_CID_PIXEL_RATE, 1,
-						    INT_MAX, 1,
-						    pixel_rate);
-	if (sensor->pixel_rate_ctrl)
-		sensor->pixel_rate_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-	sensor->ae_lock_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK,
-						 0, 1, 0, 0);
-	sensor->ae_bias_ctrl =
-		v4l2_ctrl_new_int_menu(hdl, ops,
-				       V4L2_CID_AUTO_EXPOSURE_BIAS,
-				       ARRAY_SIZE(vd55g1_ev_bias_menu) - 1,
-				       ARRAY_SIZE(vd55g1_ev_bias_menu) / 2,
-				       vd55g1_ev_bias_menu);
-	sensor->hdr_ctrl =
-		v4l2_ctrl_new_std_menu_items(hdl, ops,
-					     V4L2_CID_HDR_SENSOR_MODE,
-					     ARRAY_SIZE(vd55g1_hdr_menu) - 1, 0,
-					     VD55G1_NO_HDR, vd55g1_hdr_menu);
-	hblank = vd55g1_get_hblank_min(sensor, format, crop);
-	sensor->hblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
-						hblank, hblank, 1, hblank);
-	if (sensor->hblank_ctrl)
-		sensor->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-	vd55g1_get_vblank_limits(sensor, crop, &vblank);
-	sensor->vblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
-						vblank.min, vblank.max,
-						1, vblank.def);
-
-	/* Additional controls based on device tree properties */
-	if (sensor->ext_leds_mask) {
-		sensor->led_ctrl =
-			v4l2_ctrl_new_std_menu(hdl, ops,
-					       V4L2_CID_FLASH_LED_MODE,
-					       V4L2_FLASH_LED_MODE_FLASH, 0,
-					       V4L2_FLASH_LED_MODE_NONE);
-	}
-
-	ret = v4l2_fwnode_device_parse(sensor->dev, &fwnode_props);
-	if (ret)
-		goto free_ctrls;
-
-	ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props);
-	if (ret)
-		goto free_ctrls;
-
-	sensor->sd.ctrl_handler = hdl;
-	goto unlock_state;
-
-free_ctrls:
-	v4l2_ctrl_handler_free(hdl);
-unlock_state:
-	v4l2_subdev_unlock_state(state);
-	return ret;
-}
-
-static int vd55g1_detect(struct vd55g1 *sensor)
-{
-	unsigned int dt_id = (uintptr_t)device_get_match_data(sensor->dev);
-	u64 rev, id;
-	int ret;
-
-	ret = vd55g1_read(sensor, VD55G1_REG_MODEL_ID, &id, NULL);
-	if (ret)
-		return ret;
-
-	if (id != VD55G1_MODEL_ID_VD55G1 && id != VD55G1_MODEL_ID_VD65G4) {
-		dev_warn(sensor->dev, "Unsupported sensor id 0x%x\n",
-			 (u32)id);
-		return -ENODEV;
-	}
-	if (id != dt_id) {
-		dev_err(sensor->dev, "Probed sensor %s and device tree definition (%s) mismatch",
-			VD55G1_MODEL_ID_NAME(id), VD55G1_MODEL_ID_NAME(dt_id));
-		return -ENODEV;
-	}
-	sensor->id = id;
-
-	ret = vd55g1_read(sensor, VD55G1_REG_REVISION, &rev, NULL);
-	if (ret)
-		return ret;
-
-	if ((id == VD55G1_MODEL_ID_VD55G1 && rev != VD55G1_REVISION_CCB) &&
-	    (id == VD55G1_MODEL_ID_VD65G4 && rev != VD55G1_REVISION_BAYER)) {
-		dev_err(sensor->dev, "Unsupported sensor revision 0x%x for sensor %s\n",
-			(u16)rev, VD55G1_MODEL_ID_NAME(id));
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
-static int vd55g1_power_on(struct device *dev)
-{
-	struct v4l2_subdev *sd = dev_get_drvdata(dev);
-	struct vd55g1 *sensor = to_vd55g1(sd);
-	int ret;
-
-	ret = regulator_bulk_enable(ARRAY_SIZE(vd55g1_supply_name),
-				    sensor->supplies);
-	if (ret) {
-		dev_err(dev, "Failed to enable regulators %d\n", ret);
-		return ret;
-	}
-
-	ret = clk_prepare_enable(sensor->xclk);
-	if (ret) {
-		dev_err(dev, "Failed to enable clock %d\n", ret);
-		goto disable_bulk;
-	}
-
-	gpiod_set_value_cansleep(sensor->reset_gpio, 0);
-	usleep_range(5000, 10000);
-	ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_READY_TO_BOOT, NULL);
-	if (ret) {
-		dev_err(dev, "Sensor reset failed %d\n", ret);
-		goto disable_clock;
-	}
-
-	ret = vd55g1_detect(sensor);
-	if (ret) {
-		dev_err(dev, "Sensor detect failed %d\n", ret);
-		goto disable_clock;
-	}
-
-	/* Setup clock now to advance through system FSM states */
-	vd55g1_write(sensor, VD55G1_REG_EXT_CLOCK, sensor->xclk_freq, &ret);
-
-	ret = vd55g1_patch(sensor);
-	if (ret) {
-		dev_err(dev, "Sensor patch failed %d\n", ret);
-		goto disable_clock;
-	}
-
-	return 0;
-
-disable_clock:
-	gpiod_set_value_cansleep(sensor->reset_gpio, 1);
-	clk_disable_unprepare(sensor->xclk);
-disable_bulk:
-	regulator_bulk_disable(ARRAY_SIZE(vd55g1_supply_name),
-			       sensor->supplies);
-
-	return ret;
-}
-
-static int vd55g1_power_off(struct device *dev)
-{
-	struct v4l2_subdev *sd = dev_get_drvdata(dev);
-	struct vd55g1 *sensor = to_vd55g1(sd);
-
-	gpiod_set_value_cansleep(sensor->reset_gpio, 1);
-	clk_disable_unprepare(sensor->xclk);
-	regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
-
-	return 0;
-}
-
-static int vd55g1_check_csi_conf(struct vd55g1 *sensor,
-				 struct fwnode_handle *endpoint)
-{
-	struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
-	u8 n_lanes;
-	int ret;
-
-	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
-	if (ret)
-		return -EINVAL;
-
-	/* Check lanes number */
-	n_lanes = ep.bus.mipi_csi2.num_data_lanes;
-	if (n_lanes != 1) {
-		dev_err(sensor->dev, "Sensor only supports 1 lane, found %d\n",
-			n_lanes);
-		ret = -EINVAL;
-		goto done;
-	}
-
-	/* Clock lane must be first */
-	if (ep.bus.mipi_csi2.clock_lane != 0) {
-		dev_err(sensor->dev, "Clock lane must be mapped to lane 0\n");
-		ret = -EINVAL;
-		goto done;
-	}
-
-	/* Handle polarities in sensor configuration */
-	sensor->oif_ctrl = (ep.bus.mipi_csi2.lane_polarities[0] << 3) |
-			   (ep.bus.mipi_csi2.lane_polarities[1] << 6);
-
-	/* Check the link frequency set in device tree */
-	if (!ep.nr_of_link_frequencies) {
-		dev_err(sensor->dev, "link-frequency property not found in DT\n");
-		ret = -EINVAL;
-		goto done;
-	}
-	if (ep.nr_of_link_frequencies != 1) {
-		dev_err(sensor->dev, "Multiple link frequencies not supported\n");
-		ret = -EINVAL;
-		goto done;
-	}
-	sensor->link_freq = ep.link_frequencies[0];
-
-done:
-	v4l2_fwnode_endpoint_free(&ep);
-
-	return ret;
-}
-
-static int vd55g1_parse_dt_gpios_array(struct vd55g1 *sensor,
-				       char *prop_name, u32 *array, int *nb)
-{
-	unsigned int i;
-	int ret;
-
-	*nb = device_property_count_u32(sensor->dev, prop_name);
-	if (*nb == -EINVAL) {
-		/* Property not found */
-		*nb = 0;
-		return 0;
-	}
-
-	ret = device_property_read_u32_array(sensor->dev,
-					     prop_name, array, *nb);
-	if (ret) {
-		dev_err(sensor->dev, "Failed to read %s prop\n", prop_name);
-		return ret;
-	}
-	for (i = 0; i < *nb;  i++) {
-		if (array[i] >= VD55G1_NB_GPIOS) {
-			dev_err(sensor->dev, "Invalid GPIO number %d\n",
-				array[i]);
-			return -EINVAL;
-		}
-	}
-
-	return 0;
-}
-
-static int vd55g1_parse_dt_gpios(struct vd55g1 *sensor)
-{
-	u32 led_gpios[VD55G1_NB_GPIOS];
-	int nb_gpios_leds;
-	unsigned int i;
-	int ret;
-
-	/* Initialize GPIOs to default */
-	for (i = 0; i < VD55G1_NB_GPIOS; i++)
-		sensor->gpios[i] = VD55G1_GPIO_MODE_IN;
-	sensor->ext_leds_mask = 0;
-
-	/* Take into account optional 'st,leds' output for GPIOs */
-	ret = vd55g1_parse_dt_gpios_array(sensor, "st,leds", led_gpios,
-					  &nb_gpios_leds);
-	if (ret)
-		return ret;
-
-	for (i = 0; i < nb_gpios_leds; i++) {
-		sensor->gpios[led_gpios[i]] = VD55G1_GPIO_MODE_STROBE;
-		set_bit(led_gpios[i], &sensor->ext_leds_mask);
-	}
-
-	return 0;
-}
-
-static int vd55g1_parse_dt(struct vd55g1 *sensor)
-{
-	struct fwnode_handle *endpoint;
-	int ret;
-
-	endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(sensor->dev),
-						   0, 0, 0);
-	if (!endpoint) {
-		dev_err(sensor->dev, "Endpoint node not found\n");
-		return -EINVAL;
-	}
-
-	ret = vd55g1_check_csi_conf(sensor, endpoint);
-	fwnode_handle_put(endpoint);
-	if (ret)
-		return ret;
-
-	return vd55g1_parse_dt_gpios(sensor);
-}
-
-static int vd55g1_subdev_init(struct vd55g1 *sensor)
-{
-	int ret;
-
-	/* Init sub device */
-	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-	sensor->sd.internal_ops = &vd55g1_internal_ops;
-
-	/* Init source pad */
-	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
-	sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
-	ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
-	if (ret) {
-		dev_err(sensor->dev, "Failed to init media entity: %d\n", ret);
-		return ret;
-	}
-
-	sensor->sd.state_lock = sensor->ctrl_handler.lock;
-	ret = v4l2_subdev_init_finalize(&sensor->sd);
-	if (ret) {
-		dev_err(sensor->dev, "Subdev init error: %d\n", ret);
-		goto err_ctrls;
-	}
-
-	/*
-	 * Initialize controls after v4l2_subdev_init_finalize() to make sure
-	 * active state is set
-	 */
-	ret = vd55g1_init_ctrls(sensor);
-	if (ret) {
-		dev_err(sensor->dev, "Controls initialization failed %d\n",
-			ret);
-		goto err_media;
-	}
-
-	return 0;
-
-err_ctrls:
-	v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
-
-err_media:
-	media_entity_cleanup(&sensor->sd.entity);
-	return ret;
-}
-
-static void vd55g1_subdev_cleanup(struct vd55g1 *sensor)
-{
-	v4l2_async_unregister_subdev(&sensor->sd);
-	v4l2_subdev_cleanup(&sensor->sd);
-	media_entity_cleanup(&sensor->sd.entity);
-	v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
-}
-
-static int vd55g1_get_regulators(struct vd55g1 *sensor)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(vd55g1_supply_name); i++)
-		sensor->supplies[i].supply = vd55g1_supply_name[i];
-
-	return devm_regulator_bulk_get(sensor->dev,
-				       ARRAY_SIZE(vd55g1_supply_name),
-				       sensor->supplies);
-}
-
-static int vd55g1_probe(struct i2c_client *client)
-{
-	struct device *dev = &client->dev;
-	struct vd55g1 *sensor;
-	int ret;
-
-	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
-	if (!sensor)
-		return -ENOMEM;
-	sensor->dev = &client->dev;
-
-	v4l2_i2c_subdev_init(&sensor->sd, client, &vd55g1_subdev_ops);
-
-	ret = vd55g1_parse_dt(sensor);
-	if (ret)
-		return dev_err_probe(dev, ret, "Failed to parse Device Tree\n");
-
-	/* Get (and check) resources : power regs, ext clock, reset gpio */
-	ret = vd55g1_get_regulators(sensor);
-	if (ret)
-		return dev_err_probe(dev, ret, "Failed to get regulators\n");
-
-	sensor->xclk = devm_v4l2_sensor_clk_get(dev, NULL);
-	if (IS_ERR(sensor->xclk))
-		return dev_err_probe(dev, PTR_ERR(sensor->xclk),
-				     "Failed to get xclk\n");
-
-	sensor->xclk_freq = clk_get_rate(sensor->xclk);
-	ret = vd55g1_prepare_clock_tree(sensor);
-	if (ret)
-		return ret;
-
-	sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
-						     GPIOD_OUT_HIGH);
-	if (IS_ERR(sensor->reset_gpio))
-		return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
-				     "Failed to get reset gpio\n");
-
-	sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
-	if (IS_ERR(sensor->regmap))
-		return dev_err_probe(dev, PTR_ERR(sensor->regmap),
-				     "Failed to init regmap\n");
-
-	/* Detect if sensor is present and if its revision is supported */
-	ret = vd55g1_power_on(dev);
-	if (ret)
-		return ret;
-
-	/* Enable pm_runtime and power off the sensor */
-	pm_runtime_set_active(dev);
-	pm_runtime_get_noresume(dev);
-	pm_runtime_enable(dev);
-	pm_runtime_set_autosuspend_delay(dev, 4000);
-	pm_runtime_use_autosuspend(dev);
-	pm_runtime_put_autosuspend(dev);
-
-	ret = vd55g1_subdev_init(sensor);
-	if (ret) {
-		dev_err(dev, "V4l2 init failed: %d\n", ret);
-		goto err_power_off;
-	}
-
-	ret = v4l2_async_register_subdev(&sensor->sd);
-	if (ret) {
-		dev_err(dev, "async subdev register failed %d\n", ret);
-		goto err_subdev;
-	}
-
-	return 0;
-
-err_subdev:
-	vd55g1_subdev_cleanup(sensor);
-err_power_off:
-	pm_runtime_disable(dev);
-	pm_runtime_put_noidle(dev);
-	pm_runtime_dont_use_autosuspend(dev);
-	vd55g1_power_off(dev);
-
-	return ret;
-}
-
-static void vd55g1_remove(struct i2c_client *client)
-{
-	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	struct vd55g1 *sensor = to_vd55g1(sd);
-
-	vd55g1_subdev_cleanup(sensor);
-
-	pm_runtime_disable(&client->dev);
-	if (!pm_runtime_status_suspended(&client->dev))
-		vd55g1_power_off(&client->dev);
-	pm_runtime_set_suspended(&client->dev);
-	pm_runtime_dont_use_autosuspend(&client->dev);
-}
-
-static const struct of_device_id vd55g1_dt_ids[] = {
-	{ .compatible = "st,vd55g1", .data = (void *)VD55G1_MODEL_ID_VD55G1 },
-	{ .compatible = "st,vd65g4", .data = (void *)VD55G1_MODEL_ID_VD65G4 },
-	{ /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, vd55g1_dt_ids);
-
-static const struct dev_pm_ops vd55g1_pm_ops = {
-	SET_RUNTIME_PM_OPS(vd55g1_power_off, vd55g1_power_on, NULL)
-};
-
-static struct i2c_driver vd55g1_i2c_driver = {
-	.driver = {
-		.name  = "vd55g1",
-		.of_match_table = vd55g1_dt_ids,
-		.pm = &vd55g1_pm_ops,
-	},
-	.probe = vd55g1_probe,
-	.remove = vd55g1_remove,
-};
-
-module_i2c_driver(vd55g1_i2c_driver);
-
-MODULE_AUTHOR("Benjamin Mugnier <benjamin.mugnier@foss.st.com>");
-MODULE_AUTHOR("Sylvain Petinot <sylvain.petinot@foss.st.com>");
-MODULE_DESCRIPTION("VD55G1 camera subdev driver");
-MODULE_LICENSE("GPL");
-- 
2.55.0


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

* [PATCH 7/7] MAINTAINERS: Update ST VD55G camera driver entry
  2026-09-02 20:45 [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Peter Marshall
                   ` (5 preceding siblings ...)
  2026-09-02 20:45 ` [PATCH 6/7] media: i2c: st-vd55g: Remove legacy vd55g1 remnants Peter Marshall
@ 2026-09-02 20:45 ` Peter Marshall
  2026-09-03 13:43   ` Krzysztof Kozlowski
  2026-09-04 12:06 ` [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Benjamin Mugnier
  7 siblings, 1 reply; 14+ messages in thread
From: Peter Marshall @ 2026-09-02 20:45 UTC (permalink / raw)
  To: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot
  Cc: linux-kernel, devicetree, linux-media, platform-driver-x86,
	Peter Marshall

Point to the updated vd55g sources and documentation, and add myself as
a maintainer.

Signed-off-by: Peter Marshall <pm@petermarshall.ca>
---
 MAINTAINERS | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 5114e6db7..b090e6318 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25649,13 +25649,14 @@ F:	Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml
 F:	Documentation/hwmon/tsc1641.rst
 F:	drivers/hwmon/tsc1641.c
 
-ST VD55G1 DRIVER
+ST VD55G DRIVER
 M:	Benjamin Mugnier <benjamin.mugnier@foss.st.com>
 M:	Sylvain Petinot <sylvain.petinot@foss.st.com>
+M:	Peter Marshall <pm@petermarshall.ca>
 L:	linux-media@vger.kernel.org
 S:	Maintained
-F:	Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
-F:	drivers/media/i2c/vd55g1.c
+F:	Documentation/devicetree/bindings/media/i2c/st,vd55g.yaml
+F:	drivers/media/i2c/vd55g.c
 
 ST VD56G3 IMAGE SENSOR DRIVER
 M:	Benjamin Mugnier <benjamin.mugnier@foss.st.com>
-- 
2.55.0


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

* Re: [PATCH 2/7] dt-bindings: media: i2c: st,vd55g: rename and add VD55G0 variant
  2026-09-02 20:45 ` [PATCH 2/7] dt-bindings: media: i2c: st,vd55g: rename and add VD55G0 variant Peter Marshall
@ 2026-09-03 13:36   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 14+ messages in thread
From: Krzysztof Kozlowski @ 2026-09-03 13:36 UTC (permalink / raw)
  To: Peter Marshall
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot, linux-kernel, devicetree, linux-media,
	platform-driver-x86, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley

On Wed, Sep 02, 2026 at 04:45:41PM -0400, Peter Marshall wrote:
> Document the STMicroelectronics VD55G0 camera sensor. Rename the device
> tree bindings to represent a generic VD55G family sensor.

Renaming is not really needed. The binding should be named after
fallback or one of compatibles, usually.

> 
> Signed-off-by: Peter Marshall <pm@petermarshall.ca>
> ---
>  .../i2c/{st,vd55g1.yaml => st,vd55g.yaml}     | 30 +++++++++++++++----
>  1 file changed, 24 insertions(+), 6 deletions(-)
>  rename Documentation/devicetree/bindings/media/i2c/{st,vd55g1.yaml => st,vd55g.yaml} (76%)
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g.yaml
> similarity index 76%
> rename from Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
> rename to Documentation/devicetree/bindings/media/i2c/st,vd55g.yaml
> index 060ac6829..bd6feeb6c 100644
> --- a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
> +++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g.yaml
> @@ -2,19 +2,21 @@
>  # Copyright (c) 2025 STMicroelectronics SA.
>  %YAML 1.2
>  ---
> -$id: http://devicetree.org/schemas/media/i2c/st,vd55g1.yaml#
> +$id: http://devicetree.org/schemas/media/i2c/st,vd55g.yaml#
>  $schema: http://devicetree.org/meta-schemas/core.yaml#
>  
> -title: STMicroelectronics VD55G1 Global Shutter Image Sensor
> +title: STMicroelectronics VD55G Family Global Shutter Image Sensors
>  
>  maintainers:
>    - Benjamin Mugnier <benjamin.mugnier@foss.st.com>
>    - Sylvain Petinot <sylvain.petinot@foss.st.com>
> +  - Peter Marshall <pm@petermarshall.ca>
>  
>  description: |-
> - The STMicroelectronics VD55G1 is a global shutter image sensor with an active
> - array size of 804H x 704V. It is programmable through I2C interface. The I2C
> - address is fixed to 0x10.
> + The STMicroelectronics VD55G family (VD55G0, VD55G1, and VD65G4) consists of
> + global shutter image sensors. The VD55G0 has an array size of 644H x 604V.
> + The VD55G1 and VD65G4 have an array size of 804H x 704V. They are programmable
> + through I2C interface. The I2C address is fixed to 0x10.
>  
>   Image data is sent through MIPI CSI-2, which is configured as only 1 data
>   lane. The sensor provides 4 GPIOS that can be used for external LED signal
> @@ -23,13 +25,29 @@ description: |-
>  allOf:
>    - $ref: /schemas/media/video-interface-devices.yaml#
>  

Please move entire allOf to the bottom, before
additional/unevaluatedProperties, like in example-schema.

> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: st,vd55g0
> +    then:
> +      properties:
> +        port:
> +          properties:
> +            endpoint:
> +              properties:
> +                link-frequencies:
> +                  items:
> +                    minimum: 375000000

Best regards,
Krzysztof


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

* Re: [PATCH 1/7] media: i2c: vd55g: move patch array to a separate firmware header
  2026-09-02 20:45 ` [PATCH 1/7] media: i2c: vd55g: move patch array to a separate firmware header Peter Marshall
@ 2026-09-03 13:37   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 14+ messages in thread
From: Krzysztof Kozlowski @ 2026-09-03 13:37 UTC (permalink / raw)
  To: Peter Marshall
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot, linux-kernel, devicetree, linux-media,
	platform-driver-x86

On Wed, Sep 02, 2026 at 04:45:40PM -0400, Peter Marshall wrote:
> A firmware patch consisting of an array of bytes is uploaded to the chip
> during its boot sequence. Declutter the driver source by moving it to a
> separate header file.
> 
> Signed-off-by: Peter Marshall <pm@petermarshall.ca>
> ---
>  drivers/media/i2c/vd55g-fw.h | 305 +++++++++++++++++++++++++++++++++++
>  drivers/media/i2c/vd55g1.c   | 296 +--------------------------------
>  2 files changed, 306 insertions(+), 295 deletions(-)
>  create mode 100644 drivers/media/i2c/vd55g-fw.h
> 
> diff --git a/drivers/media/i2c/vd55g-fw.h b/drivers/media/i2c/vd55g-fw.h
> new file mode 100644
> index 000000000..02faed7e4
> --- /dev/null
> +++ b/drivers/media/i2c/vd55g-fw.h
> @@ -0,0 +1,305 @@
> +/* SPDX-License-Identifier: GPL-2.0
> + *
> + * Driver for VD55G1 global shutter sensor family driver
> + *
> + * Copyright (C) 2025 STMicroelectronics SA
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +
> +static const u8 vd55g1_patch_array[] = {

Headers should have data, because this can easily lead to duplicated
objects.

static arrays go to C units.

Best regards,
Krzysztof


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

* Re: [PATCH 5/7] media: i2c: st-vd55g: Add generic driver and VD55G0 support
  2026-09-02 20:45 ` [PATCH 5/7] media: i2c: st-vd55g: Add generic driver and VD55G0 support Peter Marshall
@ 2026-09-03 13:41   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 14+ messages in thread
From: Krzysztof Kozlowski @ 2026-09-03 13:41 UTC (permalink / raw)
  To: Peter Marshall
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot, linux-kernel, devicetree, linux-media,
	platform-driver-x86

On Wed, Sep 02, 2026 at 04:45:44PM -0400, Peter Marshall wrote:
> +static int vd55g_probe(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct vd55g *sensor;
> +	int ret;
> +
> +	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
> +	if (!sensor)
> +		return -ENOMEM;
> +	sensor->dev = &client->dev;
> +
> +	v4l2_i2c_subdev_init(&sensor->sd, client, &vd55g_subdev_ops);
> +
> +	ret = vd55g_parse_dt(sensor);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to parse Device Tree\n");

Isn't vd55g_parse_dt() printing errors already? How many times same error
should be printed?

> +
> +	/* Get (and check) resources : power regs, ext clock, reset gpio */
> +	ret = vd55g_get_regulators(sensor);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to get regulators\n");
> +
> +	sensor->xclk = devm_v4l2_sensor_clk_get(dev, NULL);
> +	if (IS_ERR(sensor->xclk))
> +		return dev_err_probe(dev, PTR_ERR(sensor->xclk),
> +				     "Failed to get xclk\n");
> +
> +	sensor->xclk_freq = clk_get_rate(sensor->xclk);
> +
> +	sensor->reset_gpio =
> +		devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
> +	if (IS_ERR(sensor->reset_gpio))
> +		return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
> +				     "Failed to get reset gpio\n");
> +
> +	sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
> +	if (IS_ERR(sensor->regmap))
> +		return dev_err_probe(dev, PTR_ERR(sensor->regmap),
> +				     "Failed to init regmap\n");
> +
> +	/* Detect if sensor is present and if its revision is supported */
> +	ret = vd55g_power_on(dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Enable pm_runtime and power off the sensor */
> +	pm_runtime_set_active(dev);
> +	pm_runtime_get_noresume(dev);
> +	pm_runtime_enable(dev);
> +	pm_runtime_set_autosuspend_delay(dev, 4000);
> +	pm_runtime_use_autosuspend(dev);
> +	pm_runtime_put_autosuspend(dev);
> +
> +	ret = vd55g_subdev_init(sensor);
> +	if (ret) {
> +		dev_err(dev, "V4l2 init failed: %d\n", ret);
> +		goto err_power_off;
> +	}
> +
> +	ret = v4l2_async_register_subdev(&sensor->sd);
> +	if (ret) {
> +		dev_err(dev, "async subdev register failed %d\n", ret);
> +		goto err_subdev;
> +	}
> +
> +	return 0;
> +
> +err_subdev:
> +	vd55g_subdev_cleanup(sensor);
> +err_power_off:
> +	pm_runtime_disable(dev);
> +	pm_runtime_put_noidle(dev);
> +	pm_runtime_dont_use_autosuspend(dev);
> +	vd55g_power_off(dev);
> +
> +	return ret;
> +}
> +
> +static void vd55g_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct vd55g *sensor = to_vd55g(sd);
> +
> +	vd55g_subdev_cleanup(sensor);
> +
> +	pm_runtime_disable(&client->dev);
> +	if (!pm_runtime_status_suspended(&client->dev))
> +		vd55g_power_off(&client->dev);
> +	pm_runtime_set_suspended(&client->dev);
> +	pm_runtime_dont_use_autosuspend(&client->dev);
> +}
> +
> +static const struct acpi_device_id vd55g_acpi_match[] = {
> +	{ "SMO55F0", (kernel_ulong_t)&vd55g0_chip_info },

Use named initializers, like in the OF table.

> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(acpi, vd55g_acpi_match);
> +
> +static const struct of_device_id vd55g_dt_ids[] = {
> +	{ .compatible = "st,vd55g0", .data = &vd55g0_chip_info },
> +	{ .compatible = "st,vd55g1", .data = &vd55g1_chip_info },
> +	{ .compatible = "st,vd65g4", .data = &vd65g4_chip_info },
> +	{ /* sentinel */ }

Best regards,
Krzysztof


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

* Re: [PATCH 7/7] MAINTAINERS: Update ST VD55G camera driver entry
  2026-09-02 20:45 ` [PATCH 7/7] MAINTAINERS: Update ST VD55G camera driver entry Peter Marshall
@ 2026-09-03 13:43   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 14+ messages in thread
From: Krzysztof Kozlowski @ 2026-09-03 13:43 UTC (permalink / raw)
  To: Peter Marshall
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot, linux-kernel, devicetree, linux-media,
	platform-driver-x86

On Wed, Sep 02, 2026 at 04:45:46PM -0400, Peter Marshall wrote:
> Point to the updated vd55g sources and documentation, and add myself as
> a maintainer.
> 
> Signed-off-by: Peter Marshall <pm@petermarshall.ca>
> ---
>  MAINTAINERS | 7 ++++---
>  1 file changed, 4 insertions(+), 3 deletions(-)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5114e6db7..b090e6318 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -25649,13 +25649,14 @@ F:	Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml
>  F:	Documentation/hwmon/tsc1641.rst
>  F:	drivers/hwmon/tsc1641.c
>  
> -ST VD55G1 DRIVER
> +ST VD55G DRIVER
>  M:	Benjamin Mugnier <benjamin.mugnier@foss.st.com>
>  M:	Sylvain Petinot <sylvain.petinot@foss.st.com>
> +M:	Peter Marshall <pm@petermarshall.ca>
>  L:	linux-media@vger.kernel.org
>  S:	Maintained
> -F:	Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
> -F:	drivers/media/i2c/vd55g1.c

Your patches are completely unbisectable... you already removed these
files so previous commits leave stale/wrong paths.

Removal of a file is only complete if you update all the paths. It is
not a separate logical action from updating paths.

Best regards,
Krzysztof


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

* Re: [PATCH 6/7] media: i2c: st-vd55g: Remove legacy vd55g1 remnants
  2026-09-02 20:45 ` [PATCH 6/7] media: i2c: st-vd55g: Remove legacy vd55g1 remnants Peter Marshall
@ 2026-09-03 13:43   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 14+ messages in thread
From: Krzysztof Kozlowski @ 2026-09-03 13:43 UTC (permalink / raw)
  To: Peter Marshall
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Benjamin Mugnier,
	Sylvain Petinot, linux-kernel, devicetree, linux-media,
	platform-driver-x86

On Wed, Sep 02, 2026 at 04:45:45PM -0400, Peter Marshall wrote:
> vd55g1 is superseded by the generic vd55g driver supporting the same
> hardware and feature set. Remove it.

Why exactly this cannot be squashed to previous patch... or rather why
this driver cannot grow to support your new hardware, instead of adding
a duplicate one and then removing old driver?

Best regards,
Krzysztof


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

* Re: [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support
  2026-09-02 20:45 [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Peter Marshall
                   ` (6 preceding siblings ...)
  2026-09-02 20:45 ` [PATCH 7/7] MAINTAINERS: Update ST VD55G camera driver entry Peter Marshall
@ 2026-09-04 12:06 ` Benjamin Mugnier
  7 siblings, 0 replies; 14+ messages in thread
From: Benjamin Mugnier @ 2026-09-04 12:06 UTC (permalink / raw)
  To: Peter Marshall, Sakari Ailus, Mauro Carvalho Chehab,
	Sylvain Petinot
  Cc: linux-kernel, devicetree, linux-media, platform-driver-x86,
	Conor Dooley, Daniel Scally, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Rob Herring

Hi Peter,

Le 02/09/2026 à 22:45, Peter Marshall a écrit :
> This patch series refactors the STMicroelectronics VD55G1 and VD65G4 image
> sensor driver (vd55g1) into a generic VD55G family driver (vd55g) and adds
> support for the VD55G0 monochrome sensor.
> 
> The VD55G0 sensor is found in devices such as the Microsoft Surface Pro 9 under
> ACPI HID `SMO55F0`. It features a native resolution of 644 x 604 pixels and
> differs from later variants in the family in several ways:
> 
> * It uses a different native resolution and register addresses mapping,
>   requiring specification of generational feature differences.
> * It lacks support for spatial and temporal HDR.
> * It requires an exposure duty cycle limit to keep the illuminator current safe.
> 
> Tested on a Microsoft Surface Pro 9 (an Intel IPU6 platform with ACPI
> HID "SMO55F0") running libcamera.

I'm sorry I haven't read through your whole serie yet.

First thing first what is the point in refactoring both drivers into the
same one ? I guess it should be addressed in your commit messages. To be
fair it even looks to add complexity because even if the sensors share
some IPs you might have notice they are in fact very different. I could
even argue that the vd56g3 is closer to the vd55g1 than the vd55g0 is.

Plus having your commits merged will also require changes in libcamera.

Otherwise I'm interested in your patches 3 and 4. If I read it correctly
they allow the Microsoft Surface Pro 9 to use the embedded vd55g0 right ?

> 
> Peter Marshall (7):
>   media: i2c: vd55g: move patch array to a separate firmware header
>   dt-bindings: media: i2c: st,vd55g: rename and add VD55G0 variant
>   media: ipu-bridge: Add VD55G0 to the list of supported sensors
>   platform/x86: int3472: Add VD55G0 supply GPIO mapping
>   media: i2c: st-vd55g: Add generic driver and VD55G0 support
>   media: i2c: st-vd55g: Remove legacy vd55g1 remnants
>   MAINTAINERS: Update ST VD55G camera driver entry
> 
>  .../i2c/{st,vd55g1.yaml => st,vd55g.yaml}     |   30 +-
>  MAINTAINERS                                   |    7 +-
>  drivers/media/i2c/Kconfig                     |   11 +
>  drivers/media/i2c/Makefile                    |    2 +-
>  drivers/media/i2c/vd55g-fw.h                  |  305 +++
>  drivers/media/i2c/vd55g.c                     | 2166 +++++++++++++++++
>  drivers/media/i2c/vd55g1.c                    | 2061 ----------------
>  drivers/media/pci/intel/ipu-bridge.c          |    2 +
>  drivers/platform/x86/intel/int3472/discrete.c |   18 +
>  9 files changed, 2531 insertions(+), 2071 deletions(-)
>  rename Documentation/devicetree/bindings/media/i2c/{st,vd55g1.yaml => st,vd55g.yaml} (76%)
>  create mode 100644 drivers/media/i2c/vd55g-fw.h
>  create mode 100644 drivers/media/i2c/vd55g.c
>  delete mode 100644 drivers/media/i2c/vd55g1.c
> 

-- 
Regards,
Benjamin


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

end of thread, other threads:[~2026-09-04 12:06 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 20:45 [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Peter Marshall
2026-09-02 20:45 ` [PATCH 1/7] media: i2c: vd55g: move patch array to a separate firmware header Peter Marshall
2026-09-03 13:37   ` Krzysztof Kozlowski
2026-09-02 20:45 ` [PATCH 2/7] dt-bindings: media: i2c: st,vd55g: rename and add VD55G0 variant Peter Marshall
2026-09-03 13:36   ` Krzysztof Kozlowski
2026-09-02 20:45 ` [PATCH 3/7] media: ipu-bridge: Add VD55G0 to the list of supported sensors Peter Marshall
2026-09-02 20:45 ` [PATCH 4/7] platform/x86: int3472: Add VD55G0 supply GPIO mapping Peter Marshall
2026-09-02 20:45 ` [PATCH 5/7] media: i2c: st-vd55g: Add generic driver and VD55G0 support Peter Marshall
2026-09-03 13:41   ` Krzysztof Kozlowski
2026-09-02 20:45 ` [PATCH 6/7] media: i2c: st-vd55g: Remove legacy vd55g1 remnants Peter Marshall
2026-09-03 13:43   ` Krzysztof Kozlowski
2026-09-02 20:45 ` [PATCH 7/7] MAINTAINERS: Update ST VD55G camera driver entry Peter Marshall
2026-09-03 13:43   ` Krzysztof Kozlowski
2026-09-04 12:06 ` [PATCH 0/7] media: i2c: st-vd55g1: Genericize driver and add VD55G0 support Benjamin Mugnier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox