Skip to content

A non-responsive VCM (dw9719) blocks all IPU3 cameras from registering on Surface Book 2 #2223

Open

Description

@chunkynut

On a Surface Book 2 (13"), no cameras are registered by libcamera (cam -l lists none), even though all three sensors probe successfully and bind to the CIO2 async notifier without error.

Root cause: the ACPI SSDB for the rear sensor (ov8865 / INT347A) declares vcmtype = 4 (dw9719), so ipu-bridge adds a lens-focus property and instantiates a VCM i2c client. That VCM never responds on i2c, so dw9719_probe() fails and the subdev is never registered. ov8865's own async notifier therefore never completes, and because v4l2-async will not complete a parent notifier until every bound subdev's child notifier has also completed, cio2_notifier_complete() is never called at all. No media links are created, v4l2_device_register_subdev_nodes() never runs, and every camera is lost — including the front sensor, which has no VCM of its own.

Working around the VCM (patch below) makes both front and rear cameras work immediately.

Environment
Device: Microsoft Surface Book 2, 13"
Kernel: 6.19.8-surface-3 (linux-surface official build)
Distro: Ubuntu 26.04, libcamera 0.7.0
CONFIG_VIDEO_IPU3_CIO2=m, CONFIG_IPU_BRIDGE=m, CONFIG_VIDEO_IPU3_IMGU=m, CONFIG_INTEL_SKL_INT3472=m
Sensors: ov5693 front (INT33BE, _SB.PCI0.I2C2.CAMF), ov8865 rear (INT347A, _SB.PCI0.I2C3.CAMR), ov7251 IR (INT347E, _SB.PCI0.I2C3.CAM3)
Reproduce
Boot a Surface Book 2 on the linux-surface kernel.
cam -l → Available cameras: with nothing listed.
media-ctl -d /dev/media0 -p → all three sensor entities show 0 link; no sensor is linked to any ipu3-csi2 receiver.
Diagnosis trail
Everything up to link creation succeeds:

ipu3-cio2 0000:00:14.3: Found supported sensor INT33BE:00
ipu3-cio2 0000:00:14.3: Found supported sensor INT347A:00
ipu3-cio2 0000:00:14.3: Found supported sensor INT347E:00
ipu3-cio2 0000:00:14.3: Connected 3 cameras
With dynamic debug enabled, all three sensors match and bind cleanly:

v4l2-async: match found, subdev ov5693 2-0036
v4l2-async: i2c-INT33BE:00 bound (ret 0)
v4l2-async: match found, subdev ov8865 3-0010
v4l2-async: i2c-INT347A:00 bound (ret 0)
v4l2-async: match found, subdev ov7251 3-0060
v4l2-async: i2c-INT347E:00 bound (ret 0)
/sys/kernel/debug/v4l2-async/pending_async_subdevices identifies the single stuck dependency — note the CIO2 notifier itself has nothing pending:

ipu3-cio2 0000:00:14.3:
ov5693 2-0036:
ov8865 3-0010:
[fwnode] dev=i2c-INT347A:00-VCM, node=dw9719-0
Adding an unconditional dev_info() at the top of cio2_notifier_complete() confirms it is never entered — so no media_create_pad_link() and no v4l2_device_register_subdev_nodes() ever happen. This is why manual media-ctl -l link creation also fails; the link objects do not exist:

$ media-ctl -d /dev/media0 -l "'ov5693 2-0036':0 -> 'ipu3-csi2 0':0[1]"
Unable to parse link: Invalid argument (22)
Why the VCM never probes
ipu_bridge_instantiate_vcm_work() creates the i2c client while holding pm_runtime_get_sync(sensor), precisely so the VCM is powered during probe. But dw9719.ko is not loaded at that moment (nothing autoloads it before the client appears), so no probe happens then. By the time the module is loaded, the sensor has runtime-suspended and the chip does not answer:

$ sudo modprobe dw9719
$ echo 'i2c-INT347A:00-VCM' | sudo tee /sys/bus/i2c/drivers/dw9719/bind
/sys/bus/i2c/drivers/dw9719/bind: No such device (os error 19)
dw9719_probe() calls dw9719_power_up(dw9719, true) before enabling runtime PM, which does a cci_read(DW9719_INFO) chip-ID read; that read fails, giving -ENODEV.

Forcing the sensor to stay powered does not help — the chip still does not respond:

$ echo on | sudo tee /sys/bus/i2c/devices/i2c-INT347A:00/power/control
$ cat /sys/bus/i2c/devices/i2c-INT347A:00/power/runtime_status # active
$ cat /sys/bus/i2c/devices/i2c-INT347A:00-VCM/waiting_for_supplier # 0
$ echo 'i2c-INT347A:00-VCM' | sudo tee /sys/bus/i2c/drivers/dw9719/bind
/sys/bus/i2c/drivers/dw9719/bind: No such device (os error 19)
So on this hardware the ACPI-declared VCM appears to be either absent, on a rail not described to Linux, or otherwise non-functional — while ACPI still advertises vcmtype = 4.

Working fix (as tested)
Adding a disable_vcm module parameter to ipu-bridge that ignores the ACPI-declared VCM:

static bool disable_vcm;
module_param(disable_vcm, bool, 0444);
MODULE_PARM_DESC(disable_vcm, "Ignore the ACPI-declared VCM (lens-focus) device.");
@@ ipu_bridge_parse_ssdb()

  • if (ssdb.vcmtype && disable_vcm) {
  •   dev_info(ADEV_DEV(adev),
    
  •   	 "disable_vcm set, ignoring VCM type %d\n", ssdb.vcmtype);
    
  •   ssdb.vcmtype = 0;
    
  • }
  • if (ssdb.vcmtype)
    sensor->vcm_type = ipu_vcm_types[ssdb.vcmtype - 1];
    Booting with ipu_bridge.disable_vcm=1 (the parameter must be applied on a cold boot — the bridge's software nodes and the VCM i2c client are intentionally kept across module reloads, so a rmmod/insmod cycle does not re-parse the SSDB):

acpi INT347A:00: disable_vcm set, ignoring VCM type 4
ipu3-cio2 0000:00:14.3: cio2_notifier_complete() entered
ipu3-cio2 0000:00:14.3: fwnode_links_to_pad port 1 ret=0
ipu3-cio2 0000:00:14.3: fwnode_links_to_pad port 0 ret=0
ipu3-cio2 0000:00:14.3: fwnode_links_to_pad port 2 ret=0
ipu3-cio2 0000:00:14.3: register_subdev_nodes ret=0
$ cam -l
1: Internal back camera (_SB_.PCI0.I2C3.CAMR)
2: Internal front camera (_SB_.PCI0.I2C2.CAMF)
Both capture correctly at 1280x720 NV12, ~30 fps. The rear camera is fixed-focus, which is the only functional loss.

Note that every v4l2_create_fwnode_links_to_pad() call returns 0 — the link creation itself was never the problem, only the fact that it was never reached.

Suggested direction
The disable_vcm parameter is a workaround, not a proper fix — it requires the user to know to set it, and it disables autofocus on hardware where the VCM does work. A better fix is probably one of:

Don't let a failed lens/flash subdev block the sensor's notifier. A missing autofocus motor is a degraded camera, not an absent one; blocking every camera on the system (including sensors on unrelated ports) is a severe failure mode for an optional, non-essential subdevice.
Make the VCM probe reliable, e.g. ensure dw9719 is loaded before ipu_bridge_instantiate_vcm_work() creates the client, so the probe happens inside the pm_runtime_get_sync() window. (This alone would not fix this machine, since the chip does not respond even when the sensor is forced on — but it may fix other devices failing the same way.)
Quirk out the VCM for affected DMI models, similar to the existing rotation quirks in ipu-bridge.c.
Happy to test patches or gather more data on this hardware.

Additional notes
Image quality is poor: libcamera has no tuning files for these sensors and falls back to uncalibrated.yaml (Configuration file 'ov8865.yaml' not found). Tracked separately in #1813.
The IR sensor (ov7251) gets a link but is not registered as a usable camera by libcamera — consistent with the wiki's "not working" status, and out of scope here.
Cheese cannot display these cameras (V4L2-only, and raw Bayer needs libcamera); GNOME Snapshot works.

Activity

  1. toor11 commented on Aug 9, 2026

    @toor11

    This is very likely the same root cause as the missing dw9719 i2c_device_id table, which was dropped from the driver in 6.19 as "unused" but is required on Surface devices where the IPU bridge instantiates the VCM client.

    Your own bind test is the tell: with no i2c_device_id table, i2c_match_id() has nothing to match against, so bind fails with ENODEV at the bus-match stage — probe never runs, and "the chip does not respond" is inferred from reading dw9719_probe(), not observed. The "nothing autoloads it" observation is the same root cause: the MODULE_DEVICE_TABLE(i2c, ...) entry is what generates the modalias that autoloads the driver when the client appears.

    The fix already exists upstream — Sakari Ailus's "media: i2c: dw9719: Add I2C device ID table" (re-added in mainline 7.1.6), with a pending linux-surface backport in #2123. Its commit message describes this exact scenario ("On Surface Book 2 ... prevents the ipu3-cio2 notifier from completing").

    Would you be able to test with that table added before settling on disable_vcm? If the VCM binds and probes inside ipu_bridge_instantiate_vcm_work()'s pm_runtime_get_sync() window, both cameras should come up with autofocus intact and no workaround needed.

  2. chunkynut commented on Aug 10, 2026

    @chunkynut
    Author

    Thanks for the response, you're right, my checks were wrong. Confirmed on 6.19.8-surface-3 that dw9719 has only of match table, so with a software node client named dw9719 there's nothing for i2c_device_match() to hit and bind_store() bails at the match stage. Probe never ran; I'd read the "chip doesn't respond" out of dw9719_probe() rather than checked it. Missing MODULE_DEVICE_TABLE(i2c, ...) explains the autoload gap too.

    Added the table locally and the enumeration half works exactly as you said — dw9719 autoloads, binds, probes inside the pm_runtime_get_sync() window, notifier completes with no workaround, and the lens entity shows up:

    • entity 43: dw9719 3-000c (0 pad, 0 link, 0 routes)
      type V4L2 subdev subtype Lens flags 0
      Catch is the rear camera then stops delivering frames — stalls with: ipu3-cio2 0000:00:14.3: payload length is 2585088, received 2588672
      Front is unaffected (~29fps both ways); rear goes from ~31fps to zero. Not mode selection — forcing -s width=1280,height=720 reproduces it identically. And libcamera's IPU3 handler doesn't expose a focus control anyway, so the autofocus isn't reachable from userspace here regardless.

    So the patch is clearly right and should land, it's just not sufficient on SB2 — trades "no cameras" for "front only". Sticking with disable_vcm=1 for now as it's the only config where both sensors capture.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions