Skip to content

load_goke: opt-in sensor_dvp / sensor_mclk gate for DVP-wired boards - #2276

Merged
widgetii merged 3 commits into
OpenIPC:masterfrom
bneigher:w7-dvp-sensor-gate
Aug 27, 2026
Merged

load_goke: opt-in sensor_dvp / sensor_mclk gate for DVP-wired boards#2276
widgetii merged 3 commits into
OpenIPC:masterfrom
bneigher:w7-dvp-sensor-gate

Conversation

@bneigher

Copy link
Copy Markdown
Contributor

Adds an opt-in env-var gate so DVP-wired boards can select the DVP pad
routing, without changing behaviour for anything that does not ask for it.

open_sys_config picks MIPI or DVP pad routing from its chip= and
g_cmos_yuv_flag= arguments. On a board that routes the sensor's parallel data,
sync, PCLK and i2c to the SoC's DVP pads, the MIPI arguments mux the i2c
controller to pads the sensor is not connected to — every address NACKs, no chip
ID is ever read, and no video is possible.

Per @widgetii's review note on #2074, this is keyed off an env
var rather than $CHIP_TYPE:

chip=gk7205v200 g_cmos_yuv_flag=1 is right for your W7 wiring but wrong for
MIPI-wired gk7202v300 boards, and there are some in the wild — a blanket change
would break them.

So a profile opts in with fw_setenv sensor_dvp 1. Unset — every existing board —
takes exactly the path it takes today.

sensor_mclk is gated the same way. open_sys_config reads MCLK only from its
module parameters and defaults to 27 MHz at this chip=, so a sensor init table
tuned for 24 MHz (the GC2053 ForCar tables are) runs against the wrong clock.
Worth noting for anyone who hits this: the .ini MCLK key is not consulted on
this path, so sweeping it produces identical results at every value — which is
indistinguishable from having ruled the cause out. That cost me a while.

Companion to the gk7202v300_lite_w7_8m device profile in OpenIPC/builder,
which is where the board-specific bring-up lives.

Verified on three GK-W7 boards (GK7202V300, 8 MB NOR, GC2053 in DVP mode with
SID strapped high): with sensor_dvp=1 and sensor_mclk=24 the i2c bus comes
up, the sensor answers at 7-bit 0x3f with chip ID 0x2053, and the pipeline
delivers 1920x1080 H.264 over RTSP at 25 fps with FrmErrCnt 0. With the vars
unset the script is byte-for-byte equivalent in behaviour to before.

sh -n clean.

Refs: #2074

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

load_goke: opt-in sensor_dvp/sensor_mclk gates for DVP-wired boards

🐞 Bug fix ✨ Enhancement ⚙️ Configuration changes 🕐 10-20 Minutes

Grey Divider

AI Description

• Add opt-in sensor_dvp=1 gate to force DVP pad routing without changing defaults.
• Add opt-in sensor_mclk=24 gate to override MCLK when .ini MCLK is ignored.
• Document why env-var gating is used instead of $CHIP_TYPE to avoid regressions.
Diagram

graph TD
  Profile["Device profile"] --> Env[("U-Boot env vars") ] --> Script["load_goke"] --> SysCfg["open_sys_config"] --> I2C["Sensor I2C bus"] --> Sensor["DVP sensor"]
  Script --> Mclk{"sensor_mclk=24?"} --> Devmem["devmem MCLK reg"]
  subgraph Legend
    direction LR
    _cfg["Config/Script"] ~~~ _env[("Env storage")] ~~~ _dec{"Decision"}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Auto-detect DVP vs MIPI via probe fallback
  • ➕ No per-board env configuration required
  • ➕ Can self-heal on misconfigured profiles
  • ➖ Risky/slow boot path (extra probe attempts/timeouts)
  • ➖ Detection may be ambiguous across sensors/boards
  • ➖ Harder to reason about regressions vs explicit opt-in
2. Key routing off `$CHIP_TYPE` or SoC table
  • ➕ Simple implementation; no tooling dependency on U-Boot env
  • ➖ Incorrect when multiple wirings exist for the same SoC (explicitly noted for gk7202v300)
  • ➖ High regression risk for existing MIPI-wired boards
3. Fix in `open_sys_config` to read `.ini` MCLK / add explicit DVP flag
  • ➕ Moves policy into the driver/module where the behavior originates
  • ➕ Avoids devmem register pokes from userland
  • ➖ Requires kernel/module changes and broader validation across boards
  • ➖ Longer turnaround; potentially larger blast radius

Recommendation: Keep the current env-var opt-in gates in load_goke: it’s the lowest-regression approach given mixed DVP/MIPI wiring on the same SoC family. If this pattern expands, consider a follow-up to make open_sys_config accept an explicit DVP/MCLK override (or consult .ini MCLK) to eliminate the devmem workaround.

Files changed (1) +23 / -0

