Skip to content

sony_imx335: the 5M "12bit" mode was programming the sensor for 10 - #224

Merged
openipc-ai merged 2 commits into
mainfrom
imx335-5m-12bit
Sep 25, 2026
Merged

openipc-ai merged 2 commits into
mainfrom
imx335-5m-12bit

Conversation

@widgetii

@widgetii widgetii commented Sep 17, 2026 •

Copy link
Copy Markdown
Member

IMX335_linear_5M30_12bit_init() set every bit-depth register to 10-bit, in a function whose name says otherwise:

0x3050  0x00    ADBIT  = 10-bit
0x319D  0x00    MDBIT  = 10-bit
0x341C  0xFF    ADBIT1 = 0x01FF, 10-bit
0x341D  0x01

The correct values were sitting in the comments beside them the whole time — one reads //00-10bit | 01 - 12bit next to a 0x00, another //Input AD bit 0047-12bit | 01FF-10bit next to 0x01FF. No init sequence in the file enabled 12-bit anywhere.

5M_imx335.ini declares raw_bitness=12, so the ISP was told to expect twelve bits, received ten, and left-aligned them. Nothing said so — the raw file's BitsPerSample and WhiteLevel both claimed 12.

Measured on a hi3516ev200 + IMX335

719,836 sampled pixels of a daylight frame:

before after
multiples of 4 100.00% 25.12% (chance)
distinct values 929 of 4096 3,716
highest value 4092 4095
low nibble only 0, 4, 8, 12 all sixteen, flat (44,392–45,606 each)

What it buys

Same scene, minutes apart:

levels used darkest fifth
padded 10-bit 907 17 steps
genuine 12-bit 3,397 65 steps

3.8× the tonal resolution in the shadows, which is where the extra bits are worth having — reading a retroreflective plate against a dark car, for instance.

The picture is otherwise unchanged: sharp, correctly coloured, full 2592×1944. SYS_MODE 0x02 (891 Mbps) carries the extra 20% of MIPI bandwidth without alteration, so no timing register moves — HMAX, VMAX and the PLL are untouched.

The 40 fps sequence is deliberately left alone

IMX335_linear_5M30_12bit_40fps_init() has the identical four registers set to ten bits and the identical misleading name. I patched it too, and tested it:

It is reached through high-fps/imx335_2592x1944_45fps.ini, which declares raw_bitness=10. With the sensor sending twelve bits and the ISP expecting ten, the two disagree — a magenta cast appeared over every white surface, and vanished the moment the change was reverted.

So there, the name is what is wrong, not the registers. The sensor and the INI that drives it have to agree, and that pair already does. Renaming it would be a reasonable follow-up; changing its registers would not.

Follow-up: review, and the duplicate #231 folded in

#231 was the same fix, found again from OpenIPC/firmware#2430 by a user reading raw frames for X-ray work. Its evidence and extras are in this PR now, and #231 is closed.

The DNG descriptor (review finding) now says 12 bits and a white level of 4095 for this mode, as the hi3516cv500 driver does.

A raw_bitness=10 profile landing in this init is harmless. Both a 2560×1440 request and high-fps/imx335_2592x1944_45fps.ini run at 30 fps or below reach this init. That was measured on gk7205v300 rather than guarded:

  • With the sensor at 12 bits and the receiver at RAW10, the receiver keeps the top ten bits.
  • Raw frames match the 10-bit ADC within 1% per channel, dim and saturated.
  • RTSP frames give the same colour on white surfaces: 230/241/240 either way, no cast.

Why the faster modes stay at 10 bits, measured on gk7205v300 with the 12-bit ADC against the 10-bit ADC at the same line period:

HMAX used by 12-bit ADC
0x226 this mode column profile within 1.3% of 10-bit
0x1A0 5M ~40 fps left-to-right shading, -36% / +12%, reproducible
0x16E 1080p crop, flex crop left-to-right shading, -20% / +17%, reproducible
0x12C — no frames delivered

That shading is a likelier source of the magenta cast described above for the 40 fps sequence than the bit-depth mismatch, which measured harmless.

Dark frames at 0.1 ms, all gains 1x, temporal noise from differenced pairs, with this branch's library loaded from init:

10-bit ADC 12-bit ADC
gk7205v300 2.23 1.22 counts rms
hi3516ev300 2.36 1.56 counts rms

The noise drop is larger than finer quantisation alone would give.

On a lit scene, the mean is unchanged, and encoded frame rate and MIPI error counters are identical in both modes. Long exposure through a fractional frame rate still works: 999504 µs at 0.5 fps.

The two-lane driver gets a TODO instead of the change. No two-lane board was available to check that 12-bit output fits its link at SYS_MODE 1188 Mbps.

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

Copy link
Copy Markdown

PR Summary by Qodo

Fix IMX335 5MP mode to output genuine 12-bit RAW data

🐞 Bug fix 🕐 Less than 10 minutes

Grey Divider

AI Description

• Programs the 5MP 30fps sensor mode for genuine 12-bit output.
• Aligns sensor samples with the ISP configuration while preserving existing timings.
• Leaves the intentionally 10-bit high-frame-rate sequence unchanged.
Diagram

sequenceDiagram
    participant C as 12-bit Config
    participant I as Mode Init
    participant R as Bit Registers
    participant S as IMX335 Sensor
    participant P as ISP Pipeline
    C->>I: Select 5MP mode
    I->>R: Set 12-bit values
    R->>S: Program bit depth
    S->>P: Send 12-bit RAW
Loading
High-Level Assessment

The targeted register correction is the optimal approach: it makes the named 12-bit mode agree with the ISP configuration without changing validated HMAX, VMAX, PLL, or MIPI timing. Reconfiguring the ISP for 10-bit would preserve reduced tonal resolution, while changing the separate high-frame-rate sequence would create a sensor/ISP bit-depth mismatch.

Files changed (1) +4 / -4

Bug fix (1) +4 / -4
imx335_sensor_ctl.cEnable true 12-bit output in the 5MP 30fps mode +4/-4

Enable true 12-bit output in the 5MP 30fps mode

• Changes ADBIT and MDBIT from 10-bit to 12-bit and updates ADBIT1 from 0x01FF to the required 0x0047 value. This makes the sensor emit genuine 12-bit samples while leaving resolution, frame timing, PLL, and the separate high-frame-rate sequence untouched.

libraries/sensor/hi3516ev200/sony_imx335/imx335_sensor_ctl.c

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

qodo-free-for-open-source-projects Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Action required

1. 4-megapixel captures use wrong depth 🐞 Bug ≡ Correctness
Description
cmos_set_image_mode maps the explicitly classified 2560×1440 10-bit linear request to
IMX335_5M_30FPS_12BIT_LINEAR_MODE, whose initializer now emits 12-bit samples while
cmos_get_isp_default still reports 10 bits per sample and a white level of 2592. Whenever that
shared mode is selected for raw or DNG capture, the 10-bit classification, receiver path, and
metadata reach sensor data with a 12-bit depth and 4095 maximum value.
Code

libraries/sensor/hi3516ev200/sony_imx335/imx335_sensor_ctl.c[1518]

+	IMX335_write_register(ViPipe, 0x3050, 0x01); /* ADBIT = 12-bit */
Evidence
The resolution predicate explicitly identifies 2560×1440 as a 10-bit linear mode, but mode selection
aliases that request to the 5-megapixel mode whose dispatch reaches the changed initializer
selecting 12-bit conversion and output. The corresponding sensor-mode metadata still declares 10
bits per sample and a white level of 2592, while the sibling platform’s IMX335 implementation
describes a 12-bit mode with 12 bits per sample and a white level of 4095, confirming the unresolved
mismatch.

libraries/sensor/hi3516ev200/sony_imx335/imx335_cmos.c[160-165]
libraries/sensor/hi3516ev200/sony_imx335/imx335_cmos.c[1833-1843]
libraries/sensor/hi3516ev200/sony_imx335/imx335_sensor_ctl.c[237-242]
libraries/sensor/hi3516ev200/sony_imx335/imx335_cmos.c[1379-1387]
libraries/sensor/hi3516ev200/sony_imx335/imx335_sensor_ctl.c[1518-1542]
libraries/sensor/hi3516cv500/sony_imx335/imx335_cmos.c[952-958]

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 2560×1440 linear request is defined as 10-bit but shares an initializer that now produces genuine 12-bit output, while the ISP/DNG metadata still declares 10-bit samples and a white level of 2592.
## Fix Focus Areas
- libraries/sensor/hi3516ev200/sony_imx335/imx335_sensor_ctl.c[1518-1542]
- libraries/sensor/hi3516ev200/sony_imx335/imx335_cmos.c[160-165]
- libraries/sensor/hi3516ev200/sony_imx335/imx335_cmos.c[1833-1843]
- libraries/sensor/hi3516ev200/sony_imx335/imx335_sensor_ctl.c[237-242]
- libraries/sensor/hi3516ev200/sony_imx335/imx335_cmos.c[1379-1387]
## Recommended Fix
Give the 2560×1440 10-bit request a distinct mode and initializer that retains the 10-bit register tuple, and dispatch only the 2592×1944 12-bit request to the modified initializer. For the 5-megapixel 12-bit linear mode, set `u8BitsPerSample` to 12 and `u32WhiteLevel` to 4095 while keeping genuinely 10-bit modes in separate metadata cases; alternatively, if 2560×1440 is intentionally becoming 12-bit, rename its classification and update its receiver and metadata configuration to 12-bit together.

ⓘ 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 enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

IMX335_write_register(ViPipe, 0x3077, 0x0F);

IMX335_write_register(ViPipe, 0x3050, 0x00);
IMX335_write_register(ViPipe, 0x3050, 0x01); /* ADBIT = 12-bit */

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. 4-megapixel captures use wrong depth 🐞 Bug ≡ Correctness

cmos_set_image_mode maps the explicitly classified 2560×1440 10-bit linear request to
IMX335_5M_30FPS_12BIT_LINEAR_MODE, whose initializer now emits 12-bit samples while
cmos_get_isp_default still reports 10 bits per sample and a white level of 2592. Whenever that
shared mode is selected for raw or DNG capture, the 10-bit classification, receiver path, and
metadata reach sensor data with a 12-bit depth and 4095 maximum value.
Agent Prompt
## Issue description
The 2560×1440 linear request is defined as 10-bit but shares an initializer that now produces genuine 12-bit output, while the ISP/DNG metadata still declares 10-bit samples and a white level of 2592.

## Fix Focus Areas
- libraries/sensor/hi3516ev200/sony_imx335/imx335_sensor_ctl.c[1518-1542]
- libraries/sensor/hi3516ev200/sony_imx335/imx335_cmos.c[160-165]
- libraries/sensor/hi3516ev200/sony_imx335/imx335_cmos.c[1833-1843]
- libraries/sensor/hi3516ev200/sony_imx335/imx335_sensor_ctl.c[237-242]
- libraries/sensor/hi3516ev200/sony_imx335/imx335_cmos.c[1379-1387]

## Recommended Fix
Give the 2560×1440 10-bit request a distinct mode and initializer that retains the 10-bit register tuple, and dispatch only the 2592×1944 12-bit request to the modified initializer. For the 5-megapixel 12-bit linear mode, set `u8BitsPerSample` to 12 and `u32WhiteLevel` to 4095 while keeping genuinely 10-bit modes in separate metadata cases; alternatively, if 2560×1440 is intentionally becoming 12-bit, rename its classification and update its receiver and metadata configuration to 12-bit together.

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

widgetii and others added 2 commits September 25, 2026 11:59
IMX335_linear_5M30_12bit_init() set every bit-depth register to 10-bit, in a
function whose name says otherwise:

    0x3050  0x00    ADBIT  = 10-bit
    0x319D  0x00    MDBIT  = 10-bit
    0x341C  0xFF    ADBIT1 = 0x01FF, 10-bit
    0x341D  0x01

The correct values were written in the comments beside them all along -- one
reads "//00-10bit | 01 - 12bit" next to a 0x00, and another "//Input AD bit
0047-12bit | 01FF-10bit" next to 0x01FF.

5M_imx335.ini declares raw_bitness=12, so the ISP was told to expect twelve
bits, received ten, and left-aligned them. Measured on a hi3516ev200 with
IMX335 before this change, over 719836 sampled pixels of a daylight frame:

    multiples of 4   100.00%      every low bit pair zero
    distinct values     929       of the 4096 twelve bits allow
    highest value      4092       of 4095

The whole readout was ten bits of information in a twelve-bit container, and
nothing said so: the raw file's BitsPerSample and WhiteLevel both claimed 12.

After:

    multiples of 4    25.12%      chance, as it should be
    distinct values    3716
    highest value      4095
    low nibble        all sixteen values, flat (44392..45606 each)

What it buys, same scene minutes apart:

    padded 10-bit     907 levels used, darkest fifth resolved in 17 steps
    genuine 12-bit   3397 levels used, darkest fifth resolved in 65 steps

3.8x the tonal resolution in the shadows, which is where the extra bits are
worth having. The picture is unchanged otherwise -- sharp, correctly coloured,
full 2592x1944 -- and SYS_MODE 0x02 (891 Mbps) carries the extra 20% of MIPI
bandwidth without alteration, so no timing register moves.

THE 40FPS SEQUENCE IS DELIBERATELY LEFT ALONE. IMX335_linear_5M30_12bit_40fps_
init() has the identical four registers set to ten bits and the identical
misleading name, but it is reached through high-fps/imx335_2592x1944_45fps.ini,
which declares raw_bitness=10. Patching it was tried on the same camera and the
ISP and sensor then disagreed: a magenta cast over every white surface, gone
again the moment it was reverted. Its name is what is wrong there, not its
registers -- the sensor and the INI that drives it have to agree, and that pair
already does.
Review follow-up, folding in #231 (the same fix, found again from
OpenIPC/firmware#2430 and measured on two more boards).

The mode's DNG descriptor still said 10 bits per sample with a white level
of 2592. It now says 12 and 4095, as the hi3516cv500 driver does for the
same mode.

A 2592x1944 or 2560x1440 request from a raw_bitness=10 profile also lands
in this init, e.g. high-fps/imx335_2592x1944_45fps.ini run at 30 fps or
below. That was checked rather than guarded: with the sensor at 12 bits
and the receiver at RAW10, the receiver keeps the top ten bits. Raw frames
match the 10-bit ADC within 1% per channel, dim and saturated, and RTSP
frames give the same colour on white surfaces (230/241/240 either way).

The comment now records why the faster modes stay at 10 bits. Measured on
gk7205v300: at HMAX 0x1A0 and 0x16E the 12-bit ADC shades the frame from
left to right by up to -36%/+17% against the 10-bit ADC, reproducibly.
At 0x12C it delivers no frames.

The two-lane driver gets a TODO instead of the change, because no
two-lane board was available to check that 12-bit output fits its link.

Dark frames, 0.1 ms, all gains 1x, temporal noise from differenced
pairs, this branch's library loaded from init:

                  10-bit ADC   12-bit ADC
  gk7205v300      2.23         1.22 counts rms
  hi3516ev300     2.36         1.56 counts rms
@openipc-ai
openipc-ai merged commit 1c77fd5 into main Sep 25, 2026
35 checks passed
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