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