Bug fix (1) +23 / -0
load_gokeAdd opt-in DVP routing and 24 MHz MCLK override via U-Boot env +23/-0

Add opt-in DVP routing and 24 MHz MCLK override via U-Boot env

• Introduces an opt-in 'sensor_dvp=1' gate that forces 'CHIP_TYPE=gk7205v200' and 'YUV_TYPE0=1' so 'open_sys_config' selects DVP pad routing on DVP-wired boards, while preserving default behavior when unset. Adds an opt-in 'sensor_mclk=24' gate to write the MCLK register via 'devmem' because this path does not consult the '.ini' MCLK key and defaults to 27 MHz.

general/package/goke-osdrv-gk7205v200/files/script/load_goke

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 16, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Silent MCLK write failure ✓ Resolved 🐞 Bug ☼ Reliability
Description
insert_ko adds a devmem write when sensor_mclk=24 but does not check whether devmem exists or
whether the write succeeded, so the script can continue with the wrong MCLK while giving no
diagnostic. This can make sensor bring-up fail with little/no signal in logs.
Code

general/package/goke-osdrv-gk7205v200/files/script/load_goke[R188-190]

+	case "$(fw_printenv -n sensor_mclk 2>/dev/null)" in
+		24) devmem 0x120100F0 32 0x0000000D ;;
+	esac
Evidence
The new MCLK path performs a raw devmem write with no || report_error handling, while the script
otherwise uses report_error for module load failures, indicating this omission is an introduced
reliability gap.

general/package/goke-osdrv-gk7205v200/files/script/load_goke[82-106]
general/package/goke-osdrv-gk7205v200/files/script/load_goke[179-190]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new `devmem`-based MCLK override is executed without checking for tool presence or write success. If the write fails, the script continues silently, leaving the system misconfigured.
### Issue Context
This is a shared boot-time module loader script; failures should be explicit to avoid hard-to-debug field breakage.
### Fix Focus Areas
- general/package/goke-osdrv-gk7205v200/files/script/load_goke[184-190]
### Suggested fix
- Check `command -v devmem` before use.
- Check `devmem` exit status; on failure, emit a `logger` message (daemon.err) and either `report_error` or explicitly continue with a warning (but do not fail silently).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. CHIP_TYPE role conflation ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The sensor_dvp opt-in block overwrites the detected CHIP_TYPE globally, conflating the actual SoC
identity with the value intended to steer open_sys_config routing. This makes later CHIP_TYPE-based
logic (present now or added later) harder to reason about and increases the risk of unintended
behavior changes when extending the script.
Code

general/package/goke-osdrv-gk7205v200/files/script/load_goke[R35-38]

+if [ "$(fw_printenv -n sensor_dvp 2>/dev/null)" = "1" ]; then
+	CHIP_TYPE=gk7205v200
+	YUV_TYPE0=1
+fi
Evidence
CHIP_TYPE is initially derived from ipcinfo --chip-name, then globally overwritten by the new
sensor_dvp gate, and subsequently reused as the chip= argument to open_sys_config (and as a
general chip discriminator elsewhere in the script).

general/package/goke-osdrv-gk7205v200/files/script/load_goke[4-10]
general/package/goke-osdrv-gk7205v200/files/script/load_goke[24-38]
general/package/goke-osdrv-gk7205v200/files/script/load_goke[109-115]
general/package/goke-osdrv-gk7205v200/files/script/load_goke[179-184]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`sensor_dvp=1` currently mutates `CHIP_TYPE` (the detected SoC name) for the remainder of the script. This mixes two concepts:
- detected SoC (used for chip-specific branching)
- desired `open_sys_config chip=` selector (used for pad routing selection)
### Issue Context
This script is shared across multiple Goke SoCs/boards; minimizing global state mutation reduces future regressions.
### Fix Focus Areas
- general/package/goke-osdrv-gk7205v200/files/script/load_goke[4-10]
- general/package/goke-osdrv-gk7205v200/files/script/load_goke[24-38]
- general/package/goke-osdrv-gk7205v200/files/script/load_goke[109-115]
- general/package/goke-osdrv-gk7205v200/files/script/load_goke[179-184]
### Suggested fix
- Introduce e.g. `SYS_CONFIG_CHIP="$CHIP_TYPE"` after detection.
- In the `sensor_dvp` block, set `SYS_CONFIG_CHIP=gk7205v200` (and keep `CHIP_TYPE` unchanged).
- Pass `chip=$SYS_CONFIG_CHIP` to `modprobe open_sys_config` in both `insert_detect` and `insert_ko`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can ask Qodo to dismiss a finding you disagree with, with your reason on record

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread general/package/goke-osdrv-gk7205v200/files/script/load_goke Outdated
Comment thread general/package/goke-osdrv-gk7205v200/files/script/load_goke
@bneigher

Copy link
Copy Markdown
Contributor Author

Thanks — both findings on this PR were fair and are now addressed in f67e5c2.

1. CHIP_TYPE role conflation. Agreed, and this one mattered more than it
looks. The opt-in was overwriting CHIP_TYPE, which is the detected SoC, with
the value wanted for open_sys_config's chip= pad-routing selector. Those are
two different things that merely coincide by default: a DVP board needs the
gk7205v200 routing tables while still being a gk7202v300. A dedicated
SYSCFG_CHIP now carries the selector, so CHIP_TYPE keeps meaning exactly one
thing and existing/future chip branches are untouched by this opt-in. That also
better serves @widgetii's original review note — the whole point is that boards
which do not set sensor_dvp are behaviourally identical to before.

2. Silent MCLK write. Also agreed, and this is precisely the failure mode
worth guarding: a wrong MCLK does not fail loudly, it produces a corrupted or
absent image while every log line still looks healthy. devmem presence and exit
status are both checked now, and either failure logs to daemon.err.

For context on why the MCLK hook exists at all: open_sys_config reads MCLK only
from its module parameters and defaults to 27 MHz at this chip=, while the
GC2053 ForCar PLL tables are 24 MHz values. The .ini MCLK key is never
consulted on this path — so sweeping it produces identical results at every
value, which is indistinguishable from having ruled the cause out. That cost me
a while, and it is the kind of thing worth having in the tree's history.

@widgetii widgetii left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay on this one — you did the hard part and then waited, which is the wrong way round.

I checked this against the pinned SDK source (openhisilicon @ 2d637e35, kernel/sys_config/sys_config.c) rather than just reading the diff. The approach is right and your diagnosis is exactly correct. Two changes I'd like before merge, then this goes in.

What checks out

The DVP lever is the only one that exists. pinmux() dispatches on chip=, and per kernel/compat/compat.h in the goke build C_HI3518EV300 is "gk7202v300". That branch is MIPI-only and ignores cmos_yuv_flag entirely — it unconditionally calls i2c0_for_mipi_sensor_pin_mux_gk7205v200(), sensor_cfg_for_mipi_sensor_mux(), vi_mipi_rx_mux(). There is no DVP path reachable for chip=gk7202v300 at any g_cmos_yuv_flag. Only the gk7205v200 branch honours cmos_yuv_flag == 1i2c0_for_vi_raw_sensor_pin_mux() + vi_raw_mode_mux_gk7205v200(). Your i2c-NACK symptom falls straight out of that. This isn't a hack, it's the only lever.

The SYSCFG_CHIP separation is provably as contained as you say. chip_list feeds only pinmux() and clkcfg() — and clkcfg()'s C_HI3518EV300 and C_HI3516EV200 branches write byte-identical values (0x00A0=0x00240400, 0x00F4=0x00110000, 0x00FC=0x8). So the swap moves pinmux and nothing else. Splitting it out of CHIP_TYPE was the right call.

The MCLK story is confirmed. parse_sensor_clock() returns 0x6 for gc2053, and the file's own table says 0x6: 27MHz. MCLK is sourced only from that sensor-name table, so the .ini key genuinely is never consulted on this path. Your note that sweeping it gives identical results at every value — indistinguishable from having ruled it out — is accurate and worth having in the history. Thanks for writing it down.

1. The devmem write is a blind store to a read-modify-write register

void sensor_clock_config(int index, unsigned int clock)
{
	reg_write32(clock << 2, 0xF << 2, reg_crg_base + 0x00F0);  /* RMW bits [5:2] */
}

reg_crg_base = ioremap(0x12010000), so 0x120100F0 is the right address, and the sns0 select field is bits [5:2] only.

devmem 0x120100F0 32 0x0000000D stores the whole word. Decomposed: 0xD = 0b1101 → bits[5:2] = 0b0011 = 0x3, and the table reads 0x3: 24MHz — so you are selecting the clock you intend, which is why it works on your three boards. But the same store also sets bit 0 and forces bits [31:6] to zero, and nothing in the driver ever writes that register wholesale.

Could you mask to the field instead? It also removes the magic constant and makes the var extensible to the other seven values:

# CRG PERI_CRG60 @ 0x120100F0, sns0 clock select = bits [5:2].
# 0x0:74.25 0x1:72 0x2:54 0x3:24 0x4:37.125 0x5:36 0x6:27 0x7:12 (MHz)
# open_sys_config's sensor_clock_config() read-modify-writes just this
# field -- match that rather than storing the whole word.
cur=$(devmem 0x120100F0)
devmem 0x120100F0 32 $(( (cur & ~0x3C) | (3 << 2) ))

While you're there — add a *) arm that logs a non-empty unsupported value. Right now sensor_mclk=37 does nothing silently, which is the same failure class the rest of this PR is guarding against.

2. sensor_dvp=1 shouldn't unconditionally force gk7205v200

gk7205v300 has its own DVP path (vi_raw_mode_mux_gk7205v300() under cmos_yuv_flag == 1). A DVP-wired v300 that sets sensor_dvp=1 would get pushed onto the v200 VI pad table — the same bug this PR fixes, pointing the other way. Only gk7202v300 lacks a DVP branch and needs to borrow:

