Skip to contentSkip to conversation

media: Add Himax HM1092 mono NIR sensor driver

PATCH New Ramshouriesh R 2 participants 5 messages Oct 25

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

v8 cover media: Add Himax HM1092 mono NIR sensor driver

This adds a V4L2 subdev driver and DT binding for the Himax HM1092, a 1 megapixel monochrome near-infrared image sensor. On laptops it sits behind the IR camera used for face unlock. It speaks a single MIPI CSI-2 data lane and outputs 10-bit RAW.

The driver exposes one fixed mode, test patterns, exposure, analogue gain and digital gain controls, pixel-array selection targets and the standard fwnode properties. The mode is selected by the external clock rate, since each known platform pairs its clock with its own PLL setup and init sequence:

24 MHz EXTCLK (Qualcomm, devicetree): 560x360, 180 MHz link frequency 19.2 MHz EXTCLK (Intel IPU, ACPI HIMX1092): 648x368, 180.48 MHz

Both run the single lane at about 360 Mbit/s and deliver about 30 frames per second.

Patch 2 adds the driver with the 24 MHz configuration. It has been tested on an ASUS Zenbook A14: the sensor probes, streams, changes brightness across the full exposure and analogue gain ranges, accepts digital gain updates while streaming, and returns to runtime suspend after capture. Its register programming was extracted from com.qti.sensormodule.hm1092.bin in the Qualcomm camera stack shipped for that system. Jake Steinman independently identified the exposure, analogue gain, digital gain and group-hold registers from Dell's hm1092.sys. The available documentation does not identify the test-pattern modes, so their menu names are based on output observed during hardware testing.

Patch 3, from Jake Steinman, adds ACPI enumeration and the 19.2 MHz configuration used on Intel IPU6/IPU7 laptops. It was posted on top of v6 and is carried here at his suggestion. It has been tested on a Dell XPS 16 (IPU 7.5, with the ipu6 driver) and a Dell Latitude 7350 (IPU6).

The 19.2 MHz configuration reads out (0,0)/1294x734, which is what the vendor sequence programs and includes the dummy border. The crop bounds therefore cover the 1296x736 native array, while the default crop stays on the 1280x720 active array.

The ipu-bridge entry for HM1092 was applied separately to media/next as commit 4fdb0342f05e ("media: ipu-bridge: Add Himax HM1092 IR sensor"). The board-level device tree and PHY work that wires this camera up on the ASUS Zenbook A14 will be sent as its own series.

---
Changes in v8:
- hm1092: report the 1296x736 native array as the crop bounds, keeping
the 1280x720 active array as the default crop, so that the 19.2 MHz readout window lies within the bounds (suggested by Jake Steinman
and Ray Strode).
- Cover: drop the staging/ipu7 format note; with the ipu6 driver in media/next the sensor streams on IPU 7.5 without it (Jake Steinman).
- Link to v7: https://patch.msgid.link/[email protected]

Changes in v7:
- Rebase onto next-20261012.
- hm1092: follow the sub-device pad op API change that added a
struct v4l2_subdev_client_info argument: take it in get_selection,
and drop .set_fmt, which only mirrored v4l2_subdev_get_fmt(), as done
for the other fixed-format drivers.
- hm1092: limit the digital gain control to 0x3ff. The register is a
10-bit 2.8 fixed-point value and the sensor ignores higher bits, so
1024 and above wrapped back to black frames (reported by Eduard Kulyk).
- hm1092: handle negative pm_runtime_get_if_in_use() returns without touching unpowered hardware or dropping an unacquired runtime PM reference.
- hm1092: credit Jake Steinman as a co-developer of the control support
and carry the requested coding-assistant disclosure.
- New patch 3 from Jake Steinman: ACPI enumeration and the 19.2 MHz configuration, with Eduard Kulyk's Tested-by. Drop its <linux/mod_devicetable.h> include as requested by Uwe Kleine-König.
- Cover: describe both configurations, document the separately applied ipu-bridge entry and correct the v5 data-lanes history.
- Link to v6: https://patch.msgid.link/[email protected]

Changes in v6:
- dt-bindings: update the example to the PLL-derived 180 MHz link frequency.
- hm1092: replace the inconsistent 400 MHz link frequency and derived
80 MHz pixel rate with 180 MHz and 36 MHz. The new values are derived from the ASUS Windows configuration's 24 MHz external clock and the sensor PLL dividers.
- hm1092: report the 1296x736 native array, 1280x720 active array and 1150x718 mode crop as distinct selection targets.
- hm1092: add exposure, analogue gain and digital gain controls using the register map supplied by Jake Steinman. Keep multi-byte controls as separate writes, use group hold, derive the exposure maximum from VTS, and program the hardware-tested 500-line exposure default in both the init table and control. Hardware-test the controls on the 560x360 mode.
- hm1092: do not power the sensor on in probe(). There is no I2C access during probe, so leave it suspended and let runtime PM power it for streaming.
- Link to v5: https://patch.msgid.link/[email protected]

Changes in v5:
- dt-bindings: drop the local data-lanes constraint and omit it from
the example. Keep the inherited video-interfaces.yaml property allowed, even though it does not describe configurable hardware.
- hm1092: implement the get_selection pad op (CROP, CROP_DEFAULT, CROP_BOUNDS and NATIVE_SIZE) reporting the sensor's pixel-array
geometry, as required by libcamera.
- Link to v4: https://patch.msgid.link/[email protected]

Changes in v4:
- dt-bindings: make data-lanes optional for the fixed single-lane sensor, constrain explicit values to <1>, and omit it from the example.
- hm1092: add the copyright notice.
- hm1092: replace the private register representation and write helper
with CCI register sequences and direct CCI writes.
- hm1092: use descriptive test-pattern names based on hardware captures; the available documentation does not identify these modes.
- hm1092: parse fwnode properties before creating controls and set read-only flags after validating control creation.
- hm1092: use the generic get_fmt callback for the fixed sensor mode.
- hm1092: initialize endpoint parsing with the fixed one-lane default, reject other lane configurations, and remove the redundant endpoint presence check.
- hm1092: use fsleep() and null-safe GPIO calls, and apply the requested declaration, brace, return-value and error-path formatting cleanups.
- Link to v3: https://patch.msgid.link/[email protected]

