Why this fix takes the shape it does. Background reading for anyone debugging mainline CAMSS on SM7125 or porting to a sibling Atoll SoC.
drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c in the sm7125-mainline/linux tree ships a 5-lane × 23-entry table labeled /* GEN2 1.2.2 2PH */. The label is correct (SM7125 is v1.2.2). The contents are a botched hybrid:
- Lane 0 first six entries hit
0x0900..0x0910— those are 3-phase / combo-mode register offsets, not 2PH. - Lane 0 entry for register
0x0000writes value0x91. The downstream Qualcomm reference (cam_csiphy_1_2_2_hwreg.h::csiphy_2ph_v1_2_2_reg[0]) writes0xD4there. - Per-lane base offset for v1.2.2 is
0x200; the upstream table uses0x100spacing (SC7180-class). Lane 1's writes therefore hit lane 0's bank etc. - Several common-control writes the v1.2.2 silicon requires before per-lane programming are missing entirely.
Net effect: CSIPHY appears to bring up — s_stream(1) returns 0, sensor power-on completes, CCI register writes flow, CSIPHY IRQ counter stays at 0 (error-only IRQs, no errors fired) — but no MIPI packets reach CSID. CSID0 IRQ counter stays at 0 throughout VIDIOC_STREAMON, capture file is 0 bytes, dmesg is silent.
Downstream arch/arm64/boot/dts/qcom/atoll-camera.dtsi declares compatible = "qcom,csiphy-v1.2.2" for all four CSIPHYs. The downstream cam_csiphy_soc.c driver maps that compatible to cam_csiphy_1_2_2_hwreg.h + CSI_3PHASE_HW_12.
Upstream's csiphy_hw_version_read uses the SHOW_REV_ID strobe trick: write BIT(1) to CTRLn(6) = 0x818, then read 4 bytes from STATUSn(12..15) = 0x8e0..0x8ec and compose. On joyeuse:
qcom-camss acb3000.camss: CSIPHY 3PH HW Version = 0x40010000
The 4 bytes are a Qualcomm vendor composite, not a packed {gen, rev, stepping} word — upstream dev_dbgs the raw value without decoding. Bytes [0x00, 0x00, 0x01, 0x40] correspond to v1.2.2 silicon when cross-referenced with downstream rev-ID readbacks.
A clean upstream-shape patch would:
- Add
enum camss_version { ..., CAMSS_7125 }incamss.h - Add
lane_regs_sm7125[5][19]separately fromlane_regs_sc7180 - Add
sm7125_resourcesstruct reusing SC7180 csiphy/csid/vfe/icc sub-resources - Add
{ .compatible = "qcom,sm7125-camss", .data = &sm7125_resources }tocamss_dt_match[] - Override the camss
compatibleinsm7125.dtsi(which currently inherits SC7180's via#include)
That's a DT change and a .h enum change. Per the project's constraint of no /boot writes and no kernel-image rebuild (the goal is a .ko swap, not a fresh kernel + DTB flash), the DT change is non-starter for an on-device fix.
This patch instead hijacks the SC7180 case: replace lane_regs_sc7180[][] body in-place with the v1.2.2 content. pmaports/device/testing/linux-postmarketos-qcom-sm7125 is SM7125-only, so no SC7180-class device consumes this .ko. The upstream-friendly form is the path for linux-media@vger.kernel.org; the hijack form is the path for the running phone.
Patch 2 adds two writes to csiphy_lanes_enable:
writel_relaxed(0x02, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0)); /* v1.2.2 PHY-enable */
writel_relaxed(0x01, csiphy->base + 0x0884); /* CTRLn(33) kickoff */Both come from downstream csiphy_common_reg_1_2_2[5]. Upstream's csiphy_lanes_enable matches 3 of the 5 downstream common writes by computing the equivalent value at runtime (CTRL5 lane mask, CTRL6 PWRDN_B, CTRL7 = 0x02). The other 2 are missing:
CTRL0 = 0x02(downstream writes0x02; upstream writes0x00). On v1.2.2 this is the top-level PHY-enable bit. With0x00, the lane table loads but the PHY top stays in standby.CTRLn(33) = 0x01. The lane-config-done strobe. Latches the preceding writes onto the analog plane. Without it, even with PHY-enable set, MIPI symbol lock doesn't fire.
Both must land in the same csiphy_lanes_enable call. Order matters: lane table walk first, then these two writes, then the IRQ-mask loop.
v1.0/v1.1 PHYs (sdm845, sc7180, sc8280xp class) use 0x100 per-lane spacing in one register cluster. v1.2.x PHYs (sm6350 = v1.2.3, atoll = v1.2.2, sm6125/sm6150 = v1.2) use 0x200 spacing with an auxiliary register cluster at 0x900+. Programming a v1.2.2 PHY with v1.0/v1.1 register addresses hits the wrong fields (or unmapped MMIO regions that return 0).
The PHY-rev mapping per public source:
| SoC | upstream lane_regs_* |
Rev (per downstream) |
|---|---|---|
| SDM845 | lane_regs_sdm845 |
v1.0 |
| SC7180 | lane_regs_sc7180 (annotated 1.2.2 but contents wrong) |
v1.1 (?) |
| SC8280XP | lane_regs_sc8280xp |
v1.1 |
| SM8250 / SC7280 | lane_regs_sm8250 |
unlabelled |
| SM6350 | (in-flight v4) | v1.2.3 |
| SM7125 (Atoll) | THIS PATCH | v1.2.2 |
- Hot-reload IRQ leak:
modprobe -r qcom-camss; modprobe qcom-camssfails at probe witherror -ENXIO: IRQ csiphy0 not found. Reproduces with the unpatched original.kotoo. Likely a missedplatform_irq_countreset or per-CSIPHY IRQ resource state incamss_remove(). Workaround: cold boot only. - SMMU sticky-fault (Bug C):
vfe_wm_startenables the VFE write master at STREAMON, butWM_IMAGE_ADDRis programmed in a separatevfe_wm_updatecall afterwards in a tight loop over pending buffers. If the pending queue is empty at STREAMON instant, WM goes live with stale or zeroWM_IMAGE_ADDR. First DMA hits unmapped iova → SMMU SID=0x820 fault → camera black until reboot. Combined with the pre-existingvfe_flush_buffersuse-after-vb2_buffer_done(does not NULLoutput->buf[0/1]), the second-session window is widened. Fix is a two-line move of theWM_CFG_ENwrite fromvfe_wm_startintovfe_wm_update. Planned for v0.2.0. - libcamera tuning: no
ov16a1q.yamlors5k5e9.yamlupstream. Stack falls back touncalibrated.yaml+ SoftwareIsp. Preview works but colour calibration is wrong.