if [ "$(fw_printenv -n sensor_dvp 2>/dev/null)" = "1" ]; then
	YUV_TYPE0=1
	# gk7202v300's pinmux() branch is MIPI-only -- it ignores g_cmos_yuv_flag.
	# Borrow gk7205v200's tables (same die family, and they do honour it).
	# gk7205v200/v300 already have their own DVP paths.
	if [ "$CHIP_TYPE" = "gk7202v300" ]; then
		SYSCFG_CHIP=gk7205v200
	fi
fi

Minor

  • logger -p daemon.err -t load_goke — the file's convention is logger -s -p ... -t goke (lines 356, 360, 366, 377). The -s matters here: a failed MCLK write is bring-up-fatal and should reach the console the way "SENSOR is not detected" does.
  • The command -v devmem guard is harmless, but CONFIG_DEVMEM=y is set in general/package/busybox/busybox.config, so it is always present. Keep it if you prefer belt and braces.
  • Asymmetry worth a comment rather than a change: sensor_dvp applies to both insert_detect and insert_ko, but sensor_mclk only to insert_ko, so first-boot autodetect probes at the default clock. Probably fine since i2c is master-clocked, but it will surprise someone eventually.

Before this can merge

  • CI has never run on this branch — fork PRs need workflow approval, so the umbrella gate has nothing to go on. I'll approve the run once you push.
  • The branch is behind master; please rebase.

The firmware/builder split is right — generic mechanism here, board profile in OpenIPC/builder#119. I'll pick that one up next.

@bneigher

Copy link
Copy Markdown
Contributor Author

Thanks for going to the SDK source rather than the diff — that turned a "looks
plausible" review into a checkable one, and both findings were right. Pushed in
1084143.

1. The MCLK write

You're right, and the way it was wrong is worth stating: 0x0000000D selects the
correct clock by accident of the constant, not by writing the field.
Decomposed, bits [5:2] are 0x3 = 24 MHz — but the same store also cleared bits
[31:6] and set bit 0, which sensor_clock_config() never does. It worked on
three boards, which is exactly how a blind store survives review.

Now masked to the field the driver actually touches:

mclk_cur="$(devmem 0x120100F0)"
devmem 0x120100F0 32 $(( (mclk_cur & ~0x3C) | (mclk_sel << 2) ))

I also took your suggestion to make it extensible and filled in the whole table
(74.25/72/54/24/37.125/36/27/12), with a *) arm that logs. You were right that
sensor_mclk=37 silently doing nothing was the same failure class this PR is
supposed to be guarding against — it was inconsistent to guard the write and not
the value.

2. sensor_dvp forcing gk7205v200

This one I should have caught. I generalised a fix from the only board I had.
Only gk7202v300 lacks a DVP branch and needs to borrow; a DVP-wired gk7205v300
setting sensor_dvp=1 would have been pushed onto the v200 VI pad table — the
same bug, mirrored, and harder to diagnose because the flag would look correct.
Now gated on CHIP_TYPE = gk7202v300.

Minor

  • logger -s -p ... -t goke throughout, per the file's convention. Agreed on
    -s: a failed MCLK write is bring-up fatal and should reach the console the
    way "SENSOR is not detected" does.
  • Kept the command -v devmem guard. CONFIG_DEVMEM=y so it is redundant, but
    the read is now load-bearing too, and I would rather a missing tool say so than
    produce an empty $mclk_cur and a nonsense arithmetic expansion.
  • Documented the insert_detect / insert_ko asymmetry in a comment rather than
    changing it, as you suggested — autodetect probes at the sensor table's default
    clock, which is fine because i2c is master-clocked, but now it is written down.

One thing I have not verified

The masked write is correct by inspection and sh -n clean, but it is not yet
tested on hardware
— my three boards are running the previous whole-word store.
I will flash and confirm the sensor still comes up before this merges, and report
back either way. Given the failure mode here is a silently corrupted image rather
than a loud error, I would rather not have "it compiles" standing in for "it
works".

open_sys_config picks MIPI or DVP pad routing from its chip= and
g_cmos_yuv_flag= arguments. On a board that wires the sensor to the DVP pads,
the MIPI arguments mux the i2c controller to pads the sensor is not connected
to, so every address NACKs and no sensor is ever detected.

Gated on an env var rather than $CHIP_TYPE deliberately: both wirings exist on
gk7202v300, so keying this off the SoC name would fix DVP boards by breaking
every MIPI one. Profiles opt in with 'fw_setenv sensor_dvp 1'; unset means the
current behaviour is unchanged.

sensor_mclk is gated the same way. open_sys_config reads MCLK only from its
module parameters and defaults to 27 MHz at this chip=, so a sensor init table
tuned for 24 MHz runs against the wrong clock -- and the ini's MCLK key is never
consulted on this path, which makes sweeping it look like a ruled-out cause.

Refs: OpenIPC#2074
…rite MCLK blind

Two findings from review:

1. CHIP_TYPE role conflation. The opt-in was overwriting CHIP_TYPE, which is the
   DETECTED SoC, with the value wanted for open_sys_config's chip= pad-routing
   selector. Those are two different things that happen to coincide by default:
   a DVP board needs the gk7205v200 routing tables while still BEING a
   gk7202v300. Conflating them makes every existing and future CHIP_TYPE branch
   harder to reason about. Now a dedicated SYSCFG_CHIP carries the selector and
   CHIP_TYPE keeps meaning exactly one thing.

2. Silent MCLK write. devmem was called without checking that it exists or that
   the write succeeded. A wrong MCLK does not fail loudly — it yields a corrupted
   or absent image while every log line still looks healthy — so this is exactly
   the case that must not pass quietly. Both failures now log to daemon.err.

Still opt-in and still keyed off env vars: with sensor_dvp unset the script is
behaviourally identical to before.

Refs: OpenIPC#2074
Addresses @widgetii's review, which checked this against the pinned SDK source
rather than the diff. Both findings were correct.

1. The devmem write was a blind whole-word store to a register the driver only
   ever read-modify-writes. sensor_clock_config() is
   reg_write32(clock << 2, 0xF << 2, base+0xF0) — bits [5:2] and nothing else.
   0x0000000D happens to put 0x3 (24MHz) in that field, so it worked, but it also
   cleared bits [31:6] and set bit 0. Now masks: (cur & ~0x3C) | (sel << 2).

   Also extended to the full documented table (74.25/72/54/24/37.125/36/27/12)
   instead of a lone magic constant, and an unsupported value now logs instead of
   doing nothing silently — that was the same failure class the rest of this PR
   guards against.

2. sensor_dvp=1 no longer forces gk7205v200 unconditionally. Only gk7202v300
   lacks a DVP branch: its pinmux() case is MIPI-only and ignores
   g_cmos_yuv_flag entirely. gk7205v200 and gk7205v300 have their own DVP paths,
   and pushing a DVP-wired v300 onto the v200 VI pad table would be this very bug
   pointing the other way.

Minor, also from review: logger now follows the file's convention
(logger -s -p ... -t goke). The -s matters — a failed MCLK write is bring-up
fatal and should reach the console the way 'SENSOR is not detected' does. The
command -v devmem guard is kept as belt and braces even though CONFIG_DEVMEM=y,
since the read is now load-bearing too.

The insert_detect/insert_ko asymmetry is documented rather than changed:
first-boot autodetect probes at the sensor table's default clock, which is fine
because i2c is master-clocked, but it is the kind of thing that surprises people.

Refs: OpenIPC#2074
@widgetii
widgetii force-pushed the w7-dvp-sensor-gate branch from 1084143 to aa6c983 Compare August 27, 2026 16:36
@widgetii

Copy link
Copy Markdown
Member

Heads up: I rebased this branch onto current master and force-pushed it1084143aa6c983. Please git fetch && git reset --hard origin/w7-dvp-sensor-gate (or re-pull your fork) before you flash, so your hardware test runs against what will actually merge rather than a base that was 36 commits behind.