Changes in v3:
- dt-bindings: add the Reviewed-by tag from Conor Dooley.
- hm1092: initialize RAW colorimetry fields in the pad format helper so userspace values cannot leak into subdevice state.
- hm1092: return -ENXIO when the required firmware graph endpoint is
absent instead of deferring probe indefinitely.
- Link to v2: https://patch.msgid.link/[email protected]

Changes in v2:
- hm1092: use pm_ptr() instead of pm_sleep_ptr() for the dev_pm_ops pointer. The ops come from DEFINE_RUNTIME_DEV_PM_OPS(), so gating them
on CONFIG_PM_SLEEP dropped runtime PM on a CONFIG_PM=y, PM_SLEEP=n
build.
- hm1092: free the control handler on the error paths in hm1092_init_controls(); the fwnode-parse and ctrl_hdlr->error returns leaked the handler.
- Link to v1: https://patch.msgid.link/[email protected]

---
Jake Steinman (1):
media: i2c: hm1092: Add ACPI enumeration and the 19.2 MHz configuration

Ramshouriesh R (2):
media: dt-bindings: Add Himax HM1092 NIR sensor
media: i2c: hm1092: add Himax HM1092 mono NIR sensor driver

 .../bindings/media/i2c/himax,hm1092.yaml           |  101 ++
 MAINTAINERS                                        |    7 +
 drivers/media/i2c/Kconfig                          |   11 +
 drivers/media/i2c/Makefile                         |    1 +
 drivers/media/i2c/hm1092.c                         | 1140 ++++++++++++++++++++
 5 files changed, 1260 insertions(+)

---
base-commit: 4eb2586bd40db27d77cc5b724ca9386ed0c71d53
change-id: 20260618-hm1092-driver-a6f2aaddf201

Best regards,

Signature · 1 line
Ramshouriesh R <[email protected]>
#1 lore raw

PATCH v8 1/3 media: dt-bindings: Add Himax HM1092 NIR sensor

Add a dt-binding schema for the Himax HM1092, a 1 megapixel monochrome near-infrared CMOS image sensor used as the face-authentication IR camera on laptops. The sensor streams 10-bit RAW over a single MIPI CSI-2 data lane.

---
 .../bindings/media/i2c/himax,hm1092.yaml           | 101 +++++++++++++++++++++
 1 file changed, 101 insertions(+)
Documentation/devicetree/bindings/media/i2c/himax,hm1092.yaml new file +101 -0
new file mode 100644
index 000000000000..e0a8bee62afc
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/himax,hm1092.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Himax HM1092 Monochrome NIR Sensor
+
+maintainers:
+  - Ramshouriesh R <[email protected]>
+
+description:
+  The Himax HM1092 is a 1 megapixel monochrome near-infrared CMOS image
+  sensor with a MIPI CSI-2 interface, commonly used as the IR camera for
+  face authentication on laptops. It outputs 10-bit RAW over a single
+  MIPI CSI-2 data lane.
+
+allOf:
+  - $ref: /schemas/media/video-interface-devices.yaml#
+
+properties:
+  compatible:
+    const: himax,hm1092
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  avdd-supply:
+    description: Analogue circuit voltage supply.
+
+  dovdd-supply:
+    description: I/O circuit voltage supply.
+
+  dvdd-supply:
+    description: Digital circuit voltage supply.
+
+  reset-gpios:
+    maxItems: 1
+    description: Active low GPIO connected to the XSHUTDOWN pad.
+
+  port:
+    $ref: /schemas/graph.yaml#/$defs/port-base
+    additionalProperties: false
+
+    properties:
+      endpoint:
+        $ref: /schemas/media/video-interfaces.yaml#
+        unevaluatedProperties: false
+
+        required:
+          - link-frequencies
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - avdd-supply
+  - dovdd-supply
+  - dvdd-supply
+  - port
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        camera@24 {
+            compatible = "himax,hm1092";
+            reg = <0x24>;
+
+            reset-gpios = <&tlmm 109 GPIO_ACTIVE_LOW>;
+            pinctrl-names = "default";
+            pinctrl-0 = <&cam_ir_default>;
+
+            clocks = <&camcc 1>;
+            assigned-clocks = <&camcc 1>;
+            assigned-clock-rates = <24000000>;
+
+            orientation = <0>;
+
+            avdd-supply = <&vreg_l7m_2p9>;
+            dvdd-supply = <&vreg_l7m_2p9>;
+            dovdd-supply = <&vreg_l4m_1p8>;
+
+            port {
+                hm1092_ep: endpoint {
+                    link-frequencies = /bits/ 64 <180000000>;
+                    remote-endpoint = <&camss_csiphy0_inep>;
+                };
+            };
+        };
+    };
+...
Signature · 1 line
2.55.0
#2 lore raw

PATCH v8 2/3 media: i2c: hm1092: add Himax HM1092 mono NIR sensor driver

In reply to Ramshouriesh R

Add a V4L2 subdev driver for the Himax HM1092, a 1 megapixel monochrome near-infrared image sensor with a single-lane MIPI CSI-2 interface, typically used as the face-authentication IR camera on laptops.

The driver supports the sensor's 560x360 10-bit mono mode, test patterns, exposure, analogue gain and digital gain controls, and the standard fwnode properties (orientation and rotation). It also reports the 1296x736 native pixel array, the 1280x720 active array and the mode's 1150x718 crop.

The vendor Windows configuration supplies a 24 MHz external clock and programs the MIPI PLL with a divide-by-12 pre-divider and a multiply-by-90 multiplier. This gives a 180 MHz CSI-2 DDR link frequency, a 360 Mbit/s single-lane bit rate and a 36 MHz pixel rate. With HTS 1616 and VTS 750, the resulting frame rate is approximately 29.7 frames per second.

The exposure, analogue gain and digital gain register layout was independently identified by Jake Steinman. Program multi-byte controls as separate high- and low-byte writes, and bracket exposure and gain updates with the sensor's group hold. The exposure default is 500 lines, selected from illuminated captures on an ASUS Zenbook A14 and programmed by the init sequence so the hardware and control states agree. The controls were exercised on that system, including full-range exposure and analogue gain tests and digital gain endpoint updates while streaming.

---
 MAINTAINERS                |   7 +
 drivers/media/i2c/Kconfig  |  11 +
 drivers/media/i2c/Makefile |   1 +
 drivers/media/i2c/hm1092.c | 870 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 889 insertions(+)
MAINTAINERS +7 -0
index c5519d67b6f4..fcba2a83baa9 100644
@@ -11969,6 +11969,13 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/media/i2c/himax,hm1246.yaml
 F:	drivers/media/i2c/hm1246.c
 
+HIMAX HM1092 SENSOR DRIVER
+M:	Ramshouriesh R <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/himax,hm1092.yaml
+F:	drivers/media/i2c/hm1092.c
+
 HIMAX HX83112B TOUCHSCREEN SUPPORT
 M:	Job Noorman <[email protected]>
 L:	[email protected]
drivers/media/i2c/Kconfig +11 -0
index 7de2d095c969..24ea80676849 100644
@@ -137,6 +137,17 @@ config VIDEO_HI847
           To compile this driver as a module, choose M here: the
           module will be called hi847.
 
+config VIDEO_HM1092
+	tristate "Himax HM1092 sensor support"
+	select V4L2_CCI_I2C
+	help
+	  This is a Video4Linux2 sensor driver for the Himax HM1092
+	  monochrome near-infrared CMOS image sensor, used as the
+	  face-authentication IR camera on laptops.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hm1092.
+
 config VIDEO_HM1246
 	tristate "Himax HM1246 sensor support"
 	depends on OF
drivers/media/i2c/Makefile +1 -0
index 59e31af457cd..b25b9d73a04d 100644
@@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_GC2145) += gc2145.o
 obj-$(CONFIG_VIDEO_HI556) += hi556.o
 obj-$(CONFIG_VIDEO_HI846) += hi846.o
 obj-$(CONFIG_VIDEO_HI847) += hi847.o
+obj-$(CONFIG_VIDEO_HM1092) += hm1092.o
 obj-$(CONFIG_VIDEO_HM1246) += hm1246.o
 obj-$(CONFIG_VIDEO_I2C) += video-i2c.o
 obj-$(CONFIG_VIDEO_IMX111) += imx111.o
drivers/media/i2c/hm1092.c new file +870 -0

870 lines not on this page · raw message

Signature · 1 line
2.55.0
#3 lore raw

Re: [PATCH v8 2/3] media: i2c: hm1092: add Himax HM1092 mono NIR sensor driver

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] media: i2c: hm1092: broken CONFIG_PM=n and disabled runtime PM handling - [Low] media: i2c…

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] media: i2c: hm1092: broken CONFIG_PM=n and disabled runtime PM handling
- [Low] media: i2c: hm1092: invalid max range on read-only VBLANK in hm1092_init_controls() --

commit f1e4ed7ce54114835ec68f36cc7257ae9a2789c1
Author: Ramshouriesh R <[email protected]>

media: i2c: hm1092: add Himax HM1092 mono NIR sensor driver

This commit adds a V4L2 subdevice driver for the Himax HM1092 monochrome near-infrared CMOS image sensor over a single-lane MIPI CSI-2 interface. It supports the sensor's 560x360 10-bit monochrome mode, standard controls including exposure, analogue gain, digital gain, and test patterns, and manages sensor power via runtime PM.

Quoted 18 lines
drivers/media/i2c/Kconfig +10 -0
index 7de2d095c9696..24ea806768493 100644
@@ -137,6 +137,17 @@ config VIDEO_HI847
           To compile this driver as a module, choose M here: the
           module will be called hi847.
 
+config VIDEO_HM1092
+	tristate "Himax HM1092 sensor support"
+	select V4L2_CCI_I2C
+	help
+	  This is a Video4Linux2 sensor driver for the Himax HM1092
+	  monochrome near-infrared CMOS image sensor, used as the
+	  face-authentication IR camera on laptops.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hm1092.

[ ... ]

Quoted 5 lines
drivers/media/i2c/hm1092.c new file +0 -0
new file mode 100644
index 0000000000000..400e9ded21667

[ ... ]

Quoted 4 lines
drivers/media/i2c/hm1092.c new file +4 -0
+static int hm1092_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct hm1092 *hm1092 = container_of(ctrl->handler, struct hm1092,
+					     ctrl_handler);

drivers/media/i2c/hm1092.c line 354

Quoted 10 lines
drivers/media/i2c/hm1092.c new file +10 -0
+	int release_ret;
+	int ret = 0;
+
+	/*
+	 * The control value is cached by the framework and (re)applied from
+	 * hm1092_enable_streams() once the device is powered up for streaming.
+	 */
+	ret = pm_runtime_get_if_in_use(hm1092->dev);
+	if (ret <= 0)
+		return ret;

[Severity: Medium]
Can this return -EINVAL when runtime PM is disabled or when the kernel is built with CONFIG_PM=n?

pm_runtime_get_if_in_use() returns 0 when the device is runtime suspended, 1 when it is active and in use, and -EINVAL when runtime PM is disabled (dev->power.disable_depth > 0 or CONFIG_PM=n). Returning ret directly when ret <= 0 propagates -EINVAL to VIDIOC_S_CTRL / VIDIOC_S_EXT_CTRLS callers and causes hm1092_enable_streams() -> __v4l2_ctrl_handler_setup() -> hm1092_set_ctrl() to fail if runtime PM is disabled.

Additionally, VIDEO_HM1092 in drivers/media/i2c/Kconfig does not depend on PM, while hm1092_probe() leaves the sensor powered off (with reset asserted via GPIOD_OUT_HIGH) and relies solely on pm_runtime_resume_and_get() in hm1092_enable_streams() to invoke hm1092_power_on(). When CONFIG_PM=n:

- pm_runtime_resume_and_get() is a no-op returning 0, so hm1092_power_on() is never called and the sensor remains unpowered during streaming.
- In hm1092_remove(), pm_runtime_status_suspended() unconditionally returns false, so hm1092_power_off() calls clk_disable_unprepare(hm1092->img_clk) and regulator_bulk_disable() on resources that were never enabled.

Should hm1092_set_ctrl() return 0 when pm_runtime_get_if_in_use() <= 0 (or use pm_runtime_get_if_active()), and should the driver either depend on PM in Kconfig or handle powering the sensor on/off in hm1092_probe() and hm1092_remove() when CONFIG_PM is disabled?

[ ... ]

