@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
@@ -0,0 +1,178 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Zephyr gray UI display adapter — Stage 3's gray8 lowering target.
3
+ //
4
+ // The panel accepts PIXEL_FORMAT_L_8 (8-bit luminance; the solomon,ssd1327
5
+ // driver nibble-reduces to its 16 display levels). The adapter keeps a
6
+ // row-major L_8 backing store (one byte per pixel — 4KB at 128x64) and
7
+ // pushes the whole frame with ONE display_write, mirroring the mono
8
+ // adapter's full-frame semantic at gray fidelity. UI_COLOR_T is uint8_t and
9
+ // node values are luminance bytes, so every draw is a store.
10
+ //
11
+ // EMIT BOUNDARY: emitted bytes land in user firmware. Covered by the TypeCAD
12
+ // Runtime Exception (RUNTIME_EXCEPTION.md at the repo root).
13
+ //
14
+ // AGENTS.md rendering guardrails honored: one static framebuffer, no
15
+ // per-frame allocation, one atomic push per frame.
16
+ // ---------------------------------------------------------------------------
17
+
18
+ import type { DisplayAdapterCode } from "@typecad/cuttlefish/api/shared";
19
+ import type { ZephyrDisplayProfile } from "./profiles.js";
20
+ import { displayFactsLine } from "./profiles.js";
21
+ import { CANVAS_LIFECYCLE_SECTION, TARGET_FORWARDERS_SECTION, profileMarkerLine } from "./ui-adapter-shared.js";
22
+
23
+ /**
24
+ * Build the gray8 UI adapter for a profile. The profile's dtLabel must name
25
+ * a display node bound by an L_8-capable driver (solomon,ssd1327-class —
26
+ * the overlay generates the node; the driver self-builds from it).
27
+ */
28
+ export function zephyrGrayDisplayAdapter(profile: ZephyrDisplayProfile): DisplayAdapterCode {
29
+ const w = profile.width;
30
+ const h = profile.height;
31
+ const dtLabel = profile.dtLabel;
32
+
33
+ const includes = [
34
+ `// --- Zephyr gray UI display adapter (${profile.driver}, full-frame L_8) ---`,
35
+ profileMarkerLine(profile.driver),
36
+ displayFactsLine(profile),
37
+ `// Gray target: the adapter's ZephyrGrayTarget (a CuttlefishGFX writing an`,
38
+ `// L_8 backing store — one luminance byte per pixel; the driver reduces`,
39
+ `// to the panel's 16 display levels).`,
40
+ `#define CuttlefishDisplayTarget ZephyrGrayTarget`,
41
+ `#include <zephyr/kernel.h>`,
42
+ `#include <zephyr/drivers/display.h>`,
43
+ `#include <zephyr/drivers/gpio.h>`,
44
+ ].join("\n");
45
+
46
+ const declaration = [
47
+ `// CUTTLEFISH_DISPLAY_BEGIN`,
48
+ `// The panel device: the overlay's display0 node, bound by an L_8-capable`,
49
+ `// driver. Compile-time constant — DEVICE_DT_GET requires the driver to`,
50
+ `// exist at build time, which the Kconfig layer guarantees.`,
51
+ `static const struct device* __tc_zd_dev = DEVICE_DT_GET(DT_NODELABEL(${dtLabel}));`,
52
+ `static struct display_capabilities __tc_zd_caps;`,
53
+ `// Full-frame backing store, row-major L_8: one luminance byte per pixel`,
54
+ `// (${w}*${h} = ${w * h} bytes).`,
55
+ `static uint8_t __tc_gray_fb[${w} * ${h}];`,
56
+ `// Rig diagnostics: frames pushed since boot (see display_partial_refresh).`,
57
+ `static uint32_t __tc_gray_frames = 0;`,
58
+ `// Gray target clip (w <= 0 ⇒ no clip). Enforced in ZephyrGrayTarget's`,
59
+ `// drawPixel/fillRect so EVERY pixel — including ones emitted inside`,
60
+ `// CuttlefishGFX geometry algorithms — respects it.`,
61
+ `static int16_t __tc_mono_clip_x = 0;`,
62
+ `static int16_t __tc_mono_clip_y = 0;`,
63
+ `static int16_t __tc_mono_clip_w = 0;`,
64
+ `static int16_t __tc_mono_clip_h = 0;`,
65
+ `// CUTTLEFISH_DISPLAY_END`,
66
+ ].join("\n");
67
+
68
+ const functions = `
69
+ // ── Gray target ───────────────────────────────────────────────────────────
70
+ // A CuttlefishGFX whose pixels are backing-store bytes. All geometry/text
71
+ // primitives the runtime draws funnel through these two overrides.
72
+ class ZephyrGrayTarget final : public CuttlefishGFX {
73
+ public:
74
+ ZephyrGrayTarget() : CuttlefishGFX(nullptr, nullptr) {}
75
+ int16_t width() const { return ${w}; }
76
+ int16_t height() const { return ${h}; }
77
+ void drawPixel(int16_t x, int16_t y, uint16_t color) {
78
+ if (x < 0 || y < 0 || x >= ${w} || y >= ${h}) return;
79
+ if (__tc_mono_clip_w > 0 &&
80
+ (x < __tc_mono_clip_x || y < __tc_mono_clip_y ||
81
+ x >= __tc_mono_clip_x + __tc_mono_clip_w ||
82
+ y >= __tc_mono_clip_y + __tc_mono_clip_h)) return;
83
+ __tc_gray_fb[static_cast<uint32_t>(y) * ${w}u + static_cast<uint32_t>(x)] =
84
+ static_cast<uint8_t>(color);
85
+ }
86
+ void fillRect(int16_t x, int16_t y, int16_t rw, int16_t rh, uint16_t color) {
87
+ if (rw <= 0 || rh <= 0) return;
88
+ for (int16_t j = 0; j < rh; j++) {
89
+ for (int16_t i = 0; i < rw; i++) {
90
+ drawPixel(static_cast<int16_t>(x + i), static_cast<int16_t>(y + j), color);
91
+ }
92
+ }
93
+ }
94
+ };
95
+ static ZephyrGrayTarget __tc_display;
96
+
97
+ // ── Gray clip (the runtime's full-frame scroll clipping) ──────────────────
98
+ static inline void display_mono_set_clip(int16_t x, int16_t y, int16_t rw, int16_t rh) {
99
+ __tc_mono_clip_x = x;
100
+ __tc_mono_clip_y = y;
101
+ __tc_mono_clip_w = rw;
102
+ __tc_mono_clip_h = rh;
103
+ }
104
+ static inline void display_mono_clear_clip() {
105
+ __tc_mono_clip_w = 0;
106
+ __tc_mono_clip_h = 0;
107
+ }
108
+
109
+ // ── Frame push ─────────────────────────────────────────────────────────────
110
+ // Full-frame redraw, ALWAYS (Stage 3): the ${w * h}-byte L_8 frame is one
111
+ // display_write. The dirty-rect args are accepted and ignored — at
112
+ // ~50-100ms a frame (I2C fast mode), incremental compositing has nothing
113
+ // to optimize.
114
+ static inline void display_partial_refresh(int16_t x, int16_t y, int16_t rw, int16_t rh) {
115
+ (void)x; (void)y; (void)rw; (void)rh;
116
+ struct display_buffer_descriptor __desc = {};
117
+ __desc.width = ${w}u;
118
+ __desc.height = ${h}u;
119
+ __desc.pitch = ${w}u;
120
+ __desc.buf_size = sizeof(__tc_gray_fb);
121
+ __desc.frame_incomplete = false;
122
+ int __ret = display_write(__tc_zd_dev, 0, 0, &__desc, __tc_gray_fb);
123
+ __tc_gray_frames++;
124
+ if (__ret != 0) {
125
+ printk("TC_DISPLAY: display_write failed: %d\\n", __ret);
126
+ } else if ((__tc_gray_frames & 0x0Fu) == 1u) {
127
+ printk("TC_DISPLAY: gray frame #%u pushed\\n", static_cast<unsigned int>(__tc_gray_frames));
128
+ }
129
+ }
130
+
131
+ // ── display_init (called from setup) ───────────────────────────────────────
132
+
133
+ static inline void display_init() {
134
+ printk("TC_DISPLAY: gray full-frame transport (${dtLabel}, ${w}x${h}, L_8)\\n");
135
+ if (!device_is_ready(__tc_zd_dev)) {
136
+ printk("TC_DISPLAY: device not ready — is the panel driver enabled?\\n");
137
+ return;
138
+ }
139
+ display_get_capabilities(__tc_zd_dev, &__tc_zd_caps);
140
+ printk("TC_DISPLAY: panel reports %ux%u fmt=%u screen_info=%u\\n",
141
+ static_cast<unsigned int>(__tc_zd_caps.x_resolution),
142
+ static_cast<unsigned int>(__tc_zd_caps.y_resolution),
143
+ static_cast<unsigned int>(__tc_zd_caps.current_pixel_format),
144
+ static_cast<unsigned int>(__tc_zd_caps.screen_info));
145
+ // L_8 is the driver's native format (it nibble-reduces to the panel's 16
146
+ // levels); request it in case the driver boots in something else.
147
+ if (__tc_zd_caps.current_pixel_format != PIXEL_FORMAT_L_8) {
148
+ if (display_set_pixel_format(__tc_zd_dev, PIXEL_FORMAT_L_8) != 0) {
149
+ printk("TC_DISPLAY: panel refused L_8 — check the driver\\n");
150
+ }
151
+ }
152
+ for (size_t i = 0; i < sizeof(__tc_gray_fb); i++) __tc_gray_fb[i] = 0u;
153
+ (void)display_blanking_off(__tc_zd_dev);
154
+ display_partial_refresh(0, 0, ${w}, ${h});
155
+ printk("TC_DISPLAY: gray init done (L_8 full-frame)\\n");
156
+ }
157
+
158
+ static inline void display_fillScreen(UI_COLOR_T color) {
159
+ for (size_t i = 0; i < sizeof(__tc_gray_fb); i++) __tc_gray_fb[i] = static_cast<uint8_t>(color);
160
+ }
161
+ static inline CuttlefishDisplayTarget* display_defaultTarget() { return &__tc_display; }
162
+ static inline int16_t display_width() { return ${w}; }
163
+ static inline int16_t display_height() { return ${h}; }
164
+
165
+ // Immediate-push no-ops: all drawing targets the backing store; the frame
166
+ // publishes atomically in display_partial_refresh.
167
+ static inline void display_startWrite() { }
168
+ static inline void display_endWrite() { }
169
+ static inline void display_setAddrWindow(int16_t x, int16_t y, int16_t winW, int16_t winH) {
170
+ (void)x; (void)y; (void)winW; (void)winH;
171
+ }
172
+ static inline void display_writePixels(const UI_COLOR_T* pixels, uint32_t count) {
173
+ (void)pixels; (void)count;
174
+ }
175
+ ${CANVAS_LIFECYCLE_SECTION}${TARGET_FORWARDERS_SECTION}`;
176
+
177
+ return { includes, declaration, functions };
178
+ }
@@ -0,0 +1,238 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Zephyr mono UI display adapter — Stage 2's 1bpp lowering target.
3
+ //
4
+ // The mono panel IS a backing store: the adapter keeps a full-frame vtiled
5
+ // MONO01 framebuffer (byte = 8 vertical pixels, buf[(y>>3)*W + x], bit
6
+ // 1<<(y&7) — the layout Zephyr's ssd1306-class drivers expect from
7
+ // display_write: pitch == width, y and height multiples of 8). Every UI draw
8
+ // lands in RAM through a CuttlefishGFX subclass; ui_refresh_flush pushes the
9
+ // whole frame with ONE display_write call (full-frame redraw, always — a
10
+ // 128×64 frame is 1KB, ~25ms over I²C; incremental compositing has nothing
11
+ // to optimize).
12
+ //
13
+ // The runtime's scroll-canvas/band machinery is bypassed under
14
+ // UI_FULL_FRAME_REDRAW (see the engine's runtime header); scroll clipping
15
+ // rides this adapter's mono clip rect (display_mono_set_clip), which every
16
+ // draw primitive enforces — including pixels emitted inside the CuttlefishGFX
17
+ // geometry algorithms, which is why the clip lives here and not in the shim.
18
+ //
19
+ // EMIT BOUNDARY: emitted bytes land in user firmware. Covered by the TypeCAD
20
+ // Runtime Exception (RUNTIME_EXCEPTION.md at the repo root).
21
+ //
22
+ // AGENTS.md rendering guardrails honored: one static framebuffer, no per-frame
23
+ // allocation, one atomic push per frame.
24
+ // ---------------------------------------------------------------------------
25
+
26
+ import type { DisplayAdapterCode } from "@typecad/cuttlefish/api/shared";
27
+ import type { ZephyrDisplayProfile } from "./profiles.js";
28
+ import { displayFactsLine } from "./profiles.js";
29
+ import { CANVAS_LIFECYCLE_SECTION, TARGET_FORWARDERS_SECTION, profileMarkerLine } from "./ui-adapter-shared.js";
30
+
31
+ /**
32
+ * Build the mono (1bpp) UI adapter for a profile. The profile's dtLabel must
33
+ * name a display node an in-tree mono driver binds (ssd1306-class over I²C —
34
+ * the overlay generates the node; the driver self-builds from it).
35
+ * `opts.eink` retunes it for the e-ink refresh model (Stage 4): skip the
36
+ * MONO01 negotiation (ssd16xx/uc81xx are MONO10-only) and throttle pushes
37
+ * to the panel's flash-cycle economics.
38
+ */
39
+ export function zephyrMonoDisplayAdapter(profile: ZephyrDisplayProfile, opts?: { eink?: boolean }): DisplayAdapterCode {
40
+ const eink = opts?.eink === true;
41
+ const w = profile.width;
42
+ const h = profile.height;
43
+ const dtLabel = profile.dtLabel;
44
+ const nativeW = profile.nativeWidth ?? w;
45
+ const nativeH = profile.nativeHeight ?? h;
46
+
47
+ const includes = [
48
+ `// --- Zephyr mono UI display adapter (${profile.driver}, full-frame 1bpp) ---`,
49
+ profileMarkerLine(profile.driver),
50
+ displayFactsLine(profile),
51
+ `// Mono target: the adapter's ZephyrMonoTarget (a CuttlefishGFX writing a`,
52
+ `// vtiled MONO01 backing store), NOT the rgb565 panel-ops path.`,
53
+ `#define CuttlefishDisplayTarget ZephyrMonoTarget`,
54
+ `#include <zephyr/kernel.h>`,
55
+ `#include <zephyr/drivers/display.h>`,
56
+ `#include <zephyr/drivers/gpio.h>`,
57
+ ].join("\n");
58
+
59
+ const declaration = [
60
+ `// CUTTLEFISH_DISPLAY_BEGIN`,
61
+ `// The panel device: the overlay's display0 node, bound by an in-tree mono`,
62
+ `// driver (ssd1306-class). Compile-time constant — DEVICE_DT_GET requires`,
63
+ `// the driver to exist at build time, which the Kconfig layer guarantees.`,
64
+ `static const struct device* __tc_zd_dev = DEVICE_DT_GET(DT_NODELABEL(${dtLabel}));`,
65
+ `static struct display_capabilities __tc_zd_caps;`,
66
+ `// Full-frame backing store, vtiled MONO01: byte = 8 vertical pixels,`,
67
+ `// buf[(y>>3)*${w} + x], bit = 1<<(y&7). One bit per pixel ⇒ ${w}*${h}/8 = ${(w * h) / 8} bytes.`,
68
+ `static uint8_t __tc_mono_fb[(${w} * ${h} + 7) / 8];`,
69
+ `// Push scratch for panels that insist on MONO10 (1=black): the frame is`,
70
+ `// complemented here, never in the backing store itself.`,
71
+ `static uint8_t __tc_mono_inv[(${w} * ${h} + 7) / 8];`,
72
+ `// Rig diagnostics: frames pushed since boot (see display_partial_refresh).`,
73
+ `static uint32_t __tc_mono_frames = 0;`,
74
+ `// E-ink flush floor: one flash per push at most this often (the drivers
75
+ // block through BUSY inside display_write — an unthrottled binding tick
76
+ // would stall the app loop for seconds).
77
+ #define TC_EINK_MIN_REFRESH_MS 2000u
78
+ // Set when the panel insists on MONO10 (1=black): pushes invert the frame.`,
79
+ `static bool __tc_mono_invert = false;`,
80
+ `// Scroll-viewport clip in display coords (w <= 0 ⇒ no clip). Enforced in`,
81
+ `// ZephyrMonoTarget's drawPixel/fillRect so EVERY pixel — including ones`,
82
+ `// emitted inside CuttlefishGFX geometry algorithms — respects it.`,
83
+ `static int16_t __tc_mono_clip_x = 0;`,
84
+ `static int16_t __tc_mono_clip_y = 0;`,
85
+ `static int16_t __tc_mono_clip_w = 0;`,
86
+ `static int16_t __tc_mono_clip_h = 0;`,
87
+ `// CUTTLEFISH_DISPLAY_END`,
88
+ ].join("\n");
89
+
90
+ const functions = `
91
+ // ── Mono target ───────────────────────────────────────────────────────────
92
+ // A CuttlefishGFX whose pixels are backing-store bits. All geometry/text
93
+ // primitives the runtime draws funnel through these two overrides.
94
+ class ZephyrMonoTarget final : public CuttlefishGFX {
95
+ public:
96
+ ZephyrMonoTarget() : CuttlefishGFX(nullptr, nullptr) {}
97
+ int16_t width() const { return ${w}; }
98
+ int16_t height() const { return ${h}; }
99
+ void drawPixel(int16_t x, int16_t y, uint16_t color) {
100
+ if (x < 0 || y < 0 || x >= ${w} || y >= ${h}) return;
101
+ if (__tc_mono_clip_w > 0 &&
102
+ (x < __tc_mono_clip_x || y < __tc_mono_clip_y ||
103
+ x >= __tc_mono_clip_x + __tc_mono_clip_w ||
104
+ y >= __tc_mono_clip_y + __tc_mono_clip_h)) return;
105
+ uint8_t mask = static_cast<uint8_t>(1u << (y & 7));
106
+ uint8_t* b = &__tc_mono_fb[(static_cast<uint32_t>(y) >> 3) * ${w}u + static_cast<uint32_t>(x)];
107
+ if (color) *b = static_cast<uint8_t>(*b | mask);
108
+ else *b = static_cast<uint8_t>(*b & static_cast<uint8_t>(~mask));
109
+ }
110
+ void fillRect(int16_t x, int16_t y, int16_t rw, int16_t rh, uint16_t color) {
111
+ if (rw <= 0 || rh <= 0) return;
112
+ for (int16_t j = 0; j < rh; j++) {
113
+ for (int16_t i = 0; i < rw; i++) {
114
+ drawPixel(static_cast<int16_t>(x + i), static_cast<int16_t>(y + j), color);
115
+ }
116
+ }
117
+ }
118
+ };
119
+ static ZephyrMonoTarget __tc_display;
120
+
121
+ // ── Mono clip (the runtime's full-frame scroll clipping) ──────────────────
122
+ static inline void display_mono_set_clip(int16_t x, int16_t y, int16_t rw, int16_t rh) {
123
+ __tc_mono_clip_x = x;
124
+ __tc_mono_clip_y = y;
125
+ __tc_mono_clip_w = rw;
126
+ __tc_mono_clip_h = rh;
127
+ }
128
+ static inline void display_mono_clear_clip() {
129
+ __tc_mono_clip_w = 0;
130
+ __tc_mono_clip_h = 0;
131
+ }
132
+
133
+ // ── Frame push ─────────────────────────────────────────────────────────────
134
+ // Full-frame redraw, ALWAYS (Stage 2): the 1KB frame is one display_write.
135
+ // The ssd1306-class drivers require pitch == width and (y, height) multiples
136
+ // of 8 — the full frame satisfies both by construction. The dirty-rect args
137
+ // are accepted and ignored: at ~25ms a frame, partial compositing has nothing
138
+ // to optimize.
139
+ static inline void display_partial_refresh(int16_t x, int16_t y, int16_t rw, int16_t rh) {
140
+ (void)x; (void)y; (void)rw; (void)rh;
141
+ ${eink ? ` // E-ink deferred flush: the flash cycle blocks inside display_write for
142
+ // 1-4s — rate-limit pushes, keep the newest frame pending in between.
143
+ static uint32_t last_push_ms = 0;
144
+ static bool pending = false;
145
+ const uint32_t now_ms = k_uptime_get_32();
146
+ if (pending && (now_ms - last_push_ms) < TC_EINK_MIN_REFRESH_MS) return;
147
+ pending = true;
148
+ last_push_ms = now_ms;` : ""}
149
+ struct display_buffer_descriptor __desc = {};
150
+ __desc.width = ${w}u;
151
+ __desc.height = ${h}u;
152
+ __desc.pitch = ${w}u;
153
+ __desc.buf_size = sizeof(__tc_mono_fb);
154
+ __desc.frame_incomplete = false;
155
+ const uint8_t* src = __tc_mono_fb;
156
+ if (__tc_mono_invert) {
157
+ // Panel insists on MONO10 (1=black): push the complement of the frame.
158
+ for (size_t i = 0; i < sizeof(__tc_mono_fb); i++) {
159
+ __tc_mono_inv[i] = static_cast<uint8_t>(~__tc_mono_fb[i]);
160
+ }
161
+ src = __tc_mono_inv;
162
+ }
163
+ int __ret = display_write(__tc_zd_dev, 0, 0, &__desc, src);
164
+ // Rig diagnostics: the first frame + every 64th reports liveness, and any
165
+ // write failure prints once-per-second-class errno (EIO = I2C NACK —
166
+ // address/wiring; ENODEV = device never initialized). A blank panel with
167
+ // these lines green means the failure is panel-side (power/quirks).
168
+ __tc_mono_frames++;
169
+ if (__ret != 0) {
170
+ printk("TC_DISPLAY: display_write failed: %d\\n", __ret);
171
+ } else if ((__tc_mono_frames & 0x3Fu) == 1u) {
172
+ printk("TC_DISPLAY: mono frame #%u pushed\\n", static_cast<unsigned int>(__tc_mono_frames));
173
+ }
174
+ }
175
+
176
+ // ── display_init (called from setup) ───────────────────────────────────────
177
+
178
+ static inline void display_init() {
179
+ printk("TC_DISPLAY: ${eink ? 'e-ink deferred' : 'mono full-frame'} transport (${dtLabel}, ${w}x${h})\\n");
180
+ if (!device_is_ready(__tc_zd_dev)) {
181
+ printk("TC_DISPLAY: device not ready — is the panel driver enabled?\\n");
182
+ return;
183
+ }
184
+ display_get_capabilities(__tc_zd_dev, &__tc_zd_caps);
185
+ // Rig diagnostics: what the driver reports — geometry/format mismatches
186
+ // print here before anything reaches the panel.
187
+ printk("TC_DISPLAY: panel reports %ux%u fmt=%u screen_info=%u\\n",
188
+ static_cast<unsigned int>(__tc_zd_caps.x_resolution),
189
+ static_cast<unsigned int>(__tc_zd_caps.y_resolution),
190
+ static_cast<unsigned int>(__tc_zd_caps.current_pixel_format),
191
+ static_cast<unsigned int>(__tc_zd_caps.screen_info));
192
+ // MONO01 (0=black, 1=white) matches our packing. A MONO10 panel gets asked
193
+ // to switch once; if it refuses, frames push bit-inverted (still correct).
194
+ if (${!eink} && __tc_zd_caps.current_pixel_format == PIXEL_FORMAT_MONO10) {
195
+ if (display_set_pixel_format(__tc_zd_dev, PIXEL_FORMAT_MONO01) == 0) {
196
+ display_get_capabilities(__tc_zd_dev, &__tc_zd_caps);
197
+ }
198
+ }
199
+ if (__tc_zd_caps.current_pixel_format == PIXEL_FORMAT_MONO10) {
200
+ __tc_mono_invert = true;
201
+ } else if (${!eink} && __tc_zd_caps.current_pixel_format != PIXEL_FORMAT_MONO01) {
202
+ printk("TC_DISPLAY: unexpected pixel format %u (expected MONO01) — rendering best-effort\\n",
203
+ static_cast<unsigned int>(__tc_zd_caps.current_pixel_format));
204
+ }
205
+ if (__tc_zd_caps.x_resolution != static_cast<uint16_t>(${nativeW})
206
+ || __tc_zd_caps.y_resolution != static_cast<uint16_t>(${nativeH})) {
207
+ printk("TC_DISPLAY: panel reports %ux%u, DT node carries ${nativeW}x${nativeH} — check the display node geometry\\n",
208
+ static_cast<unsigned int>(__tc_zd_caps.x_resolution),
209
+ static_cast<unsigned int>(__tc_zd_caps.y_resolution));
210
+ }
211
+ for (size_t i = 0; i < sizeof(__tc_mono_fb); i++) __tc_mono_fb[i] = 0u;
212
+ (void)display_blanking_off(__tc_zd_dev);
213
+ display_partial_refresh(0, 0, ${w}, ${h});
214
+ printk("TC_DISPLAY: ${eink ? 'e-ink init done (1bpp deferred, min 2s/flash)' : 'mono init done (1bpp full-frame)'}\\n");
215
+ }
216
+
217
+ static inline void display_fillScreen(UI_COLOR_T color) {
218
+ const uint8_t fill = color ? 0xFFu : 0x00u;
219
+ for (size_t i = 0; i < sizeof(__tc_mono_fb); i++) __tc_mono_fb[i] = fill;
220
+ }
221
+ static inline CuttlefishDisplayTarget* display_defaultTarget() { return &__tc_display; }
222
+ static inline int16_t display_width() { return ${w}; }
223
+ static inline int16_t display_height() { return ${h}; }
224
+
225
+ // Immediate-push no-ops: all drawing targets the backing store; the frame
226
+ // publishes atomically in display_partial_refresh.
227
+ static inline void display_startWrite() { }
228
+ static inline void display_endWrite() { }
229
+ static inline void display_setAddrWindow(int16_t x, int16_t y, int16_t winW, int16_t winH) {
230
+ (void)x; (void)y; (void)winW; (void)winH;
231
+ }
232
+ static inline void display_writePixels(const UI_COLOR_T* pixels, uint32_t count) {
233
+ (void)pixels; (void)count;
234
+ }
235
+ ${CANVAS_LIFECYCLE_SECTION}${TARGET_FORWARDERS_SECTION}`;
236
+
237
+ return { includes, declaration, functions };
238
+ }