Nothing in your change was altered: the three commits replayed cleanly (master hadn't touched load_goke since your fork point), and the resulting file is byte-identical to what you pushed. Still +79/-2 in one file.

I also approved the CI runs — that was the reason this PR had never had a check on it, and it was my hold-up, not yours.

On the masked write

I checked the arithmetic rather than eyeballing it, and it holds. In both dash and busybox ash, cur=0x00000861 with mclk_sel=3 gives 0x0000084D — bits [5:2] = 0x3, and bits 0, 6 and 11 preserved.

Two ways that expression could have silently broken, both of which happen to be fine, but worth recording since they are invisible failure modes of exactly the kind this PR is about:

  • busybox devmem prints "0x%0*llX" (miscutils/devmem.c:126), so the 0x prefix is there and $(( )) parses it as hex. Had it printed bare digits, a reading like 00000741 would have been taken as octal and you would have written the wrong clock — silently, with a healthy-looking log.
  • Its default read width is 8 * sizeof(int) = 32 (devmem.c:35), matching your explicit 32 on the write, and the write value goes through bb_strtoull(..., 0), so the decimal output of $(( )) is read back correctly.

Your mclk_sel="" sentinel is also right — it distinguishes "unsupported value" from sel=0 (74.25 MHz), which a -z-on-zero check would have conflated.

Agreed on keeping the command -v devmem guard now that the read is load-bearing; an empty $mclk_cur feeding the arithmetic expansion is a worse failure than a clear message.

Where this stands

The review is still marked changes-requested, and I am leaving it that way until you confirm on hardware — for the reason you gave yourself, which was the right instinct: the failure mode here is a silently corrupted image, so "it compiles" should not stand in for "it works". Report back either way and I will clear it and merge.

Thanks for the turnaround, and sorry again for the nine days of silence before it.

@widgetii

Copy link
Copy Markdown
Member

Rebased head is green across all six workflows, and the matrix picked exactly the right set — all seven Goke boards that use this load_goke built: gk7202v300_lite/ultimate, gk7205v200_lite/ultimate, gk7205v300_lite/ultimate, gk7605v100_lite.

Since a build cannot catch a wrong MCLK, I verified the two things a build can't, so you aren't the only evidence for this.

1. The register layout, confirmed on hardware

I read 0x120100F0 on a lab hi3516ev300 — which is the same sys_config.c and the same CRG as this family, just with the HiSilicon names bound in compat.h instead of the Goke ones (C_HI3516EV300 is hi3516ev300 there and gk7205v300 here):

0x120100F0 = 0x00000011      sensor: imx335
  bits [5:2] = 0x4
  bit0       = 1
  bits[31:6] = 0

parse_sensor_clock("imx335") returns 0x4 in the driver, and the table reads 0x4: 37.125MHz. So the field really is bits [5:2] at that address, and the encoding is what we both read it to be. That is now measured rather than inferred.

I also ran your expression in that board's own busybox ash against the live register value, and exercised the write path with a value equal to what was already there — a true no-op, readback identical, majestic undisturbed:

sel=0 -> 0x00000001    sel=4 -> 0x00000011
sel=3 -> 0x0000000D    sel=6 -> 0x00000019
                       sel=7 -> 0x0000001D

Worth knowing: sel=3 computes to 0x0000000D — precisely the constant the first version hardcoded. On this silicon everything above bit 5 and bit 1 reads zero, so the blind whole-word store and the correct read-modify-write coincide. That is why your three boards worked and why nothing looked wrong. The masked form is still the right one, because it is correct by construction rather than by a property of the register that no one had checked; but it also means your existing hardware validation very likely still stands — the new code computes the same word the old code wrote, unless the W7's register has bits set above bit 5, in which case the masked version is the more correct of the two.

2. The no-op path, proven by execution trace

The gate's safety claim is that a board with neither var set behaves exactly as before. That is a claim about the sequence of commands the script runs, so I asserted it that way: stub every external onto PATH, log each argv, run the base and the candidate with identical inputs, diff the traces.

Byte-identical across 4 chips × 3 sensors, plus the first-boot autodetect path (insert_detect/remove_detect) and the -r removal path. Then the opt-in routing and the MCLK masking on top — 39 checks.

The important part: I ran the same harness against f67e5c2, the version before the review, as a negative control. It fails 13 checks — catching both the unconditional gk7205v200 force and the whole-word write. So the harness bites; it is not passing everything I point it at.

I would like to add this as .github/scripts/test_load_goke.sh alongside the existing test_load_hisilicon.sh, so the no-op guarantee is enforced in CI rather than resting on this thread. Happy to send it as a separate PR after this merges, or push it here if you would rather it land together — your call, it is your branch.

Where that leaves things

Both review findings are addressed and independently verified. The one thing neither CI nor I can reach is the DVP pinmux actually bringing up the GC2053 on a DVP-wired gk7202v300 — the lab board is MIPI-wired with an imx335, so it is the twin of a gk7205v300, not of your W7.

Given the above I am clearing the changes-requested. If your flash comes back clean, say so and I will merge; if you would rather I merge now on the strength of the equivalence above, that is reasonable too, since the opt-in path is inert for every board that does not set the vars.

@widgetii
widgetii dismissed their stale review August 27, 2026 17:09

Both findings addressed in 1084143 and independently verified: register layout measured on lab hardware, no-op path proven by execution-trace equivalence with a working negative control. Holding merge only on the author's DVP bring-up test.

@widgetii
widgetii merged commit fc923b7 into OpenIPC:master Aug 27, 2026
23 checks passed
@bneigher

Copy link
Copy Markdown
Contributor Author

Hardware verification, as promised. Flashed to a GK7202V300 (OpenIPC nightly
base, kernel 4.9.37, U-Boot 2016.11-gd5c37e8). Sensor comes up: i2c init failures
0, ISP errors 0, 1920×1080 @ 25 fps.

The interesting part is that the boards had more to say than "it works".

The reset value. Read from the U-Boot prompt, before Linux touches anything:

OpenIPC # md.l 0x120100f0 1
120100f0: 00000000

(I dumped 0x12010000 16 first to confirm md was reading live registers and not
a dead window — that block is full of varied non-zero content, so the zero here is
real.)

What the write actually sees. I then instrumented load_goke to log the
register immediately before the write, and rebooted:

goke: NCAM-DEBUG pre-write reg=0x00000019

Decoded, that is:

bits value meaning
[5:2] 0x6 27 MHz — the chip default
bit 0 1 already set, by the driver, before load_goke runs
[31:6] 0 nothing above the field on this SoC

Two things fall out of that.

First, it independently confirms the table in the patch: the hardware sits at
sel=6 and the documented default for this chip is 27 MHz, so entry 6 → 27 MHz is
right. I had transcribed that table rather than measured it.

Second, and more to your original point: the register is already
driver-configured by the time this code runs.
The old whole-word store of
0x0000000D was therefore not writing into a blank register — it was overwriting
live state with a constant. On this SoC the final value is identical either way
((0x19 & ~0x3C) | 0xC == 0xD), but only because bit 0 happened to already be 1
and bits [31:6] happened to be 0. Neither is something the script knew or checked.
That is a sharper version of what you flagged, and it only showed up on hardware.

What I did not verify. My board is a gk7202v300, so it still takes the
borrow-gk7205v200-tables path — the sensor_dvp change is exercised in the sense
that the gate evaluates true, but a DVP-wired gk7205v200/v300 taking its own path
is untested by me. If anyone has one, that is the case worth a second pair of eyes.

bneigher added a commit to bneigher/builder that referenced this pull request Aug 27, 2026
Two review items from @widgetii and @flyrouter.

1. The profile never set sensor_mclk. This was a real hole, not a doc mismatch:
   the PR description claimed sensor_dvp=1 AND sensor_mclk=24, but only the first
   was in customizer.sh, so a board flashed from this profile would have come up
   at 27 MHz against 24 MHz init tables — the silent-corruption failure the
   firmware PR exists to prevent. My bench boards had sensor_mclk set by hand,
   which is exactly why the gap did not show.

   Confirmed against hardware rather than re-reading the code: PERI_CRG60 reads
   0x00000019 at the moment load_goke writes it, i.e. bits [5:2] = 0x6 = 27 MHz,
   matching parse_sensor_clock()'s gc2053 -> 0x6 mapping. The ini cannot fix this
   — MCLK is a CRG clock programmed before majestic reads any ini.

2. Renamed gk7202v300_lite_w7_8m -> gk7202v300_lite_generic-w7, per the
   documented <soc>_<flavor>_<vendor>-<model> shape. "gk-w7" is dropped: GK is
   Goke, the SoC vendor, already carried by gk7202v300 — flyrouter's doubt that
   it names the board vendor was right, it does not. "generic" is the honest
   token for a board with no identifiable vendor, and is already established
   here (gk7205v200_otg_generic, ssc30kq_rubyfpv_generic). _8m dropped since
   flash size is BR2_OPENIPC_FLASH_SIZE.

   The excludes list stays gk7202v300_lite.list — that name is keyed to
   <soc>_<flavor>, not to the profile, matching every other profile in the tree.

The inert ini Data_seq field is left as-is per review.

Refs: OpenIPC/firmware#2276
@widgetii

Copy link
Copy Markdown
Member

Your pre-write reg=0x00000019 is the most useful number in this thread, and it holds up independently of everything else below.

Set beside what I read on the lab board:

board sensor register bits [5:2] parse_sensor_clock() table
gk7202v300 (yours) gc2053 0x00000019 0x6 0x6 27 MHz
hi3516ev300 (lab) imx335 0x00000011 0x4 0x4 37.125 MHz

Two different SoCs, two different sensors, two different table entries, both matching — with bit 0 set and bits [31:6] clear in both cases. Neither of us had that alone. Your U-Boot md.l read of 00000000 at reset adds the piece I couldn't get: bit 0 is set by the driver between reset and load_goke, not by the bootloader. That makes your point sharper than mine was — the old whole-word store wasn't writing into a blank register, it was overwriting live driver state with a constant, and the two only agreed because bit 0 happened to already be 1 and nothing lived above bit 5.

One correction to the record

You posted a correction on OpenIPC/builder#119 sixteen minutes after this comment: your boards run a forked load_goke that hardcodes the DVP branch and the 24 MHz write and never reads the env. I don't think you'd traced that back to here, so — the 1920×1080 @ 25 fps, i2c init failures 0 result above is evidence your fork works. It isn't evidence the merged script does.

The timing agrees with your correction rather than with the original: this merged at 17:11 and no nightly built from it existed by 17:35, and a board on stock pre-merge load_goke couldn't have brought up a DVP-wired sensor at all, because the gate didn't exist. So the script behind that 1080p25 was necessarily yours.

Flagging it because this is already merged and I'd rather the thread not read as end-to-end validation it isn't. It doesn't change the merge — that went in on the lab register measurement, the trace-equivalence harness with its negative control, and CI green across all seven Goke boards, none of which route through your bench. And it doesn't touch the register measurements above, which are reads of hardware and don't care which script logged them.

What's still open is the same gap you identified yourself: merged load_goke, DVP board, env set by the profile rather than by hand. Builder#119 now has CI running for the first time — I approved the runs — so the artifact for that test is about to exist.

The case neither of us can reach

Agreed on the DVP-wired gk7205v200/v300 path. Your board is a gk7202v300, so it exercises the borrow branch; the case where a chip keeps its own chip= and takes its own DVP routing has no hardware behind it from either of us. The lab board is MIPI-wired, so it can't stand in.

It is covered by the trace harness at the level of "the right arguments reach open_sys_config" — that's what caught the original unconditional force — but not at the level of "those arguments produce video". If anyone reading has a DVP-wired v200 or v300, that's the one worth a report.

Thanks for the instrumented reboot, and for the correction. Both made the record more accurate than it would have been.

@bneigher

Copy link
Copy Markdown
Contributor Author

Correcting my "Hardware verification" comment above. @widgetii is right, and the
error is mine: I presented a result from a forked load_goke as verification
of the merged one.

Concretely, our fork hardcodes the DVP branch behind [ "$CHIP_TYPE" = "gk7202v300" ]
and writes MCLK behind an internal flag. It never calls fw_printenv. So the
merged script's actual gate — [ "$(fw_printenv -n sensor_dvp)" = "1" ] — did not
execute on that board at all.

That makes one line of my comment worse than merely unsupported. I wrote that the
sensor_dvp change "is exercised in the sense that the gate evaluates true". It
was not exercised in any sense. The gate was not present in the script that ran,
and I would have caught that by reading the file I flashed instead of the file I
had written.

What does not survive: 1920×1080 @ 25 fps, i2c init failures 0, ISP errors
0, as evidence for this PR. That is evidence our fork works, which was never in
question.

What does survive, because it is a register read and does not care which script
emitted it:

  • md.l 0x120100f0 = 0x00000000 at the U-Boot prompt (reset state), with the
    surrounding CRG block dumped first to prove md was reading live registers.
  • pre-write reg=0x00000019 — bits [5:2] = 0x6, taken after open_sys_config
    configured the register and before anything overwrote it.

Your lab board reading 0x00000011 with bits [5:2] = 0x4 for imx335 is a better
cross-check than mine, because it moves both the SoC and the sensor and still lands
exactly where parse_sensor_clock() says. Two boards, two sensors, two different
table entries, bit 0 set and bits [31:6] clear in both. I had one point and called
it confirmation; two points that disagree in the right way is confirmation.

The outstanding test is unchanged and now has a defined shape: stock upstream
load_goke, sensor_dvp and sensor_mclk cleared from U-Boot env, flashed from
the CI artifact on builder#119, so the profile is the only thing that sets them.
Our boards currently fail both preconditions — they run the fork, and I set those
vars by hand this afternoon. I will clear them and report on the builder PR.

Thank you for joining the two threads up. I had posted the correction on #119 and
not carried it back to here, which left the more flattering version standing in the
record of the PR that actually merged.

widgetii pushed a commit to OpenIPC/builder that referenced this pull request Aug 29, 2026
…6006C Wi-Fi) (#119)

GK-W7 board (product string ipc533331a-W7-gc2053dvp-f8): GK7202V300, 8MB NOR,
GC2053 wired in DVP/parallel mode with the SID strap high, iComm/SSV SSV6006C USB
WiFi (8065:6000), no Ethernet PHY.

Differs from the generic gk7202v300_lite target in two ways it cannot express
without a profile. The sensor is on the DVP pads, so open_sys_config's MIPI
arguments mux i2c to pads nothing is connected to and no sensor is ever detected;
customizer.sh opts into the sensor_dvp / sensor_mclk gate added in
OpenIPC/firmware#2276. And the Lite target ships only mt7601u, which will never
bind an SSV part, so the defconfig selects BR2_PACKAGE_SSV635X_OPENIPC and the
overlay adds the ssv6x5x-generic wlandev branch.

Named generic-w7 rather than after a vendor: the board carries no identifiable
vendor marking, and "gk" is Goke -- the SoC vendor, already encoded in the soc
field -- not the board's. `generic` is the established token here
(gk7205v200_otg_generic, ssc30kq_rubyfpv_generic).

Verified: builds green, rootfs 5017600B with 220KB of headroom in the 5120KB
partition; the baked load_goke is master's verbatim (md5 confirmed after
normalising the build's comment stripping); customizer.sh carries both env vars
and is sh/dash/ash clean; S30customizer runs it on first boot ahead of S70vendor,
so the vars are set before load_goke reads them; and on hardware, master's
load_goke driven only by those two vars brings up the DVP GC2053 at 1920x1080
@ 25 fps with i2c init failures 0 and ISP errors 0.

Not verified: the conjunction of the above on a single unattended first boot from
this image with no hand-set environment. Each link is independently evidenced;
the composition is not. Follow-up expected on the PR.

Headroom note for later: 220KB of rootfs headroom is about 4% of the partition,
and this family has a history of profiles going over as majestic grows.
gk7202v300_lite.list is the lever.

Refs: OpenIPC/firmware#2074, OpenIPC/firmware#2276
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants