Skip to content

anyka: name every generation's part, report media memory, detect the sensor - #273

Merged
dimerr merged 8 commits into
masterfrom
anyka-detect
Nov 8, 2026
Merged

dimerr merged 8 commits into
masterfrom
anyka-detect

Conversation

@dimerr

@dimerr dimerr commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

Anyka: name every generation's part, report media memory, and detect the sensor. Plus two new sensor families and SC2145H without a revision suffix.

Chip

  • Device tree first. A root compatible naming the part (anyka,ak3918ev200, anyka,ak3919ev330) wins. OpenIPC's kernels and the EV330 vendor kernels have one.
  • Board string next. It often names only the package: AK3918E80PIN is an EV200 on one board (Cloud39EV2_…) and an EV300 on another (Cloud39EV3_…), and a V380E2 says Cloud39E_AK3918E+H42. The chip ID's generation completes the name.
  • Shared chip ID. 0x20120100 is both the Aimer39 AK3918's and some EV200s'. A board string or device tree saying Aimer39 puts it in its own family, ak3918.
  • AK3918AV130. Its ID is read at 0x080003fc (0x565735xx).
  • EV330 fallback. An EV330 named by neither the device tree nor the board string reports AK39XXEV330, as the vendor CPU banner does. The board string's AK39EV330 is not a part.
  • Naming check. Every name was checked against vendor SDKs, OpenIPC's SoC list and field reports. No part name is invented.

Memory

OpenIPC's kernels take mem= alone, so without memsize= the total is the device tree's /memory and media is what the kernel did not get.

Sensor

Anyka sensors answer I2C only while the camera driver has them clocked and out of reset, and the drivers power them down after their own probe. On EV200/EV300, hal_enable_sensor_clock now does what the drivers do:

  • starts the CIS clock (SYSCTRL 0x18 from the peripheral PLL, 24 MHz);
  • pulses the reset/power-down GPIOs the device tree names for the camera.

hal_cleanup then puts back exactly what it changed. When the clock is already running a pipeline is, and nothing is touched.

The address table gains OV9712 at 0x60, GC1024 at 0x78, the EV330 GC4653 at 0x52, SmartSens at 0x64, ImageDesign, PrimeSensor and BYD.

Sensors

  • New families: PrimeSensor PS52xx (bank 0 ID via 0xef, restored) and BYD BF3703.
  • 0x1245 reports SC2145H. The _A/_B suffix came from 0x3020, a silicon revision the Anyka driver uses to pick its init variant, not a different part. sensors_golden.txt is updated.

Tested

On an AK3918EV200 (OpenIPC firmware, SC1345):

before                          after
chip model: AK3918E80PIN        chip model: AK3918EV200
ram total: 64M                  ram total: 64M, media: 24M
No sensor detected              sensors: SmartSens SC1345, i2c bus 0, addr 0x60
ipcinfo -c: ak3918e80pin        ipcinfo -c: ak3918ev200, -s: sc1345

The CIS clock and GPIO registers read the same before and after a probe (0x08000018=0x00106231, GPIO dir 0x010000C0, out 0x00000001). With a capture streaming, the clock is running, ipctool reads the sensor without touching anything, and the stream keeps 15 fps.

The naming for the other generations is covered by anyka_test, with inputs from real boot logs: gwell and V380E2 EV200, an AK3916EV301, and EV330 vendor firmware, booted under qemu-hisilicon. Those generations were not run on hardware. sensors_test passes.

Size

ipcinfo grows 728 bytes with IPCHW_VENDORS=all and 5 with none, measured the way the size check does. It powers the sensor up by starting the CIS clock, and takes the part from the device tree. ipctool additionally pulses the camera GPIOs, works a part number out of a vendor kernel's board string, and knows PrimeSensor and BYD.

The sensor drivers' power-off leaves reset an input, so the clock is enough on the board tested, whose device tree declares no pwdn-gpio. On a board that has that pin the drivers assert it, and there only ipctool would find the sensor.

Known limit: an AK3916EV301 whose vendor kernel calls itself AK3918EV300 reports as AK3918EV300. Nothing readable in software tells the two apart.

Formatting was done by hand (no clang-format on the build host). The arm32 build is clean; mips32 and arm64 are left to CI.

Two families the Anyka EV200/EV300 SDKs drive that no probe knew:

- PrimeSensor PS5250/5260/5268/5270/5280: ID at byte registers
  0x00/0x01 of bank 0, which 0xef selects. The bank is put back as it
  was found, as the SuperPix probe does with its page.
- BYD BF3703: ID 0x3703 at byte registers 0xfc/0xfd.

They are probed last, and only at addresses a HAL lists for them.
…sensor

Model. A device tree's root compatible names the part outright
("anyka,ak3918ev200", "anyka,ak3919ev330") and now wins; OpenIPC's
kernels and the EV330 vendor ones have one. Otherwise the board string
does, but it often names only the package: "AK3918E80PIN" is an EV200 on
OpenIPC's board and an EV300 on another camera, "AK3918E" a V380E2's
EV200. The chip ID's generation now completes it, so these report
AK3918EV200 / AK3918EV300 rather than the package. 0x20120100 is both
the Aimer39 AK3918's and some EV200s': the board string or device tree
puts it in a generation of its own (family ak3918). The AK3918AV130
keeps its ID at SYSCTRL + 0x3fc (0x565735xx, revisions A and B) and is
now read there.

Memory. OpenIPC's kernels take mem= alone and give the ISP the rest of
the memory the device tree declares, so with no memsize= the total is
the device tree's /memory and media what the kernel did not get.

Sensor. Anyka sensors answer I2C only while the camera driver has them
clocked and out of reset, and the drivers power them down after their
own probe, so ipctool found none. On EV200/EV300 the HAL now starts the
CIS clock as the drivers do (SYSCTRL 0x18 from the peripheral PLL,
24 MHz) and pulses the reset/power-down GPIOs the device tree names for
the camera, then puts back exactly what it changed. When the clock is
already running a pipeline is, and nothing is touched. The address
table gains the OV9712's 0x60, the GC1024's 0x78, the EV330 GC4653's
0x52, SmartSens' 0x64, ImageDesign, PrimeSensor and BYD.

On an AK3918EV200 (OpenIPC, SC1345): model AK3918EV200 (was
AK3918E80PIN), ram total 64M / media 24M, sensor SmartSens SC1345 at
0x60 (was none), ipcinfo -s sc1345; the clock and GPIO registers read
the same before and after. With a capture running it reads the sensor
without touching the clock and the stream keeps 15 fps. The naming was
checked against the boot logs of gwell and V380E2 EV200, an AK3916EV301
and EV330 vendor firmware under qemu-hisilicon; anyka_test covers it.
The _A/_B suffix came from 0x3020, which tells two silicon revisions of
the part apart -- the Anyka driver for this ID picks its init variant by
it -- rather than two parts. A name that changes with the revision makes
a poor key for anything looked up by sensor name, such as a tuning file.
EV330 board strings say "AK39EV330", which no part is called. Where the
device tree does not name the part either, report the generation the
way the vendor kernel's CPU banner does, "AK39XXEV330", rather than the
board's short form.
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

PR Summary by Qodo

Identify Anyka parts, report media memory, and probe powered sensors

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Identify Anyka parts from device trees, board names, and chip IDs across generations.
• Report media-reserved memory on kernels without memsize=.
• Power sensors safely for probing, expand supported families, and correct I2C writes and SC2145H
 naming.
Diagram

graph TD
  A["Kernel metadata"] --> B["Anyka HAL"] --> C["Chip identity"]
  B --> D["Memory report"]
  B --> E["Clock and GPIO"] --> F["Sensor probes"] --> G["Sensor identity"]
  F --> H["I2C write helper"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Power sensors through camera-driver interfaces
  • ➕ Would let the kernel coordinate sensor power with an active capture pipeline.
  • ➕ Would avoid direct clock and GPIO register writes.
  • ➖ Vendor and OpenIPC kernels may not expose a common, usable control interface.
  • ➖ Adding kernel-specific controls would substantially narrow portability or expand scope.

Recommendation: The HAL-based approach is reasonable for the supported kernels: it uses the existing probe hook, avoids touching an already-running clock, and restores saved state. Driver-mediated power would be preferable where a stable interface exists, but is not a practical common path here.

Files changed (9) +652 / -67

Enhancement (5) +518 / -41
chipid.cExpose Aimer39 and AV130 chip families +4/-0

Expose Aimer39 and AV130 chip families

• Adds family names for the first AK39 generation and AK3918AV130.

src/chipid.c

anyka.cExpand Anyka identification, memory accounting, and sensor setup +418/-37

Expand Anyka identification, memory accounting, and sensor setup

• Prioritizes exact device-tree part names, completes package-only board names using the generation, and handles Aimer39, AV130, and EV330 cases. Derives media-reserved memory from device-tree RAM when 'memsize=' is absent. Expands sensor addresses and adds guarded EV200/EV300 clock and GPIO setup with cleanup.

src/hal/anyka.c

anyka.hDeclare new Anyka generations and parser interfaces +16/-0

Declare new Anyka generations and parser interfaces

• Defines the AV130 chip-ID register and generation constants. Exposes device-tree, model-naming, GPIO, memory, and PLL helpers for testing.

src/hal/anyka.h

common.hRegister PrimeSensor and BYD families +2/-0

Register PrimeSensor and BYD families

• Adds sensor-family identifiers so HAL address tables and shared probes can select the new detectors.

src/hal/common.h

sensors.cProbe PS52xx and BF3703 and normalize SC2145H +78/-4

Probe PS52xx and BF3703 and normalize SC2145H

• Adds address-limited PrimeSensor PS52xx probing with bank restoration and BYD BF3703 probing. Reports SC2145H without treating its silicon revision as a separate part.

src/sensors.c

Bug fix (1) +16 / -12
common.cSend I2C register values with their addresses +16/-12

Send I2C register values with their addresses

• Fixes the shared write helper to send the register address followed by the value in one transaction, including two-byte forms. Previously it sent address bytes alone.

src/hal/common.c

Tests (2) +94 / -1
anyka_test.cCover Anyka naming and device-tree inputs +93/-0

Cover Anyka naming and device-tree inputs

• Adds fixtures for generation-aware model names, EV330 fallback, Aimer39 detection, AV130 IDs, device-tree memory and GPIO cells, and PLL decoding.

src/hal/anyka_test.c

sensors_golden.txtExpect the SC2145H part name +1/-1

Expect the SC2145H part name

• Changes the expected sensor name from the revision-suffixed SC2145H_B to SC2145H.

src/sensors_golden.txt

Documentation (1) +24 / -13
CLAUDE.mdDocument Anyka detection and verification +24/-13

Document Anyka detection and verification

• Explains device-tree-first part naming, shared chip IDs, and temporary sensor power-up. Updates the test guidance to describe fixture coverage and the reported EV200 hardware check.

CLAUDE.md

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

qodo-free-for-open-source-projects Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Action required

1. A failed probe leaves the camera clock on ✓ Resolved
Description
probe_sensor() calls arm_sensor_clock() again before the SPI probe, after the first bus's
hal_cleanup() already restored everything. anyka_enable_sensor_clock() arms again there (the bus
still opens), but get_sensor_id_spi() returns early on Anyka and never calls hal_cleanup(), and
on a single-bus board no other bus opens either. In that case the CIS clock and the reset/power-down
GPIOs stay modified when the probe returns with no sensor found. Because anyka_power.armed also
stays true, a later probe in the same process (for example majestic reloading) skips arming, and its
hal_cleanup() then writes back the stale PLL2 word with bit 18 cleared. If a pipeline has started
the clock since, that write stops it.

2. PrimeSensor detection misses banked devices ✓ Resolved
Description
detect_primesensor_sensor() uses i2c_write_register() to select bank zero, but Anyka's fallback
writer discards the supplied value and sends only the register address. When a PS52xx sensor is on a
nonzero bank, the following ID reads remain on that bank, so the new probe cannot reliably identify
it.


Remediation recommended

3. New test helper sits after two blank lines 📘 Rule violation ⚙ Maintainability
Description
The new expect_model helper in src/hal/anyka_test.c is preceded by two consecutive blank lines:
one kept from the existing code and one added by this PR. The project's .clang-format (LLVM style,
MaxEmptyLinesToKeep 1) allows only one, so the changed code does not match the configured
formatting. The PR description says formatting was done by hand, which is the likely cause. This is
cosmetic and has no runtime effect, but it will surface as a diff in any future clang-format run.
Code

src/hal/anyka_test.c[R146-153]

 CHECK(anyka_generation(0x20160102) == 0);
Evidence
The diff adds a blank line (+ line 152) directly after the existing blank line that follows
chip_ids()'s closing brace, so static void expect_model now has two blank lines above it.
Checklist item 7 requires changed C code to follow .clang-format, which keeps at most one empty
line.

CLAUDE.md: Follow Project Formatting Without Reformatting Generated Headers: CLAUDE.md: Follow Project Formatting Without Reformatting Generated Headers: CLAUDE.md: Follow Project Formatting Without Reformatting Generated Headers: CLAUDE.md: Follow Project Formatting Without Reformatting Generated Headers: CLAUDE.md: Follow Project Formatting Without Reformatting Generated Headers: CLAUDE.md: Follow Project Formatting Without Reformatting Generated Headers: CLAUDE.md: Follow Project Formatting Without Reformatting Generated Headers: CLAUDE.md: Follow Project Formatting Without Reformatting Generated Headers
src/hal/anyka_test.c[146-153]

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

## Issue description
The PR adds the `expect_model` helper to `src/hal/anyka_test.c` and leaves two consecutive blank lines before it, after the closing brace of `chip_ids()`. The project's `.clang-format` keeps at most one empty line, so the changed code does not match the configured formatting.
## Fix Focus Areas
- src/hal/anyka_test.c[146-153]
## Recommended Fix
Delete the extra blank line so exactly one blank line separates `chip_ids()` from `expect_model`. Then run clang-format on the file, or confirm with it that no other changed lines in the file are reformatted.

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


4. Device-tree-only Aimer39 goes undetected ✓ Resolved
Description
anyka_model_name() ignores bare Aimer39 compatibles and produces an empty name when there is no
machine string. With a device tree containing anyka,ak3918 but no usable /proc/cpuinfo Hardware
field, anyka_detect_cpu() recognizes the compatible and generation, then returns false because the
name is empty.

5. Failed cleanup can leave the camera altered 🐞 Bug ☼ Reliability
Description
anyka_hal_cleanup() clears anyka_power.armed even when a GPIO or PLL restoration write fails,
while gpio_set_bit() also discards write failures. If hardware access fails during cleanup after
the probe changed those registers, later cleanup cannot retry the restoration.
Code

src/hal/anyka.c[R518-520]

+        mem_reg(AK_REG_PERI_PLL2, &anyka_power.pll2, OP_WRITE);
+        mem_reg(AK_REG_PERI_PLL1, &anyka_power.pll1, OP_WRITE);
+        anyka_power.armed = false;
Evidence
mem_reg() can return false on hardware-access failures; the new GPIO setter and cleanup ignore
that result, and cleanup unconditionally discards the saved state.

src/tools.c[31-75]
src/hal/anyka.c[400-407]
src/hal/anyka.c[451-456]
src/hal/anyka.c[514-522]

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

## Issue description
Cleanup forgets saved hardware state even when a register write fails, leaving no way to retry restoration.
## Fix Focus Areas
- src/hal/anyka.c[400-407]
- src/hal/anyka.c[451-456]
- src/hal/anyka.c[514-522]
## Recommended Fix
Propagate GPIO and PLL write results through restoration. Keep the saved state armed when restoration fails so a subsequent cleanup can retry, and report the failed restoration.

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


View review recommended (3)
6. A missing bus leaves the camera powered ✓ Resolved
Description
anyka_enable_sensor_clock() arms the PLL and GPIO changes, but its restoration is confined to
anyka_hal_cleanup(), which the sensor probe does not call when no I2C adapter opens. On a board
with no available adapter, the probe returns with those hardware changes in place, and later
attempts skip initialization because the state remains armed.

7. Failed bank writes can disrupt a stream ✓ Resolved
Description
detect_primesensor_sensor() ignores the results of both its bank-selection and bank-restoration
writes. If selection succeeds but restoration fails while a pipeline is using another bank, the
probe leaves that bank changed; a failed selection can instead make its ID reads come from the wrong
bank.

8. Probing can reset the wrong camera ✓ Resolved
Description
claim_pin() globs every camera node but anyka_dt_gpio() takes only the first matching property,
without relating it to the I2C adapter being probed. When a device tree contains camera nodes on
multiple adapters, the subsequent bus sweep can probe one sensor while the power-up sequence toggles
another camera's pins.

Grey Divider

Tip of the day
💡 Did you know, you can pick a display mode instead of tweaking every Display option by hand

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

universal_i2c_write_register() ignored `data` and wrote `data_width`
bytes of the register address, so a write through the generic HAL
stored nothing: `ipctool i2cset` did nothing on those SoCs, the SuperPix
probe's page select and restore never reached the sensor, and with
two-byte addresses not even the whole address went out. Send the
address and then the value, most significant byte first, as one write.
- The probe calls hal_cleanup() only after it has opened the bus, so a
  power-up for an adapter that does not open was never undone. Arm only
  when the adapter opens.
- Take the reset/power-down GPIOs from the camera node under the adapter
  being probed (/sys/bus/i2c/devices/i2c-N/of_node), not from the first
  camera node anywhere. Without that link, use the device tree's camera
  only when there is exactly one, and touch no pin otherwise.
- PrimeSensor: give up when the bank cannot be selected rather than read
  the ID from the wrong bank, and retry the restore once.

Raised by the Qodo review of #273. Re-run on the AK3918EV200: the adapter
resolves to i2c0@20150000 and its camera@37, SC1345 is found, and the
clock and GPIO registers read the same before and after.
@dimerr

dimerr commented Nov 8, 2026

Copy link
Copy Markdown
Collaborator Author

On the Qodo review, all four fixed:

  1. PrimeSensor bank select did nothing. Correct, and wider than this PR: the generic universal_i2c_write_register() ignored its data, so ipctool i2cset and the SuperPix page select never wrote anything on SoCs using the generic HAL. Fixed in 164ad7a, which sends the address and then the value as one write.
  2. Power-up left on when the bus does not open. The HAL now arms only after the adapter opens, since only then does the probe reach hal_cleanup().
  3. Bank write results ignored. The probe gives up if the bank cannot be selected, and retries the restore once.
  4. Wrong camera's pins. The GPIOs now come from the camera node under the adapter being probed (/sys/bus/i2c/devices/i2c-N/of_node). Without that link, the device tree's camera is used only when there is exactly one, and no pin is touched otherwise.

Re-run on the AK3918EV200: the adapter resolves to i2c0@20150000 and its camera@37, the SC1345 is detected, and the clock and GPIO registers read the same before and after. anyka_test and sensors_test pass.

@dimerr
dimerr marked this pull request as draft November 8, 2026 12:23
@dimerr
dimerr marked this pull request as ready for review November 8, 2026 12:23
ipcinfo grew 4985 bytes, over the size check's 1024, for a family few
cameras use. It now grows 968 with every vendor built in and 5 with none.

The sensor power-up and the PrimeSensor and BYD detectors move behind
!STANDALONE_LIBRARY: ipctool keeps them, ipcinfo does not, so ipcinfo -s
names an Anyka sensor only while something already clocks it. Neither
sensor has a tuning in the firmware package.

The part naming stays in ipcinfo, since ipcinfo -c has to be right, but
takes the device tree's first compatible through line_from_file(), and
the suffix from the generation constant instead of a table of strings.
An Aimer39 is now told by the bare part its board or device tree names.
ipcinfo is how OpenIPC finds the sensor, so it cannot leave the power-up
to ipctool. It now starts the CIS clock itself and grows 728 bytes with
every vendor built in, 5 with none.

The sensor drivers' power-off leaves reset an input, so on a board with
no pwdn-gpio the sensor answers once clocked. ipctool also pulses the
camera GPIOs. Every write goes through one helper that remembers the
register's old value, and cleanup writes those back.

ipcinfo takes the part from the device tree and otherwise the board
string as before; working a part number out of a vendor board string,
and the AV130 and Aimer39 generations, stay in ipctool.
@dimerr
dimerr merged commit a21ed12 into master Nov 8, 2026
5 checks passed
@dimerr
dimerr deleted the anyka-detect branch November 8, 2026 16:34
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.

1 participant