Quoted 36 lines
drivers/media/i2c/hm1092.c new file +36 -0
+static int hm1092_init_controls(struct hm1092 *hm1092)
+{
+	struct v4l2_ctrl_handler *ctrl_hdlr = &hm1092->ctrl_handler;
+	const struct hm1092_mode *mode = &hm1092_mode_560x360;
+	struct v4l2_fwnode_device_properties props;
+	s64 hblank, pixel_rate;
+	int ret;
+
+	ret = v4l2_fwnode_device_parse(hm1092->dev, &props);
+	if (ret)
+		return ret;
+
+	v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+
+	hm1092->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
+						   &hm1092_ctrl_ops,
+						   V4L2_CID_LINK_FREQ,
+						   0, 0,
+						   hm1092_link_freq_menu);
+
+	pixel_rate = div_u64(HM1092_LINK_FREQ * 2 * hm1092->mipi_lanes,
+			     HM1092_BITS_PER_SAMPLE);
+	hm1092->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &hm1092_ctrl_ops,
+					       V4L2_CID_PIXEL_RATE, 0,
+					       pixel_rate, 1, pixel_rate);
+
+	hblank = mode->hts - mode->width;
+	hm1092->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &hm1092_ctrl_ops,
+					   V4L2_CID_HBLANK, hblank, hblank, 1,
+					   hblank);
+
+	hm1092->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &hm1092_ctrl_ops,
+					   V4L2_CID_VBLANK,
+					   mode->vts - mode->height,
+					   0xffff - mode->height, 1,
+					   mode->vts - mode->height);

[ ... ]

drivers/media/i2c/hm1092.c line 478

Quoted 8 lines
drivers/media/i2c/hm1092.c new file +8 -0
+	if (ctrl_hdlr->error) {
+		ret = ctrl_hdlr->error;
+		goto err_free;
+	}
+
+	hm1092->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	hm1092->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	hm1092->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;

[Severity: Low]
In hm1092_init_controls(), V4L2_CID_VBLANK is marked with V4L2_CTRL_FLAG_READ_ONLY and is not handled in hm1092_set_ctrl(), but its maximum value is set to 0xffff - mode->height instead of mode->vts - mode->height.

Will advertising a larger maximum on a fixed, read-only V4L2_CID_VBLANK control cause userspace camera stacks to compute an incorrect maximum frame duration? Should the maximum for V4L2_CID_VBLANK match mode->vts - mode->height, similar to V4L2_CID_HBLANK?

Also, the pixel_rate and exposure pointers in struct hm1092 (along with HM1092_TEST_PATTERN_MAX) are never read after assignment; could those unused fields and macro be dropped?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
#4 lore raw

PATCH v8 3/3 media: i2c: hm1092: Add ACPI enumeration and the 19.2 MHz configuration

In reply to Ramshouriesh R

From: Jake Steinman <[email protected]>

The HM1092 is also the IR camera of Intel IPU6/IPU7 laptops. There it is enumerated through ACPI as HIMX1092 and clocked at 19.2 MHz rather than 24 MHz. The driver cannot bind on those machines: it has no ACPI match table and probe rejects any external clock other than 24 MHz.

Turn the single fixed mode into a table of configurations selected by the external clock rate, each with its own init sequence, link frequency, timings, readout window and exposure default. Add the configuration the vendor Windows driver uses on the Intel platforms:
648x368, HTS 1620, VTS 740, with the PLL pre-divider at 10 and the multiplier at 94, giving 19200000 * 94 / 10 = 180480000 Hz.

ipu-bridge lists both 180000000 and 180480000 for this sensor, so matching any menu entry against the endpoint is no longer enough. Check that the endpoint offers the link frequency of the selected configuration.

The 24 MHz configuration is unchanged: same init sequence, timings, controls and defaults.

Tested on a Dell XPS 16 DA16260 (Panther Lake, IPU7, 19.2 MHz clock from INT3472): the sensor binds on CSI-2 port 2, streams 648x368 at link frequency 180480000 and the exposure control changes the image level. The 24 MHz configuration is compile-tested only.

[rshouriesh: drop the <linux/mod_devicetable.h> include, <linux/i2c.h>
already provides struct acpi_device_id; rebase the pixel-array comment over the widened crop bounds]

---
 drivers/media/i2c/hm1092.c | 362 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 316 insertions(+), 46 deletions(-)
drivers/media/i2c/hm1092.c +316 -46
index 400e9ded2166..ac61fd59c63c 100644
@@ -20,15 +20,19 @@
 /*
  * CSI-2 D-PHY link frequency (DDR, i.e. half the per-lane bit rate).
  *
- * The vendor Windows configuration supplies a 24 MHz EXTCLK and programs the
- * MIPI PLL pre-divider to 12 (0x030d = 0x0c) and multiplier to 90
- * (0x030f = 0x5a):
- *   link freq  = 24000000 * 90 / 12          = 180000000
- *   bit rate   = link freq * 2               = 360000000 bps
- *   pixel_rate = bit rate * 1 lane / 10 bpp  = 36000000 pixels/s
+ * Two vendor Windows configurations are known. Each pairs an EXTCLK rate with
+ * its own MIPI PLL pre-divider (0x030d) and multiplier (0x030f):
+ *
+ *   24 MHz EXTCLK, pre-divider 12, multiplier 90 (Qualcomm):
+ *     link freq  = 24000000 * 90 / 12          = 180000000
+ *     pixel_rate = link freq * 2 * 1 lane / 10 = 36000000 pixels/s
+ *
+ *   19.2 MHz EXTCLK, pre-divider 10, multiplier 94 (Intel IPU):
+ *     link freq  = 19200000 * 94 / 10          = 180480000
+ *     pixel_rate = link freq * 2 * 1 lane / 10 = 36096000 pixels/s
  */
-#define HM1092_LINK_FREQ		180000000ULL
-#define HM1092_MCLK			24000000
+#define HM1092_LINK_FREQ_180MHZ		180000000ULL
+#define HM1092_LINK_FREQ_180P48MHZ	180480000ULL
 #define HM1092_BITS_PER_SAMPLE		10
 
 #define HM1092_REG_STREAM		CCI_REG8(0x0100)
@@ -43,16 +47,14 @@
  * Digital gain is a 10-bit value in 2.8 fixed point (0x100 is unity) split
  * across 0x020e/0x020f; bits above bit 9 are ignored by the sensor.
  * The register layout is not documented publicly; it was recovered from the
- * vendor Windows driver. The exposure default (500) was selected from
- * illuminated hardware tests and is also programmed by the init sequence at
- * 0x0202/0x0203.
+ * vendor Windows driver. The exposure default is per mode and matches what
+ * that mode's init sequence programs at 0x0202/0x0203.
  */
 #define HM1092_REG_EXPOSURE_H		CCI_REG8(0x0202)
 #define HM1092_REG_EXPOSURE_L		CCI_REG8(0x0203)
 #define HM1092_EXPOSURE_MIN		2
 #define HM1092_EXPOSURE_MARGIN		21
 #define HM1092_EXPOSURE_STEP		1
-#define HM1092_EXPOSURE_DEFAULT		500
 
 #define HM1092_REG_ANALOGUE_GAIN	CCI_REG8(0x0205)
 #define HM1092_ANALOGUE_GAIN_MIN	0
@@ -76,9 +78,9 @@
  * full array including the surrounding dummy/optical-black pixels is 1296x736
  * (8 pixels of margin on each side). The crop bounds cover the full array,
  * since a readout window may include the margin; the default crop is the
- * active array. The single fixed mode reads a window out of the active array
- * (X_ADDR [0x0030..0x04ad], Y_ADDR [0x0008..0x02d5]); subsequent internal
- * processing produces the 560x360 output.
+ * active array. Each mode reads a fixed window out of the array (X_ADDR/Y_ADDR
+ * registers 0x0344..0x034b, reported as the crop rectangle); subsequent
+ * internal processing produces the output size.
  */
 #define HM1092_NATIVE_WIDTH		1296U
 #define HM1092_NATIVE_HEIGHT		736U
@@ -86,12 +88,8 @@
 #define HM1092_ACTIVE_TOP		8U
 #define HM1092_ACTIVE_WIDTH		1280U
 #define HM1092_ACTIVE_HEIGHT		720U
-#define HM1092_CROP_LEFT		48U
-#define HM1092_CROP_TOP			8U
-#define HM1092_CROP_WIDTH		1150U
-#define HM1092_CROP_HEIGHT		718U
 
-static const struct cci_reg_sequence hm1092_init_regs[] = {
+static const struct cci_reg_sequence hm1092_560x360_regs[] = {
 	{ CCI_REG8(0x0103), 0x00 },
 	{ CCI_REG8(0x030a), 0x05 },
 	{ CCI_REG8(0x030d), 0x0c },
@@ -279,18 +277,265 @@ static const struct cci_reg_sequence hm1092_init_regs[] = {
 	{ CCI_REG8(0x0037), 0x30 },
 };
 
+/*
+ * Init sequence used with a 19.2 MHz EXTCLK, as found on Intel IPU laptops
+ * where the sensor is described in ACPI as HIMX1092. Recovered from the
+ * vendor Windows driver for those platforms; kept in the order that driver
+ * issues it, including its repeated writes.
+ */
+static const struct cci_reg_sequence hm1092_648x368_regs[] = {
+	{ CCI_REG8(0x0100), 0x00 },
+	{ CCI_REG8(0x0103), 0x00 },
+	{ CCI_REG8(0x0000), 0x00 },
+	{ CCI_REG8(0x0101), 0x00 },
+	{ CCI_REG8(0x0202), 0x02 },
+	{ CCI_REG8(0x0203), 0xe5 },
+	{ CCI_REG8(0x0307), 0x00 },
+	{ CCI_REG8(0x0309), 0x01 },
+	{ CCI_REG8(0x030a), 0x05 },
+	{ CCI_REG8(0x030d), 0x0a },
+	{ CCI_REG8(0x030f), 0x5e },
+	{ CCI_REG8(0x0310), 0x00 },
+	{ CCI_REG8(0x0340), 0x03 },
+	{ CCI_REG8(0x0341), 0x1e },
+	{ CCI_REG8(0x0342), 0x05 },
+	{ CCI_REG8(0x0343), 0xe4 },
+	{ CCI_REG8(0x0350), 0x53 },
+	{ CCI_REG8(0x0387), 0x01 },
+	{ CCI_REG8(0x3110), 0x02 },
+	{ CCI_REG8(0x3735), 0xe2 },
+	{ CCI_REG8(0x3704), 0x04 },
+	{ CCI_REG8(0x4001), 0x00 },
+	{ CCI_REG8(0x4002), 0x2b },
+	{ CCI_REG8(0x4024), 0x40 },
+	{ CCI_REG8(0x4131), 0x01 },
+	{ CCI_REG8(0x4132), 0x20 },
+	{ CCI_REG8(0x4265), 0x02 },
+	{ CCI_REG8(0x4b04), 0x01 },
+	{ CCI_REG8(0x4b0e), 0x0e },
+	{ CCI_REG8(0x4b18), 0x00 },
+	{ CCI_REG8(0x4b20), 0x9e },
+	{ CCI_REG8(0x4b31), 0x06 },
+	{ CCI_REG8(0x4b3b), 0x02 },
+	{ CCI_REG8(0x4b3e), 0x00 },
+	{ CCI_REG8(0x4b44), 0x0c },
+	{ CCI_REG8(0x4b45), 0x01 },
+	{ CCI_REG8(0x4b47), 0x00 },
+	{ CCI_REG8(0x5004), 0x40 },
+	{ CCI_REG8(0x5005), 0x28 },
+	{ CCI_REG8(0x5006), 0x40 },
+	{ CCI_REG8(0x5007), 0x28 },
+	{ CCI_REG8(0x5010), 0x20 },
+	{ CCI_REG8(0x5011), 0x00 },
+	{ CCI_REG8(0x5013), 0x03 },
+	{ CCI_REG8(0x5015), 0xb3 },
+	{ CCI_REG8(0x501d), 0x4c },
+	{ CCI_REG8(0x5098), 0x00 },
+	{ CCI_REG8(0x5099), 0x11 },
+	{ CCI_REG8(0x509b), 0x03 },
+	{ CCI_REG8(0x50a0), 0x30 },
+	{ CCI_REG8(0x50a2), 0x0b },
+	{ CCI_REG8(0x50a6), 0x00 },
+	{ CCI_REG8(0x50a7), 0x00 },
+	{ CCI_REG8(0x50aa), 0x22 },
+	{ CCI_REG8(0x50ab), 0x07 },
+	{ CCI_REG8(0x50ac), 0x24 },
+	{ CCI_REG8(0x50ad), 0x07 },
+	{ CCI_REG8(0x50ae), 0x20 },
+	{ CCI_REG8(0x50af), 0x40 },
+	{ CCI_REG8(0x50b3), 0x04 },
+	{ CCI_REG8(0x50b4), 0x00 },
+	{ CCI_REG8(0x50b7), 0x00 },
+	{ CCI_REG8(0x50b8), 0x70 },
+	{ CCI_REG8(0x50b9), 0xff },
+	{ CCI_REG8(0x50ba), 0xff },
+	{ CCI_REG8(0x50bb), 0x14 },
+	{ CCI_REG8(0x50cb), 0x21 },
+	{ CCI_REG8(0x50d5), 0xe0 },
+	{ CCI_REG8(0x50d7), 0x12 },
+	{ CCI_REG8(0x50dd), 0x00 },
+	{ CCI_REG8(0x50e8), 0x00 },
+	{ CCI_REG8(0x50ea), 0x74 },
+	{ CCI_REG8(0x50fa), 0x02 },
+	{ CCI_REG8(0x5100), 0x03 },
+	{ CCI_REG8(0x5101), 0x13 },
+	{ CCI_REG8(0x5102), 0x23 },
+	{ CCI_REG8(0x5103), 0x33 },
+	{ CCI_REG8(0x5104), 0x43 },
+	{ CCI_REG8(0x5105), 0x42 },
+	{ CCI_REG8(0x5106), 0x40 },
+	{ CCI_REG8(0x5118), 0x00 },
+	{ CCI_REG8(0x5119), 0x00 },
+	{ CCI_REG8(0x511a), 0x00 },
+	{ CCI_REG8(0x511b), 0x00 },
+	{ CCI_REG8(0x511c), 0x00 },
+	{ CCI_REG8(0x511d), 0x00 },
+	{ CCI_REG8(0x511e), 0x00 },
+	{ CCI_REG8(0x5130), 0x13 },
+	{ CCI_REG8(0x5131), 0x23 },
+	{ CCI_REG8(0x5132), 0x33 },
+	{ CCI_REG8(0x5133), 0x43 },
+	{ CCI_REG8(0x5134), 0x42 },
+	{ CCI_REG8(0x5135), 0x40 },
+	{ CCI_REG8(0x5136), 0x40 },
+	{ CCI_REG8(0x5148), 0x01 },
+	{ CCI_REG8(0x5149), 0x01 },
+	{ CCI_REG8(0x514a), 0x01 },
+	{ CCI_REG8(0x514b), 0x01 },
+	{ CCI_REG8(0x514c), 0x01 },
+	{ CCI_REG8(0x514d), 0x01 },
+	{ CCI_REG8(0x514e), 0x01 },
+	{ CCI_REG8(0x51c0), 0x00 },
+	{ CCI_REG8(0x51c1), 0x81 },
+	{ CCI_REG8(0x51c2), 0xec },
+	{ CCI_REG8(0x51c3), 0x00 },
+	{ CCI_REG8(0x51c4), 0x55 },
+	{ CCI_REG8(0x51c5), 0x44 },
+	{ CCI_REG8(0x51c6), 0x00 },
+	{ CCI_REG8(0x51c7), 0x81 },
+	{ CCI_REG8(0x51c8), 0xec },
+	{ CCI_REG8(0x51c9), 0x00 },
+	{ CCI_REG8(0x51ca), 0x55 },
+	{ CCI_REG8(0x51cb), 0x24 },
+	{ CCI_REG8(0x51cc), 0x00 },
+	{ CCI_REG8(0x51cd), 0x81 },
+	{ CCI_REG8(0x51ce), 0xec },
+	{ CCI_REG8(0x51cf), 0x00 },
+	{ CCI_REG8(0x51d0), 0x54 },
+	{ CCI_REG8(0x51d1), 0x24 },
+	{ CCI_REG8(0x51d2), 0x00 },
+	{ CCI_REG8(0x51d3), 0x81 },
+	{ CCI_REG8(0x51d4), 0xec },
+	{ CCI_REG8(0x51d5), 0x00 },
+	{ CCI_REG8(0x51d6), 0x53 },
+	{ CCI_REG8(0x51d7), 0x14 },
+	{ CCI_REG8(0x51d8), 0x00 },
+	{ CCI_REG8(0x51d9), 0x81 },
+	{ CCI_REG8(0x51da), 0xec },
+	{ CCI_REG8(0x51db), 0x00 },
+	{ CCI_REG8(0x51dc), 0x53 },
+	{ CCI_REG8(0x51dd), 0x14 },
+	{ CCI_REG8(0x51e0), 0x09 },
+	{ CCI_REG8(0x51e1), 0x03 },
+	{ CCI_REG8(0x51e2), 0x04 },
+	{ CCI_REG8(0x51e3), 0x03 },
+	{ CCI_REG8(0x51e4), 0x08 },
+	{ CCI_REG8(0x51e5), 0x07 },
+	{ CCI_REG8(0x51e6), 0x08 },
+	{ CCI_REG8(0x51e7), 0x07 },
+	{ CCI_REG8(0x51e8), 0x04 },
+	{ CCI_REG8(0x51e9), 0x46 },
+	{ CCI_REG8(0x51ea), 0x43 },
+	{ CCI_REG8(0x51eb), 0x62 },
+	{ CCI_REG8(0x51ec), 0x61 },
+	{ CCI_REG8(0x51ed), 0x00 },
+	{ CCI_REG8(0x51ee), 0x00 },
+	{ CCI_REG8(0x5200), 0x60 },
+	{ CCI_REG8(0x5201), 0x80 },
+	{ CCI_REG8(0x5202), 0x00 },
+	{ CCI_REG8(0x5203), 0x01 },
+	{ CCI_REG8(0x5206), 0x80 },
+	{ CCI_REG8(0x5208), 0x0b },
+	{ CCI_REG8(0x5209), 0x0c },
+	{ CCI_REG8(0x520c), 0x15 },
+	{ CCI_REG8(0x520d), 0x40 },
+	{ CCI_REG8(0x5214), 0x28 },
+	{ CCI_REG8(0x5215), 0x04 },
+	{ CCI_REG8(0x5216), 0x02 },
+	{ CCI_REG8(0x5217), 0x01 },
+	{ CCI_REG8(0x5218), 0x07 },
+	{ CCI_REG8(0x521e), 0x01 },
+	{ CCI_REG8(0x5282), 0xff },
+	{ CCI_REG8(0x5283), 0x03 },
+	{ CCI_REG8(0x0202), 0x01 },
+	{ CCI_REG8(0x0203), 0x68 },
+	{ CCI_REG8(0x0340), 0x02 },
+	{ CCI_REG8(0x0341), 0xe4 },
+	{ CCI_REG8(0x0342), 0x06 },
+	{ CCI_REG8(0x0343), 0x54 },
+	{ CCI_REG8(0x0344), 0x00 },
+	{ CCI_REG8(0x0345), 0x00 },
+	{ CCI_REG8(0x0346), 0x00 },
+	{ CCI_REG8(0x0347), 0x00 },
+	{ CCI_REG8(0x0348), 0x05 },
+	{ CCI_REG8(0x0349), 0x0d },
+	{ CCI_REG8(0x034a), 0x02 },
+	{ CCI_REG8(0x034b), 0xdd },
+	{ CCI_REG8(0x034c), 0x02 },
+	{ CCI_REG8(0x034d), 0x88 },
+	{ CCI_REG8(0x034e), 0x01 },
+	{ CCI_REG8(0x034f), 0x70 },
+	{ CCI_REG8(0x0383), 0x00 },
+	{ CCI_REG8(0x0387), 0x10 },
+	{ CCI_REG8(0x0390), 0x03 },
+	{ CCI_REG8(0x4800), 0xac },
+	{ CCI_REG8(0x0104), 0x01 },
+	{ CCI_REG8(0x0104), 0x00 },
+	{ CCI_REG8(0x4801), 0xae },
+	{ CCI_REG8(0x4b20), 0x9e },
+	{ CCI_REG8(0x0101), 0x03 },
+};
+
+/**
+ * struct hm1092_mode - sensor configuration, selected by the EXTCLK rate
+ * @mclk: EXTCLK rate the init sequence programs the PLL for
+ * @link_freq_index: index into hm1092_link_freq_menu
+ * @width: output width
+ * @height: output height
+ * @hts: horizontal total size, 0x0342/0x0343
+ * @vts: vertical total size, 0x0340/0x0341
+ * @exposure_def: exposure programmed by the init sequence
+ * @crop: readout window on the pixel array
+ * @regs: init sequence
+ * @num_regs: number of entries in @regs
+ */
 struct hm1092_mode {
+	unsigned long mclk;
+	unsigned int link_freq_index;
 	u32 width;
 	u32 height;
 	u32 hts;
 	u32 vts;
+	u32 exposure_def;
+	struct v4l2_rect crop;
+	const struct cci_reg_sequence *regs;
+	unsigned int num_regs;
 };
 
-static const struct hm1092_mode hm1092_mode_560x360 = {
-	.width = 560,
-	.height = 360,
-	.hts = 0x0650,
-	.vts = 0x02ee,
+static const struct hm1092_mode hm1092_modes[] = {
+	{
+		.mclk = 24000000,
+		.link_freq_index = 0,
+		.width = 560,
+		.height = 360,
+		.hts = 0x0650,
+		.vts = 0x02ee,
+		.exposure_def = 500,
+		.crop = {
+			.left = 48,
+			.top = 8,
+			.width = 1150,
+			.height = 718,
+		},
+		.regs = hm1092_560x360_regs,
+		.num_regs = ARRAY_SIZE(hm1092_560x360_regs),
+	}, {
+		.mclk = 19200000,
+		.link_freq_index = 1,
+		.width = 648,
+		.height = 368,
+		.hts = 0x0654,
+		.vts = 0x02e4,
+		.exposure_def = 360,
+		.crop = {
+			.left = 0,
+			.top = 0,
+			.width = 1294,
+			.height = 734,
+		},
+		.regs = hm1092_648x368_regs,
+		.num_regs = ARRAY_SIZE(hm1092_648x368_regs),
+	},
 };
 
 static const char * const hm1092_supply_names[] = {
@@ -313,7 +558,8 @@ static const char * const hm1092_test_pattern_menu[] = {
 };
 
 static const s64 hm1092_link_freq_menu[] = {
-	HM1092_LINK_FREQ,
+	HM1092_LINK_FREQ_180MHZ,
+	HM1092_LINK_FREQ_180P48MHZ,
 };
 
 struct hm1092 {
@@ -330,6 +576,7 @@ struct hm1092 {
 	struct v4l2_ctrl *hblank;
 	struct v4l2_ctrl *vblank;
 	struct v4l2_ctrl *exposure;
+	const struct hm1092_mode *mode;
 	u8 mipi_lanes;
 };
 
@@ -405,7 +652,7 @@ static const struct v4l2_ctrl_ops hm1092_ctrl_ops = {
 static int hm1092_init_controls(struct hm1092 *hm1092)
 {
 	struct v4l2_ctrl_handler *ctrl_hdlr = &hm1092->ctrl_handler;
-	const struct hm1092_mode *mode = &hm1092_mode_560x360;
+	const struct hm1092_mode *mode = hm1092->mode;
 	struct v4l2_fwnode_device_properties props;
 	s64 hblank, pixel_rate;
 	int ret;
@@ -419,11 +666,12 @@ static int hm1092_init_controls(struct hm1092 *hm1092)
 	hm1092->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
 						   &hm1092_ctrl_ops,
 						   V4L2_CID_LINK_FREQ,
-						   0, 0,
+						   ARRAY_SIZE(hm1092_link_freq_menu) - 1,
+						   mode->link_freq_index,
 						   hm1092_link_freq_menu);
 
-	pixel_rate = div_u64(HM1092_LINK_FREQ * 2 * hm1092->mipi_lanes,
-			     HM1092_BITS_PER_SAMPLE);
+	pixel_rate = div_u64(hm1092_link_freq_menu[mode->link_freq_index] * 2 *
+			     hm1092->mipi_lanes, HM1092_BITS_PER_SAMPLE);
 	hm1092->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &hm1092_ctrl_ops,
 					       V4L2_CID_PIXEL_RATE, 0,
 					       pixel_rate, 1, pixel_rate);
@@ -445,7 +693,7 @@ static int hm1092_init_controls(struct hm1092 *hm1092)
 					     mode->vts -
 					     HM1092_EXPOSURE_MARGIN,
 					     HM1092_EXPOSURE_STEP,
-					     HM1092_EXPOSURE_DEFAULT);
+					     mode->exposure_def);
 
 	v4l2_ctrl_new_std(ctrl_hdlr, &hm1092_ctrl_ops,
 			  V4L2_CID_ANALOGUE_GAIN,
@@ -487,10 +735,11 @@ static int hm1092_init_controls(struct hm1092 *hm1092)
 	return ret;
 }
 
-static void hm1092_update_pad_format(struct v4l2_mbus_framefmt *fmt)
+static void hm1092_update_pad_format(const struct hm1092_mode *mode,
+				     struct v4l2_mbus_framefmt *fmt)
 {
-	fmt->width = hm1092_mode_560x360.width;
-	fmt->height = hm1092_mode_560x360.height;
+	fmt->width = mode->width;
+	fmt->height = mode->height;
 	fmt->code = MEDIA_BUS_FMT_Y10_1X10;
 	fmt->field = V4L2_FIELD_NONE;
 	fmt->colorspace = V4L2_COLORSPACE_RAW;
@@ -510,8 +759,8 @@ static int hm1092_enable_streams(struct v4l2_subdev *sd,
 	if (ret)
 		return ret;
 
-	ret = cci_multi_reg_write(hm1092->regmap, hm1092_init_regs,
-				  ARRAY_SIZE(hm1092_init_regs), NULL);
+	ret = cci_multi_reg_write(hm1092->regmap, hm1092->mode->regs,
+				  hm1092->mode->num_regs, NULL);
 	if (ret) {
 		dev_err(hm1092->dev, "failed to write init registers\n");
 		goto out;
@@ -563,16 +812,18 @@ static int hm1092_enum_frame_size(struct v4l2_subdev *sd,
 				  struct v4l2_subdev_state *state,
 				  struct v4l2_subdev_frame_size_enum *fse)
 {
+	const struct hm1092_mode *mode = to_hm1092(sd)->mode;
+
 	if (fse->index)
 		return -EINVAL;
 
 	if (fse->code != MEDIA_BUS_FMT_Y10_1X10)
 		return -EINVAL;
 
-	fse->min_width = hm1092_mode_560x360.width;
-	fse->max_width = hm1092_mode_560x360.width;
-	fse->min_height = hm1092_mode_560x360.height;
-	fse->max_height = hm1092_mode_560x360.height;
+	fse->min_width = mode->width;
+	fse->max_width = mode->width;
+	fse->min_height = mode->height;
+	fse->max_height = mode->height;
 
 	return 0;
 }
@@ -584,10 +835,7 @@ static int hm1092_get_selection(struct v4l2_subdev *sd,
 {
 	switch (sel->target) {
 	case V4L2_SEL_TGT_CROP:
-		sel->r.left = HM1092_CROP_LEFT;
-		sel->r.top = HM1092_CROP_TOP;
-		sel->r.width = HM1092_CROP_WIDTH;
-		sel->r.height = HM1092_CROP_HEIGHT;
+		sel->r = to_hm1092(sd)->mode->crop;
 		return 0;
 	case V4L2_SEL_TGT_CROP_DEFAULT:
 		sel->r.left = HM1092_ACTIVE_LEFT;
@@ -610,7 +858,8 @@ static int hm1092_get_selection(struct v4l2_subdev *sd,
 static int hm1092_init_state(struct v4l2_subdev *sd,
 			     struct v4l2_subdev_state *state)
 {
-	hm1092_update_pad_format(v4l2_subdev_state_get_format(state, 0));
+	hm1092_update_pad_format(to_hm1092(sd)->mode,
+				 v4l2_subdev_state_get_format(state, 0));
 
 	return 0;
 }
@@ -670,6 +919,14 @@ static int hm1092_check_hwcfg(struct hm1092 *hm1092)
 	if (ret)
 		goto out;
 
+	if (!(link_freq_bitmap & BIT(hm1092->mode->link_freq_index))) {
+		ret = dev_err_probe(dev, -EINVAL,
+				    "link frequency %lld not available for a %lu Hz external clock\n",
+				    hm1092_link_freq_menu[hm1092->mode->link_freq_index],
+				    hm1092->mode->mclk);
+		goto out;
+	}
+
 	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1) {
 		ret = dev_err_probe(dev, -EINVAL,
 				    "only 1 data lane is supported, got %u\n",
@@ -782,7 +1039,13 @@ static int hm1092_probe(struct i2c_client *client)
 				     "failed to get imaging clock\n");
 
 	freq = clk_get_rate(hm1092->img_clk);
-	if (freq != HM1092_MCLK)
+	for (unsigned int i = 0; i < ARRAY_SIZE(hm1092_modes); i++) {
+		if (hm1092_modes[i].mclk == freq) {
+			hm1092->mode = &hm1092_modes[i];
+			break;
+		}
+	}
+	if (!hm1092->mode)
 		return dev_err_probe(hm1092->dev, -EINVAL,
 				     "external clock %lu is not supported\n",
 				     freq);
@@ -848,6 +1111,12 @@ static int hm1092_probe(struct i2c_client *client)
 static DEFINE_RUNTIME_DEV_PM_OPS(hm1092_pm_ops, hm1092_power_off,
 				 hm1092_power_on, NULL);
 
+static const struct acpi_device_id hm1092_acpi_ids[] = {
+	{ "HIMX1092" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, hm1092_acpi_ids);
+
 static const struct of_device_id hm1092_of_match[] = {
 	{ .compatible = "himax,hm1092" },
 	{ /* sentinel */ }
@@ -858,6 +1127,7 @@ static struct i2c_driver hm1092_i2c_driver = {
 	.driver = {
 		.name = "hm1092",
 		.pm = pm_ptr(&hm1092_pm_ops),
+		.acpi_match_table = hm1092_acpi_ids,
 		.of_match_table = hm1092_of_match,
 	},
 	.probe = hm1092_probe,
Signature · 1 line
2.55.0