@typecad/framework-zephyr 1.0.0-alpha.18 → 1.0.0-alpha.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/audit.d.ts +111 -0
  2. package/dist/audit.js +416 -0
  3. package/dist/boardgen.js +63 -5
  4. package/dist/chips/resolve.js +20 -0
  5. package/dist/chips/types.d.ts +21 -0
  6. package/dist/display/bindings.d.ts +55 -0
  7. package/dist/display/bindings.js +316 -0
  8. package/dist/display/gfx.d.ts +2 -3
  9. package/dist/display/gfx.js +166 -154
  10. package/dist/display/index.js +20 -1
  11. package/dist/display/mipi-dbi-host.d.ts +9 -0
  12. package/dist/display/mipi-dbi-host.js +174 -0
  13. package/dist/display/profiles.d.ts +109 -4
  14. package/dist/display/profiles.js +270 -7
  15. package/dist/display/touch-adapter.js +118 -48
  16. package/dist/display/ui-adapter-eink.d.ts +2 -0
  17. package/dist/display/ui-adapter-eink.js +4 -0
  18. package/dist/display/ui-adapter-gray.d.ts +8 -0
  19. package/dist/display/ui-adapter-gray.js +170 -0
  20. package/dist/display/ui-adapter-mono.d.ts +13 -0
  21. package/dist/display/ui-adapter-mono.js +230 -0
  22. package/dist/display/ui-adapter-native.d.ts +10 -0
  23. package/dist/display/ui-adapter-native.js +295 -0
  24. package/dist/display/ui-adapter-shared.d.ts +11 -0
  25. package/dist/display/ui-adapter-shared.js +122 -0
  26. package/dist/display/ui-adapter.js +53 -101
  27. package/dist/dt-config/kconfig.d.ts +62 -1
  28. package/dist/dt-config/kconfig.js +123 -26
  29. package/dist/dt-config/overlay.d.ts +13 -0
  30. package/dist/dt-config/overlay.js +431 -16
  31. package/dist/framework.manifest.d.ts +9 -3
  32. package/dist/framework.manifest.js +123 -10
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.js +9 -1
  35. package/dist/licenses.js +5 -84
  36. package/dist/lowering/can.d.ts +25 -0
  37. package/dist/lowering/can.js +97 -0
  38. package/dist/lowering/clock.d.ts +17 -0
  39. package/dist/lowering/clock.js +58 -0
  40. package/dist/lowering/hid.d.ts +27 -0
  41. package/dist/lowering/hid.js +244 -0
  42. package/dist/lowering/i2c.d.ts +8 -0
  43. package/dist/lowering/i2c.js +140 -0
  44. package/dist/lowering/i2s.d.ts +27 -0
  45. package/dist/lowering/i2s.js +98 -0
  46. package/dist/lowering/index.d.ts +9 -1
  47. package/dist/lowering/index.js +25 -1
  48. package/dist/lowering/matrix.d.ts +15 -0
  49. package/dist/lowering/matrix.js +63 -0
  50. package/dist/lowering/power.d.ts +10 -0
  51. package/dist/lowering/power.js +35 -0
  52. package/dist/lowering/pwm.js +25 -0
  53. package/dist/lowering/sensor.d.ts +2 -2
  54. package/dist/lowering/sensor.js +8 -4
  55. package/dist/lowering/strip.d.ts +16 -0
  56. package/dist/lowering/strip.js +70 -0
  57. package/dist/lowering/thread.js +5 -1
  58. package/dist/lowering/trace.d.ts +44 -0
  59. package/dist/lowering/trace.js +239 -0
  60. package/dist/lowering/uart.js +6 -1
  61. package/dist/lowering/usb.d.ts +3 -1
  62. package/dist/lowering/usb.js +6 -3
  63. package/dist/sbom.d.ts +181 -0
  64. package/dist/sbom.js +901 -0
  65. package/dist/strategy.d.ts +60 -2
  66. package/dist/strategy.js +485 -25
  67. package/dist/toolchain/index.d.ts +12 -1
  68. package/dist/toolchain/index.js +115 -27
  69. package/dist/toolchain/scaffold.d.ts +4 -1
  70. package/dist/toolchain/scaffold.js +67 -3
  71. package/dist/west-inventory.d.ts +25 -0
  72. package/dist/west-inventory.js +97 -0
  73. package/package.json +3 -3
  74. package/src/audit.ts +529 -0
  75. package/src/boardgen.ts +54 -5
  76. package/src/chips/resolve.ts +21 -0
  77. package/src/chips/types.ts +576 -567
  78. package/src/display/bindings.ts +347 -0
  79. package/src/display/gfx.ts +318 -306
  80. package/src/display/index.ts +87 -70
  81. package/src/display/mipi-dbi-host.ts +183 -0
  82. package/src/display/profiles.ts +458 -139
  83. package/src/display/touch-adapter.ts +274 -204
  84. package/src/display/ui-adapter-eink.ts +13 -0
  85. package/src/display/ui-adapter-gray.ts +178 -0
  86. package/src/display/ui-adapter-mono.ts +238 -0
  87. package/src/display/ui-adapter-native.ts +304 -0
  88. package/src/display/ui-adapter-shared.ts +125 -0
  89. package/src/display/ui-adapter.ts +51 -100
  90. package/src/dt-config/kconfig.ts +647 -511
  91. package/src/dt-config/overlay.ts +433 -16
  92. package/src/framework.manifest.ts +131 -10
  93. package/src/index.ts +11 -1
  94. package/src/licenses.ts +5 -84
  95. package/src/lowering/can.ts +140 -0
  96. package/src/lowering/clock.ts +91 -0
  97. package/src/lowering/hid.ts +261 -0
  98. package/src/lowering/i2c.ts +146 -0
  99. package/src/lowering/i2s.ts +143 -0
  100. package/src/lowering/index.ts +18 -1
  101. package/src/lowering/matrix.ts +70 -0
  102. package/src/lowering/power.ts +41 -0
  103. package/src/lowering/pwm.ts +192 -167
  104. package/src/lowering/sensor.ts +159 -155
  105. package/src/lowering/strip.ts +81 -0
  106. package/src/lowering/thread.ts +5 -1
  107. package/src/lowering/trace.ts +270 -0
  108. package/src/lowering/uart.ts +6 -1
  109. package/src/lowering/usb.ts +224 -221
  110. package/src/sbom.ts +1117 -0
  111. package/src/strategy.ts +440 -25
  112. package/src/toolchain/index.ts +117 -28
  113. package/src/toolchain/scaffold.ts +62 -3
  114. package/src/west-inventory.ts +102 -0
@@ -10,7 +10,10 @@ export interface ZephyrDisplayProfile {
10
10
  * effective size (e.g. a 320x480 panel mounted landscape = 480x320). */
11
11
  readonly nativeWidth?: number;
12
12
  readonly nativeHeight?: number;
13
- readonly colorFormat: 'rgb565' | 'mono';
13
+ readonly colorFormat: 'rgb565' | 'mono' | 'gray8';
14
+ /** Panel class — drives the capability derivation (eink ⇒ deferred
15
+ * refresh, all dynamic features off). */
16
+ readonly displayClass?: 'tft' | 'eink' | 'oled';
14
17
  /** Applied via display_set_orientation (0/90/180/270). */
15
18
  readonly rotation?: number;
16
19
  /** DT alias for the backlight GPIO (set high at init), if any. */
@@ -20,6 +23,38 @@ export interface ZephyrDisplayProfile {
20
23
  * Required for rgb565 profiles; mono profiles use the direct-op GFX
21
24
  * runtime and ignore it. */
22
25
  readonly controller?: ZephyrPanelController;
26
+ /** How the UI adapter reaches the panel:
27
+ * - 'direct-spi' (default): the emitted adapter drives the panel directly
28
+ * over spi_write (Adafruit ST77xx protocol, CS held across command+data).
29
+ * Required for panels the in-tree drivers cannot init (clone ST7796S —
30
+ * the generic mipi-dbi-spi bridge deasserts CS between the command byte
31
+ * and its parameters, which scrambles those panels).
32
+ * - 'zephyr-display': the adapter calls Zephyr's display API
33
+ * (display_write/display_get_capabilities) on the DT display device; an
34
+ * in-tree panel driver (bound from the overlay's display node) owns the
35
+ * init sequence, rotation, and wire format. */
36
+ readonly transport?: ZephyrDisplayTransport;
37
+ /** Which mipi-dbi host device backs the panel driver under the
38
+ * 'zephyr-display' transport:
39
+ * - 'spi-bridge' (default): the stock zephyr,mipi-dbi-spi host
40
+ * (CONFIG_MIPI_DBI_SPI). Fine for panels that tolerate per-transaction
41
+ * CS (standard ILI9341 modules).
42
+ * - 'local-hold-cs': an app-local host device the adapter emits — the
43
+ * mipi-dbi-spi protocol with CS held across each command+data burst
44
+ * (GPIO-managed, mirroring the hardware-verified direct transport).
45
+ * Needed for clone ST77xx panels the stock bridge scrambles; implements
46
+ * the mipi-hold-cs behavior the binding documents but 4.4.2 does not. */
47
+ readonly dbiHost?: ZephyrDbiHost;
48
+ /** The panel's R and B channels are crossed in 16-bit mode (verified clone
49
+ * ST7796S quirk): the native adapter swaps R/B channels at pack time so
50
+ * the in-tree driver's RGB565 stream renders with true colors (lossless —
51
+ * the direct transport instead avoids it by driving 18-bit mode). */
52
+ readonly channelSwapRb?: boolean;
53
+ /** RGB565 wire byte-order inversion: the overlay emits the st7796s-family
54
+ * rgb-is-inverted DT property, flipping the format the in-tree driver
55
+ * reports (565 <-> 565X) so the adapter byte-swaps at pack time. Clone
56
+ * SPI panels whose white reads purple / dark reads green need it. */
57
+ readonly rgbInverted?: boolean;
23
58
  /** DT compatible string for the display@0 node. Defaults per controller
24
59
  * (see PANEL_CONTROLLER_DEFAULTS) — override only for a panel whose DT
25
60
  * binding differs from its controller family. */
@@ -30,17 +65,87 @@ export interface ZephyrDisplayProfile {
30
65
  /** Nodelabel of the MIPI DBI bridge node carrying the dc/reset GPIOs.
31
66
  * Default 'mipi_dbi' (the overlay emits the bridge under that label). */
32
67
  readonly bridgeLabel?: string;
68
+ /** The board's own devicetree already wires this display (native_sim's
69
+ * built-in sdl_dc): the overlay emits NO display node — the dtLabel points
70
+ * at the board's node. */
71
+ readonly boardProvidesDisplay?: boolean;
72
+ /** Extra Kconfig fragments the profile requires (e.g. the SDL panel's mono
73
+ * pixel-format choice). Appended verbatim to the generated prj.conf. */
74
+ readonly kconfig?: readonly string[];
33
75
  }
76
+ /** How the UI adapter talks to the panel (see ZephyrDisplayProfile.transport). */
77
+ export type ZephyrDisplayTransport = 'direct-spi' | 'zephyr-display';
78
+ /** Which mipi-dbi host backs the panel driver (see ZephyrDisplayProfile.dbiHost). */
79
+ export type ZephyrDbiHost = 'spi-bridge' | 'local-hold-cs';
80
+ /** Resolve a profile's transport with the historical default. */
81
+ export declare function transportFor(profile: Pick<ZephyrDisplayProfile, 'transport'>): ZephyrDisplayTransport;
82
+ /** Resolve a profile's mipi-dbi host. Clone-ST77xx panels need the local
83
+ * CS-holding host; everything else defaults to the stock SPI bridge. */
84
+ export declare function dbiHostFor(profile: Pick<ZephyrDisplayProfile, 'dbiHost' | 'controller'>): ZephyrDbiHost;
85
+ /** Marker the display adapters stamp into the emitted source so the toolchain
86
+ * can recover the exact profile that produced it (one source of truth — the
87
+ * profile registry — instead of re-deriving geometry from emitted C). */
88
+ export declare const DISPLAY_PROFILE_MARKER = "typecad-display-profile:";
89
+ /** Machine-readable facts line the adapters stamp beside the marker: JSON
90
+ * carrying everything the toolchain needs to regenerate the DT overlay —
91
+ * including synthesized (compatible-driven) profiles that have no registry
92
+ * entry. */
93
+ export declare function displayFactsLine(profile: ZephyrDisplayProfile): string;
94
+ /** Recover the profile whose adapter emitted this source, or undefined when
95
+ * no display adapter marker is present (no display in the program). Prefers
96
+ * the JSON facts line (carries synthesized profiles); falls back to the
97
+ * plain marker + registry lookup. */
98
+ export declare function profileFromEmittedSource(src: string): ZephyrDisplayProfile | undefined;
34
99
  /** Panel controllers the direct-drive UI adapter knows how to init. */
35
- export type ZephyrPanelController = 'st7796s' | 'ili9341';
100
+ export type ZephyrPanelController = 'st7796s' | 'ili9341' | 'ssd16xx' | 'uc81xx';
36
101
  /** Per-controller DT + transport defaults, shared by the overlay generator
37
102
  * (DT node props) and the UI adapter (init sequence + wire format). */
38
103
  export declare const PANEL_CONTROLLER_DEFAULTS: Record<ZephyrPanelController, {
39
104
  dtCompatible: string;
40
105
  }>;
41
106
  /** Resolve a profile's panel controller, inferring it from the driver id when
42
- * the profile doesn't declare one (the '<controller>-zephyr' naming scheme). */
43
- export declare function panelControllerFor(profile: Pick<ZephyrDisplayProfile, 'driver' | 'controller'>): ZephyrPanelController;
107
+ * the profile doesn't declare one (the '<controller>-zephyr' naming scheme).
108
+ * Synthesized (compatible-driven) profiles carry no controller — undefined
109
+ * routes them to the binding-driven generic paths. */
110
+ export declare function panelControllerFor(profile: Pick<ZephyrDisplayProfile, 'driver' | 'controller'>): ZephyrPanelController | undefined;
111
+ /** A DT compatible string (vendor,name) — the drop-in driver id shape. */
112
+ export declare function isDtCompatible(driver: string): boolean;
113
+ /** True when a drop-in compatible (or explicit config) selects the e-ink
114
+ * lowering target — mono format + deferred refresh. */
115
+ export declare function isEinkDisplay(display: {
116
+ driver: string;
117
+ displayClass?: string;
118
+ }): boolean;
119
+ /** True when a drop-in compatible (or explicit config) selects the gray8
120
+ * (8-bit luminance) lowering target. */
121
+ export declare function isGrayDisplay(display: {
122
+ driver: string;
123
+ colorFormat?: string;
124
+ }): boolean;
125
+ /** True when a drop-in compatible (or explicit config) selects the mono
126
+ * (1bpp) lowering target. */
127
+ export declare function isMonoDisplay(display: {
128
+ driver: string;
129
+ colorFormat?: string;
130
+ }): boolean;
131
+ /** Synthesize a native-transport profile for a compatible-driven config
132
+ * (display.driver = DT compatible, no registry profile). The in-tree driver
133
+ * bound by the overlay's display node owns init/geometry/quirks; the
134
+ * config's panel-quirk flags (channelSwapRb, csHold) carry what the
135
+ * driver cannot know. Returns undefined unless the driver string is a
136
+ * compatible shape. */
137
+ export declare function synthesizeZephyrProfile(display: {
138
+ driver: string;
139
+ width: number;
140
+ height: number;
141
+ nativeWidth?: number;
142
+ nativeHeight?: number;
143
+ colorFormat?: string;
144
+ rotation?: number;
145
+ channelSwapRb?: boolean;
146
+ csHold?: boolean;
147
+ rgbInverted?: boolean;
148
+ }): ZephyrDisplayProfile | undefined;
44
149
  /**
45
150
  * Built-in profile registry. Looked up by driver id. Add a profile here when a
46
151
  * new board's display node is wired into its devicetree.
@@ -6,20 +6,206 @@
6
6
  // pin wiring. The GFX runtime (gfx.ts) reads width/height/colorFormat from the
7
7
  // active profile to size its line buffer.
8
8
  // ---------------------------------------------------------------------------
9
+ /** Resolve a profile's transport with the historical default. */
10
+ export function transportFor(profile) {
11
+ return profile.transport ?? 'direct-spi';
12
+ }
13
+ /** Resolve a profile's mipi-dbi host. Clone-ST77xx panels need the local
14
+ * CS-holding host; everything else defaults to the stock SPI bridge. */
15
+ export function dbiHostFor(profile) {
16
+ if (profile.dbiHost)
17
+ return profile.dbiHost;
18
+ return profile.controller === 'st7796s' ? 'local-hold-cs' : 'spi-bridge';
19
+ }
20
+ /** Marker the display adapters stamp into the emitted source so the toolchain
21
+ * can recover the exact profile that produced it (one source of truth — the
22
+ * profile registry — instead of re-deriving geometry from emitted C). */
23
+ export const DISPLAY_PROFILE_MARKER = 'typecad-display-profile:';
24
+ /** Machine-readable facts line the adapters stamp beside the marker: JSON
25
+ * carrying everything the toolchain needs to regenerate the DT overlay —
26
+ * including synthesized (compatible-driven) profiles that have no registry
27
+ * entry. */
28
+ export function displayFactsLine(profile) {
29
+ const controller = panelControllerFor(profile);
30
+ const facts = {
31
+ driver: profile.driver,
32
+ transport: transportFor(profile),
33
+ width: profile.width,
34
+ height: profile.height,
35
+ };
36
+ if (profile.nativeWidth !== undefined)
37
+ facts.nativeWidth = profile.nativeWidth;
38
+ if (profile.nativeHeight !== undefined)
39
+ facts.nativeHeight = profile.nativeHeight;
40
+ if (controller !== undefined)
41
+ facts.controller = controller;
42
+ if (profile.rotation !== undefined)
43
+ facts.rotation = profile.rotation;
44
+ if (profile.channelSwapRb === true)
45
+ facts.channelSwapRb = true;
46
+ if (profile.rgbInverted === true)
47
+ facts.rgbInverted = true;
48
+ if (transportFor(profile) === 'zephyr-display') {
49
+ facts.dbiHost = dbiHostFor(profile);
50
+ }
51
+ return `// typecad-display-facts: ${JSON.stringify(facts)}`;
52
+ }
53
+ /** Recover the profile whose adapter emitted this source, or undefined when
54
+ * no display adapter marker is present (no display in the program). Prefers
55
+ * the JSON facts line (carries synthesized profiles); falls back to the
56
+ * plain marker + registry lookup. */
57
+ export function profileFromEmittedSource(src) {
58
+ const facts = src.match(/typecad-display-facts: (\{.*\})/);
59
+ if (facts) {
60
+ try {
61
+ const f = JSON.parse(facts[1]);
62
+ const registered = ZEPHYR_DISPLAY_PROFILES[f.driver];
63
+ if (registered)
64
+ return registered;
65
+ if (typeof f.driver === 'string' && isDtCompatible(f.driver)) {
66
+ const synth = synthesizeZephyrProfile({
67
+ driver: f.driver,
68
+ width: f.width,
69
+ height: f.height,
70
+ nativeWidth: f.nativeWidth,
71
+ nativeHeight: f.nativeHeight,
72
+ rotation: f.rotation,
73
+ channelSwapRb: f.channelSwapRb === true,
74
+ rgbInverted: f.rgbInverted === true,
75
+ csHold: f.dbiHost === 'local-hold-cs',
76
+ });
77
+ if (synth)
78
+ return synth;
79
+ }
80
+ }
81
+ catch {
82
+ // Malformed facts line — fall through to the plain marker.
83
+ }
84
+ }
85
+ const m = src.match(/typecad-display-profile: ([\w-]+)/);
86
+ if (!m)
87
+ return undefined;
88
+ return ZEPHYR_DISPLAY_PROFILES[m[1]];
89
+ }
9
90
  /** Per-controller DT + transport defaults, shared by the overlay generator
10
91
  * (DT node props) and the UI adapter (init sequence + wire format). */
11
92
  export const PANEL_CONTROLLER_DEFAULTS = {
12
93
  st7796s: { dtCompatible: 'sitronix,st7796s' },
13
94
  ili9341: { dtCompatible: 'ilitek,ili9341' },
95
+ // E-ink families: no single dtCompatible (the drop-in driver IS one of the
96
+ // many panel compatibles) — the overlay branch keys off the driver string.
97
+ ssd16xx: { dtCompatible: 'solomon,ssd16xx' },
98
+ uc81xx: { dtCompatible: 'ultrachip,uc81xx' },
14
99
  };
15
100
  /** Resolve a profile's panel controller, inferring it from the driver id when
16
- * the profile doesn't declare one (the '<controller>-zephyr' naming scheme). */
101
+ * the profile doesn't declare one (the '<controller>-zephyr' naming scheme).
102
+ * Synthesized (compatible-driven) profiles carry no controller — undefined
103
+ * routes them to the binding-driven generic paths. */
17
104
  export function panelControllerFor(profile) {
18
105
  if (profile.controller)
19
106
  return profile.controller;
20
107
  if (profile.driver.startsWith('ili9341'))
21
108
  return 'ili9341';
22
- return 'st7796s';
109
+ if (profile.driver.startsWith('st7796'))
110
+ return 'st7796s';
111
+ // E-ink families (drop-in compatibles): the driver symbol the Kconfig
112
+ // layer enables under the mipi-dbi SPI branch.
113
+ if (profile.driver.startsWith('solomon,ssd16'))
114
+ return 'ssd16xx';
115
+ if (profile.driver.startsWith('ultrachip,uc81'))
116
+ return 'uc81xx';
117
+ return undefined;
118
+ }
119
+ /** A DT compatible string (vendor,name) — the drop-in driver id shape. */
120
+ export function isDtCompatible(driver) {
121
+ return /^[a-z0-9]+(-[a-z0-9]+)*,[a-z0-9-]+$/i.test(driver);
122
+ }
123
+ /** 1bpp mono panel compatibles (DATA, not code): the Zephyr drivers behind
124
+ * these report PIXEL_FORMAT_MONO01/10 — the Stage 2 full-frame lowering
125
+ * target. A drop-in config naming one of these synthesizes a mono profile
126
+ * without an explicit colorFormat. Grows as mono drivers are verified; an
127
+ * unlisted mono panel still works via display.init({ colorFormat: 'mono' }).
128
+ * (ssd1320/ssd1327-class are L8/grayscale — Stage 3, not here.) */
129
+ const MONO_PANEL_COMPATIBLES = new Set([
130
+ 'solomon,ssd1306',
131
+ 'solomon,ssd1309',
132
+ 'sinowealth,sh1106',
133
+ ]);
134
+ /** 16-gray panels (Stage 3): Zephyr's solomon,ssd1327 driver accepts
135
+ * PIXEL_FORMAT_L_8 (8-bit luminance in, nibble-reduced to the panel's 16
136
+ * levels) — the Stage 3 gray8 lowering target. Same drop-in rule as mono. */
137
+ const GRAY_PANEL_COMPATIBLES = new Set([
138
+ 'solomon,ssd1327',
139
+ ]);
140
+ /** E-Ink panels (Stage 4): the ssd16xx/uc81xx SPI families — same 1bpp
141
+ * format as mono, the new axis is the REFRESH MODEL (deferred: render on
142
+ * signal change, flush with the panel's flash cycle, panel sleeps). Grows
143
+ * as e-ink drivers are verified. */
144
+ const EINK_PANEL_COMPATIBLES = new Set([
145
+ 'solomon,ssd1608',
146
+ 'solomon,ssd1673',
147
+ 'solomon,ssd1675a',
148
+ 'solomon,ssd1680',
149
+ 'solomon,ssd1681',
150
+ 'ultrachip,uc8151d',
151
+ 'ultrachip,uc8175',
152
+ 'ultrachip,uc8176',
153
+ 'ultrachip,uc8179',
154
+ ]);
155
+ /** True when a drop-in compatible (or explicit config) selects the e-ink
156
+ * lowering target — mono format + deferred refresh. */
157
+ export function isEinkDisplay(display) {
158
+ if (display.displayClass === 'eink')
159
+ return true;
160
+ return EINK_PANEL_COMPATIBLES.has(display.driver);
161
+ }
162
+ /** True when a drop-in compatible (or explicit config) selects the gray8
163
+ * (8-bit luminance) lowering target. */
164
+ export function isGrayDisplay(display) {
165
+ if (display.colorFormat === 'gray8')
166
+ return true;
167
+ if (display.colorFormat && display.colorFormat !== 'gray8')
168
+ return false;
169
+ return GRAY_PANEL_COMPATIBLES.has(display.driver);
170
+ }
171
+ /** True when a drop-in compatible (or explicit config) selects the mono
172
+ * (1bpp) lowering target. */
173
+ export function isMonoDisplay(display) {
174
+ if (display.colorFormat === 'mono')
175
+ return true;
176
+ if (display.colorFormat && display.colorFormat !== 'mono')
177
+ return false;
178
+ return MONO_PANEL_COMPATIBLES.has(display.driver);
179
+ }
180
+ /** Synthesize a native-transport profile for a compatible-driven config
181
+ * (display.driver = DT compatible, no registry profile). The in-tree driver
182
+ * bound by the overlay's display node owns init/geometry/quirks; the
183
+ * config's panel-quirk flags (channelSwapRb, csHold) carry what the
184
+ * driver cannot know. Returns undefined unless the driver string is a
185
+ * compatible shape. */
186
+ export function synthesizeZephyrProfile(display) {
187
+ if (!isDtCompatible(display.driver))
188
+ return undefined;
189
+ return {
190
+ driver: display.driver,
191
+ dtLabel: 'display0',
192
+ width: display.width,
193
+ height: display.height,
194
+ nativeWidth: display.nativeWidth,
195
+ nativeHeight: display.nativeHeight,
196
+ // E-ink compatibles are mono-format panels with a deferred refresh
197
+ // model — displayClass drives deriveCapabilities' eink branch (all
198
+ // features off, deferred-partial) and the e-ink adapter dispatch.
199
+ displayClass: isEinkDisplay(display) ? 'eink' : undefined,
200
+ colorFormat: isMonoDisplay(display) || isEinkDisplay(display) ? 'mono' : isGrayDisplay(display) ? 'gray8' : 'rgb565',
201
+ controller: undefined,
202
+ transport: 'zephyr-display',
203
+ dbiHost: display.csHold === true ? 'local-hold-cs' : undefined,
204
+ channelSwapRb: display.channelSwapRb === true ? true : undefined,
205
+ rgbInverted: display.rgbInverted === true ? true : undefined,
206
+ rotation: display.rotation ?? 0,
207
+ backlight: 'backlight',
208
+ };
23
209
  }
24
210
  /**
25
211
  * Built-in profile registry. Looked up by driver id. Add a profile here when a
@@ -36,6 +222,28 @@ export const ZEPHYR_DISPLAY_PROFILES = {
36
222
  rotation: 90,
37
223
  backlight: 'backlight',
38
224
  },
225
+ // Same ILI9341 panel as 'ili9341-zephyr', but driven through Zephyr's
226
+ // display API: the overlay's display0 node (ilitek,ili9341 under the
227
+ // mipi-dbi-spi bridge) binds the in-tree driver with CONFIG_MIPI_DBI_SPI +
228
+ // CONFIG_ILI9341, and the emitted adapter speaks display_write instead of
229
+ // spi_write. The panel init sequence, rotation, and pixel wire format move
230
+ // upstream (the driver owns them). Per-transaction CS (the mipi-dbi-spi
231
+ // bridge's behavior) is fine on standard ILI9341 SPI modules — the
232
+ // CS-held-across-command+data requirement that forces 'direct-spi' is a
233
+ // clone-ST7796S-class quirk. The sibling 'st7796-zephyr-display' profile
234
+ // is hardware-verified end-to-end; this one is compile/link-verified only
235
+ // (no ILI9341 module on the rig yet).
236
+ 'ili9341-zephyr-display': {
237
+ driver: 'ili9341-zephyr-display',
238
+ dtLabel: 'display0',
239
+ width: 320,
240
+ height: 240,
241
+ colorFormat: 'rgb565',
242
+ controller: 'ili9341',
243
+ transport: 'zephyr-display',
244
+ rotation: 90,
245
+ backlight: 'backlight',
246
+ },
39
247
  'st7796-zephyr': {
40
248
  // ST7796S SPI TFT, 320x480 RGB565 mounted landscape (effective 480x320).
41
249
  // Same DT nodelabel convention as the ILI9341 — the board's devicetree
@@ -54,18 +262,73 @@ export const ZEPHYR_DISPLAY_PROFILES = {
54
262
  backlight: 'backlight',
55
263
  },
56
264
  'ssd1306-zephyr': {
57
- // Monochrome OLED (SSD1306-class, 128x64, 1bpp). Driven through Zephyr's
58
- // generic display API (the ssd1306 driver + a DT display node). The GFX
59
- // runtime (gfx.ts mono branch) keeps a full page-framebuffer and pushes it
60
- // on display_flush — the standard model for page-buffered OLEDs. Direct
61
- // display.* ops only (no @typecad/ui CuttlefishGFX rendering on mono).
265
+ // Monochrome OLED (SSD1306-class, 128x64, 1bpp) — Stage 2's mono
266
+ // lowering target. The full-frame adapter (ui-adapter-mono.ts) keeps a
267
+ // vtiled MONO01 backing store and pushes it whole each frame through
268
+ // display_write on the DT display node (the ssd1306 driver over I2C
269
+ // self-builds from the overlay's node). Raw display.* ops ride the same
270
+ // model through gfx.ts's mono branch.
62
271
  driver: 'ssd1306-zephyr',
63
272
  dtLabel: 'display0',
273
+ dtCompatible: 'solomon,ssd1306',
64
274
  width: 128,
65
275
  height: 64,
66
276
  colorFormat: 'mono',
67
277
  rotation: 0,
68
278
  },
279
+ // The verified clone ST7796S on the demo rig, on the native display API:
280
+ // the in-tree sitronix,st7796s driver (CONFIG_ST7796S) owns init/gamma/
281
+ // geometry, backed by an app-local CS-holding mipi-dbi host the adapter
282
+ // emits (dbiHost 'local-hold-cs' — the stock bridge deasserts CS between
283
+ // command and data, which scrambles this clone; the local host implements
284
+ // the mipi-hold-cs behavior Zephyr documents but 4.4.2 does not ship).
285
+ // The clone's 16-bit color pipeline is handled at pack time (byte swap
286
+ // via the reported 565X format — see the color notes below); the direct
287
+ // transport avoids the question entirely by driving 18-bit mode.
288
+ // Color pipeline (hardware-verified on the rig, 16-bit mode, three knobs):
289
+ // - rgb-is-inverted: byte-swap at pack time (565 wire order).
290
+ // - NO R/B channel swap (the 18-bit BGR finding does not transfer).
291
+ // - CS-hold (this profile's default local host): REQUIRED on this clone —
292
+ // the stock mipi-dbi-spi bridge toggles CS per transaction and
293
+ // corrupts pixel bursts (confirmed twice: the original bring-up, and
294
+ // the drop-in path's first stock-bridge build showed the same
295
+ // corruption until csHold was set). With all three, colors are
296
+ // hardware-confirmed correct on the rig. Ladder testing observed a
297
+ // slight uniform dimness vs the direct path's 18-bit mode; the direct
298
+ // profile remains the max-fidelity option for this panel.
299
+ // Touch: FT6336U on the input subsystem — its power enable must be driven
300
+ // (resetPin 4 on the rig; see touch-adapter.ts for the rig-verified
301
+ // sequence). Touch + UI interaction hardware-verified on the rig.
302
+ 'st7796-zephyr-display': {
303
+ driver: 'st7796-zephyr-display',
304
+ dtLabel: 'display0',
305
+ width: 480,
306
+ height: 320,
307
+ nativeWidth: 320,
308
+ nativeHeight: 480,
309
+ colorFormat: 'rgb565',
310
+ controller: 'st7796s',
311
+ transport: 'zephyr-display',
312
+ rotation: 1,
313
+ backlight: 'backlight',
314
+ },
315
+ // Stage 2g — the no-hardware gate: the whole mono lowering on native_sim's
316
+ // built-in SDL panel (zephyr,sdl-dc, 320x240). The board's devicetree
317
+ // already wires sdl_dc, so the overlay emits no display node; the SDL
318
+ // panel's pixel-format choice flips to MONO01 so the 1bpp adapter's
319
+ // display_write flows render as black/white in the emulator window.
320
+ // Compile+link only in CI (Linux — the POSIX arch does not build on
321
+ // Windows); the SDL window needs libsdl2-dev on the runner to link.
322
+ 'native-sim-mono': {
323
+ driver: 'native-sim-mono',
324
+ dtLabel: 'sdl_dc',
325
+ width: 320,
326
+ height: 240,
327
+ colorFormat: 'mono',
328
+ transport: 'zephyr-display',
329
+ boardProvidesDisplay: true,
330
+ kconfig: ['CONFIG_SDL_DISPLAY_DEFAULT_PIXEL_FORMAT_MONO01=y'],
331
+ },
69
332
  };
70
333
  /** The default profile used when resolveDisplayOp is probed without a display.init. */
71
334
  export const DEFAULT_ZEPHYR_DISPLAY_PROFILE = ZEPHYR_DISPLAY_PROFILES['ili9341-zephyr'];