Skip to content

novatek: NA51055 and NA51084 tables, the primary LCD, and 0x5021's name - #240

Merged
widgetii merged 2 commits into
masterfrom
novatek-more-dies
Oct 10, 2026
Merged

widgetii merged 2 commits into
masterfrom
novatek-more-dies

Conversation

@widgetii

@widgetii widgetii commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Follow-up to #239: the three items it left open.

NA51055 and NA51084

NA51055 is the NT9852x die; NA51084 is NT98528/NT98529. The NA51089 SDK already carries their pinmux driver, na51055. It is one driver for both dies and branches on nvt_get_chip_id(). Compared with NA51089 the TOP layout is the same, the DSI pad group is gone, and there are more S, L and D pads.

The generator now runs each driver once per die, answering the chip-ID call with a constant, and writes a separate table for each die. Differences between the two dies:

Die Extra functions
NA51084 I2C4/5, SPI4/5 and an RGMII
NA51055 two SPI locations that NA51084 does not have

On NA51055, top.h disagrees with the driver in ten places, and the errata are now per driver because of it:

  • The MISC annotations were copied from NA51089 and name pads this driver does not use.
  • The PWM8..11 HSI location is commented out of the code ("52x compatible").
  • PICNT2 really is on P_GPIO14 here, which is exactly where the NA51089 erratum says it is not.

The primary LCD

pinmux_select_primary_lcd() is now run for every LCD type with every set of feature flags. Its local copies of REG2 and of the L and DSI gates are turned into the globals that every other function writes, so the same logging covers it.

Two generator rules were added for it:

  • Overwritten fields. If an enclosing block writes a field and a nested block then overwrites it before the gate write, the nested value counts. LCD_TYPE is reset at the top of the function and set inside a switch.
  • Option names. Options are now read only from & PIN_... tests and case labels. A range test can no longer give its name to a pad.

TV and HDMI have no pads on these dies: pinmux_set_host() answers E_ID for both.

Resolver fixes the new tables exposed

  • Ties. When two claims have the same conditions, the gated claim now wins.
  • Ungated set. set() of an ungated claim now writes the pad's gate to GPIO, as the vendor does.
  • Silent replay. A replay refinement that changes nothing is now reported as an error instead of passing silently.

Both behaviour fixes come from one case on NA51055's P_GPIO9..11: CCIR8 data and CCIR8 sync both hold there under SENSOR2=2.

0x5021's name

0x5021 used to be reported as "NT98562". It is NA51084, and it now reports as NT98528. The evidence is u-boot's NA51084 clock code: it drives the _528_ clock registers, and it allows 1200 MHz only on an NT98529, which is identified through OTP.

NT98562 and NT98566 share 0x7021. They differ only in an eFuse word that the SDK decodes in a binary, so both still report as NT98566.

Not done, and why

  • NA51090 and NA51103. Neither has a pinmux driver in this SDK or in any public source I could find. NA51090's registers also sit above 4 GB, beyond what mem_reg() can reach.
  • NA51068. It uses a per-pad selector, a different mechanism from this backend, and has no chip ID in this tree.

All four are documented in docs/padmux.md.

Verification

  • reginfo_test. It sweeps all three dies. For each die it also sets every function, starting from every claim's own state, and checks the result:

    Die Sets Refused
    NA51089 4363 0
    NA51055 4038 0
    NA51084 4347 0

    It also has new checks for the LCD (including a DE panel), for the differences between the two dies, and for NA51055's GPIO pad counts.

  • Generator. gen_novatek_padmux.py --verify reproduces the header. NA51089's 388 existing claims are unchanged; every new one is an LCD claim.

  • Selftest. It now covers the overwrite rule, the tie rule, shifted enum values and per-group naming. Reverting any of them makes it fail.

  • Other checks. tools/test_pipeline.sh passes, and so does gen_padmux_names.py --check. Builds pass in the configurations IPCHW_PADMUX=none, IPCHW_VENDORS=ingenic and IPCHW_PADMUX=novatek;IPCHW_VENDORS=novatek. Cross builds pass on arm32, mips32 and arm64.

  • Size, with CI's own arm32 toolchain. ipcinfo is byte-identical to master with both all and none. ipctool grows 28.7 KB, or 7.8 KB after UPX.

  • Hardware. Not verified on any Novatek board.

The three things #239 left open.

NA51055 (NT9852x) and NA51084 (NT98528/NT98529). The NA51089 SDK carries
the na51055 driver too: same TOP layout without the DSI group, more S, L
and D pads, and one driver for both dies that branches on
nvt_get_chip_id(). The generator now runs a driver per family and each
family once per die, with the chip-ID call answered by a constant, and
writes one table per die. NA51084 alone has I2C4/5, SPI4/5 and RGMII.
Their top.h annotations disagree with their code in ten places -- MISC
copied from NA51089, PWM8..11's HSI location commented out ("52x
compatible") -- so the errata are now per driver; NA51089's PICNT2
erratum is exactly wrong on NA51055.

The LCD. pinmux_select_primary_lcd() is run as every LCD type with every
set of feature flags, its local copies of REG2 and the L and DSI gates
turned into the globals every other function writes. That needed one rule
more: a field an enclosing block wrote and a nested block overwrote before
the gate write takes the nested value (LCD_TYPE is reset at the top and
set in a switch). And options are read only from `& PIN_...` tests and
case labels, so a range test cannot name a pad. TV and HDMI have no pads
on these dies: pinmux_set_host() answers E_ID for them.

Two resolver fixes the new tables exposed. Of two claims with alike
conditions the gated one now wins, and set() of an ungated claim writes
the gate to GPIO as the vendor does: CCIR8 data and CCIR8 sync both hold
under SENSOR2=2. The replay now reports a refinement that changes nothing
instead of passing over it silently.

0x5021 was named NT98562. It is NA51084: u-boot's NA51084 clock code
drives the _528_ registers and allows 1200 MHz only on an NT98529. It now
reports NT98528. NT98562 and NT98566 share 0x7021 and are told apart only
by an eFuse word the SDK decodes in a binary.

Not done, and why: NA51090 and NA51103 have no pinmux driver in this SDK
or anywhere public that was found (NA51090's registers are also above
4 GB); NA51068 is a per-pad selector part with no chip ID here.

reginfo_test sweeps all three dies and sets every function from every
claim's state (3201, 2118 and 2342 sets, none refused). ipcinfo is
byte-identical on arm32; ipctool grows 20 KB (6.5 KB after UPX).
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add NA51055/NA51084 padmux tables and primary LCD support

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

Grey Divider

AI Description

• Add die-specific NA51055 and NA51084 padmux tables, GPIO support, and register dumps.
• Model primary LCD muxing and correct claim resolution to match vendor behavior.
• Report chip ID 0x5021 as NT98528 and expand documentation and tests.
Diagram

graph TD
  Vendor["Vendor drivers"] --> Generator["Padmux generator"] --> Tables["Die-specific tables"] --> Backend["Padmux backend"] --> API["Padmux API"]
  Chip["Detected chip"] --> GPIO["GPIO controller"] --> API
  Chip --> Backend
  Chip --> Reginfo["Register dumps"]
Loading
High-Level Assessment

Keep the per-die generated tables and shared runtime backend. Hand-maintained tables would be harder to reconcile with vendor behavior, while branching on chip ID during claim resolution would add runtime complexity. Host-side replay and tests provide substantial validation, though hardware verification remains outstanding.

Files changed (15) +4429 / -336

Enhancement (9) +4284 / -288
novatek_gpio.cValidate GPIO pads according to the detected die +23/-14

Validate GPIO pads according to the detected die

• Adds NA51055/NA51084 pad counts, including their expanded S, L, and D groups and absent DSI group. GPIO support and validity checks now select counts by chip ID.

src/hal/novatek_gpio.c

novatek_padmux.cSelect new die tables and set ungated claim gates +14/-3

Select new die tables and set ungated claim gates

• Dispatches NA51055 and NA51084 to their generated tables. Setting an ungated claim now writes its pad gate to GPIO, matching vendor behavior and preventing a competing gated claim from winning.

src/hal/novatek_padmux.c

novatek_padmux.hGenerate padmux claims for three Novatek dies +2999/-181

Generate padmux claims for three Novatek dies

• Adds separate NA51055 and NA51084 pad, condition, claim, and selector tables plus primary LCD claims for NA51089. Claim ordering now prefers gated claims over ungated claims with identical conditions.

src/hal/novatek_padmux.h

novatek_reginfo.hDefine TOP register dumps without a DSI gate +24/-3

Define TOP register dumps without a DSI gate

• Adds the register list shared by NA51055 and NA51084, reusing the existing TOP definitions while omitting the DSI pad gate.

src/hal/novatek_reginfo.h

padmux.cEnable padmux API dispatch for two more dies +2/-0

Enable padmux API dispatch for two more dies

• Routes NA51055 and NA51084 to the Novatek padmux operations.

src/padmux.c

padmux_names.cIntern names for new Novatek pad functions +295/-1

Intern names for new Novatek pad functions

• Adds generated string entries for newly exposed pads and functions, including LCD and NA51084-specific functions.

src/padmux_names.c

padmux_names.hDeclare offsets for new Novatek function names +590/-2

Declare offsets for new Novatek function names

• Adds matching conditional members and name-offset definitions for the expanded generated padmux tables.

src/padmux_names.h

reginfo.cSelect register dumps for NA51055 and NA51084 +5/-0

Select register dumps for NA51055 and NA51084

• Dispatches both dies to their TOP register list and includes them in whole-register dump handling.

src/reginfo.c

gen_novatek_padmux.pyGenerate and verify claims separately for each die +332/-84

Generate and verify claims separately for each die

• Runs the NA51055 driver with each chip ID, extracts primary LCD muxing, and applies per-driver annotation errata. It corrects option attribution and overwritten-field conditions, prioritizes gated ties, rejects ineffective replay refinements, and expands self-tests.

tools/gen_novatek_padmux.py

Bug fix (1) +10 / -4
novatek.cReport chip ID 0x5021 as NT98528 +10/-4

Report chip ID 0x5021 as NT98528

• Corrects the reported name for NA51084 and uses named chip-ID constants. Comments clarify that IDs identify dies, not every part variant.

src/hal/novatek.c

Tests (1) +54 / -5
reginfo_test.cExercise LCD muxing and both new die tables +54/-5

Exercise LCD muxing and both new die tables

• Adds LCD, die-specific function, chip-name, and GPIO boundary checks. Table-integrity and set-from-every-claim-state coverage now include NA51055 and NA51084.

src/reginfo_test.c

Documentation (4) +81 / -39
CLAUDE.mdDocument multi-die Novatek generation +4/-3

Document multi-die Novatek generation

• Describes generator runs for all three dies and broadens the GPIO implementation guidance beyond NA51089.

CLAUDE.md

gpio.mdList newly supported Novatek GPIO parts +2/-1

List newly supported Novatek GPIO parts

• Extends the GPIO usage documentation to NT9852x and NT98528/NT98529.

docs/gpio.md

padmux.mdExplain expanded Novatek padmux coverage and limits +67/-28

Explain expanded Novatek padmux coverage and limits

• Documents the new die tables, LCD claims, chip naming, generation process, resolver behavior, and unsupported dies. It also records that Novatek hardware has not been verified.

docs/padmux.md

novatek.hClarify Novatek die IDs and part variants +8/-7

Clarify Novatek die IDs and part variants

• Corrects the part associations for NA51055, NA51084, and NA51090 and documents the distinction between chip IDs and eFuse- or OTP-selected variants.

src/hal/novatek.h

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

qodo-free-for-open-source-projects Bot commented Oct 10, 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. NA51055 boards without a model go undetected ✓ Resolved
Description
nvt_get_chip_id() records NA51055’s 0x4821 ID but has no success case for it, even though
padmux_ops() now supports that ID. If /proc/device-tree/model is unavailable, the fallback also
fails and chip detection stops before the new GPIO and padmux tables can be used.
Code

src/padmux.c[67]

+    case CHIP_NA51055:
Evidence
The proc chip ID is assigned to chip_generation, but the switch has no NA51055 case; the only
subsequent success path requires the device-tree model. Generic detection requires
novatek_detect_cpu() to succeed before calling HAL setup, while the PR adds NA51055 to the padmux
dispatch.

src/hal/novatek.c[32-58]
src/hal/novatek.c[67-80]
src/chipid.c[130-145]
src/padmux.c[66-70]

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

## Issue description
NA51055 is supported by the new padmux backend, but detection fails when its chip ID is available and its device-tree model is not.
## Fix Focus Areas
- src/hal/novatek.c[39-80]
- src/padmux.c[66-70]
## Recommended Fix
Treat a recognized NA51055 chip ID as sufficient for detection. Preserve the device-tree part name when it is available, and use a die-level name when it is not.

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


2. Any LCD set up with DE enabled reads as unclaimed ✓ Resolved
Description
In all three generated tables, every LCD colour-pin claim requires REG2 bit 6 (the DE flag) to be 0.
The only LCD_DE_ENABLE claim, on NA51089's L_GPIO0 and on L_GPIO11 of NA51055/NA51084, requires
LCD_TYPE == 0 (GPIO). This comes from conds_at(): it takes the value from writes in enclosing
blocks, so a type or DE write made in a sibling block before or after the gate write is either
ignored or replaced by the reset value. As a result, on a real panel with DE enabled (type ≠ 0, DE =
1) no LCD claim holds, so reginfo --pads/get() show those pads as handed over with nothing
claiming them. Also, set(L_GPIO0, "LCD_DE_ENABLE") writes LCD_TYPE=0, which turns the display
off, and setting a colour function clears DE.
Code

tools/gen_novatek_padmux.py[R898-909]

+        for f, writes in run.writes.items():
+            own = [w for w in writes if w[2] in enclosing]
+            if not own:
+                continue
+            value = own[-1][1]
+            before = [w for w in writes if w[0] < seq]
+            own_before = [w for w in own if w[0] < seq]
+            if (before and before[-1][2] not in enclosing and own_before and
+                    before[-1][0] > own_before[-1][0] and
+                    not any(w[0] > seq for w in own)):
+                value = before[-1][1]
+            conds[f] = value
Evidence
NA51089_claims line 641 {96, 0, PMX_LCD_DE_ENABLE, 158, 3} points to conds[158..160] =
{0x08,0,4,0},{0x08,6,1,1},{0x08,8,1,0}, which means LCD_TYPE=GPIO with DE=1. The walk/sel row at
line 1128 shows LCD_DE_ENABLE as REG2[3:0]=0. NA51055 (cond 209, header line 2094/1676) and NA51084
(cond 205, line 3365) have the same claim. The colour claims, e.g. RGB565 at cond 168 (lines 298-300
= {0x08,0,4,3},{0x08,6,1,0},{0x08,8,1,0}), require DE=0. There is no other variant of either
claim. The harness runs every type with every flag set, yet no claim pairs a non-zero type with
DE=1, so those states fail every claim. replay() does not catch this: when an own claim's conditions
don't match the final regs it counts the case as an orphan (self.orphans += 1) and moves on.
nvt_set() writes every condition of the chosen claim, so selecting LCD_DE_ENABLE writes LCD_TYPE=0.

src/hal/novatek_padmux.h[641-646]
src/hal/novatek_padmux.h[288-300]
src/hal/novatek_padmux.h[1128-1128]
src/hal/novatek_padmux.h[2094-2094]
src/hal/novatek_padmux.h[1676-1678]
tools/gen_novatek_padmux.py[1009-1015]
src/hal/novatek_padmux.c[415-436]

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 generated LCD claims cannot describe a DE-enabled panel. Colour pins require REG2 bit6 (DE) = 0, and `LCD_DE_ENABLE` requires `LCD_TYPE` = 0. In every real LCD-with-DE state no claim holds. set() of LCD_DE_ENABLE writes LCD_TYPE=0 and turns the display off. `conds_at()` only trusts writes from enclosing blocks, plus the one 'nested overwrite before the gate' exception. Type and DE writes from sibling blocks (the switch, the DE `if`) on the other side of the gate write are lost. replay() hides the problem by counting it as an orphan.
## Fix Focus Areas
- tools/gen_novatek_padmux.py[886-910]
- tools/gen_novatek_padmux.py[1009-1015]
- src/hal/novatek_padmux.h[641-641]
## Recommended Fix
For `pinmux_select_primary_lcd()`, take REG2's `LCD_TYPE` and feature-flag fields from the final value the call leaves (run.regs), or from the last write in the call, rather than only from enclosing blocks. Then DE claims carry the real type, and colour claims either carry the real DE value or do not depend on DE. In replay(), report an error rather than counting an orphan when the own claim's conditions disagree with the final regs only through a field the same call wrote later. Add a reginfo_test case for an LCD type with DE that checks both the colour pin and the DE pin read back, and that LCD_TYPE survives set(LCD_DE_ENABLE). Regenerate the header.

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



Informational

3. A renamed GPIO-count macro silently drops a pad group ✓ Resolved
Description
bonded_pads() now continues whenever nvt-gpio.h lacks any group's *_GPIO_NUM macro, where it
used to stop with an error. The skip was added only for NA51055's missing DSI group, but it applies
to every group and every family. If an SDK renames or drops any group's count macro, every pad in
that group is silently left out of the generated table, and --verify would accept the result.
Code

tools/gen_novatek_padmux.py[R390-392]

      m = re.search(r"#define\s+%s\s+(\d+)" % macro, gpio_h_text)
      if not m:
-            die("nvt-gpio.h has no %s" % macro)
+            continue  # NA51055 has no DSI group
Evidence
The old code ran die("nvt-gpio.h has no %s"). The new code runs `continue  # NA51055 has no DSI
group` for every entry of GROUPS and does not check which family or group is missing.

tools/gen_novatek_padmux.py[386-394]

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

## Issue description
`bonded_pads()` skips any group whose count macro is missing. A renamed macro would silently drop that whole group from the table.
## Fix Focus Areas
- tools/gen_novatek_padmux.py[386-394]
## Recommended Fix
Keep the `die()` for missing macros. Allow the skip only for the DSI group's macro, or only for families declared without DSI (e.g. add a per-family set of optional groups in FAMILIES).

ⓘ 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 copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/padmux.c
Comment thread tools/gen_novatek_padmux.py
Comment thread tools/gen_novatek_padmux.py Outdated
From review of #240.

The LCD flags (TE, DE, HVLD/VVLD, FIELD, NO_HVSYNC) are spelt `0x01 << 23`
in top.h, which the enum parser read as no value, so they were taken for
LCD types: the harness never ran a type with DE, every colour claim
required DE off, and LCD_DE_ENABLE required LCD_TYPE = GPIO. A real panel
with DE read as unclaimed and set(LCD_DE_ENABLE) would have turned it off.
Enumerator values are now evaluated for shifts and ors, and an LCD enum
with no readable flags is fatal.

That exposed a second bug: a state no source names inherited the name of
any annotated state whose conditions are a subset of its own, across
groups. NA51055's RMII claims L_GPIO0..10 with no condition at all, so
every LCD claim there was named TXD0, CRS_DV and so on. Inheritance is now
within one group.

Also:
- bonded_pads() skips a missing count macro only for the DSI group; any
  other missing one is a renamed macro and is fatal again.
- NA51055 is named by its die when the device tree has no model, so a
  board without one is still detected.
- novatek_open_i2c_fd() tests NA51103 by chip ID: the ID path names it
  NT98332G, so the "NA51103" name test never matched and its sensor bus
  was never 1.
- nvt_get_chip_id() names through one strcpy; ipcinfo stays byte-identical.

reginfo_test: a DE panel reads as the LCD on both the colour and the DE
pin, and setting DE keeps the panel type. Every-state sets: 4363, 4038 and
4347, none refused. The selftest covers shifted enum values and
cross-group inheritance, and fails with either fix reverted.
@widgetii

Copy link
Copy Markdown
Member Author

Review findings, all three fixed in eadf8c4:

  1. NA51055 without a device-tree model went undetected. Fixed. When the model is missing, the die name NA51055 is used, so such a board is still detected. While there I fixed a neighbouring bug: novatek_open_i2c_fd() tested the name "NA51103", but the chip-ID path names that die NT98332G, so the test never matched. It now tests the chip ID.

  2. A DE panel read as unclaimed. Correct, and the cause was worse than the symptom. top.h spells the LCD flags 0x01 << 23, and the enum parser read that as "no value", so every flag was treated as an LCD type. As a result the harness never ran a type with DE. Enum values are now evaluated, and an LCD enum with no readable flags is fatal.

    Fixing it exposed a second bug: a state that no source names could inherit the name of a state from another group. On NA51055, RMII claims L_GPIO0..10 with no condition at all, so every LCD claim on those pads was called TXD0, CRS_DV and so on. Inheritance now stays within one group.

    New tests in reginfo_test:

    • With DE enabled, both the colour pin and the DE pin read as the LCD.
    • set(LCD_DE_ENABLE) leaves the panel type unchanged.

    The selftest now fails with either fix reverted.

  3. A renamed count macro dropped a whole group silently. Fixed. Only DSI_GPIO_NUM may be missing; any other missing macro is fatal again.

Every-state sets are now 4363 (NA51089), 4038 (NA51055) and 4347 (NA51084), with none refused. NA51089's non-LCD claims are unchanged. ipcinfo is still byte-identical to master with CI's toolchain. ipctool grows 28.7 KB on arm32, 7.8 KB after UPX.

@widgetii
widgetii merged commit 406ac91 into master Oct 10, 2026
5 checks passed
@widgetii
widgetii deleted the novatek-more-dies branch October 10, 2026 04:50
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