@typecad/framework-zephyr 1.0.0-alpha.16 → 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 (210) hide show
  1. package/LICENSE +202 -21
  2. package/README.md +49 -87
  3. package/dist/as-built.d.ts +2 -2
  4. package/dist/as-built.js +2 -2
  5. package/dist/audit.d.ts +111 -0
  6. package/dist/audit.js +416 -0
  7. package/dist/boardgen.d.ts +1 -9
  8. package/dist/boardgen.js +288 -47
  9. package/dist/chips/resolve.js +20 -0
  10. package/dist/chips/types.d.ts +22 -1
  11. package/dist/debug-codegen.js +1 -1
  12. package/dist/display/bindings.d.ts +55 -0
  13. package/dist/display/bindings.js +316 -0
  14. package/dist/display/gfx.d.ts +2 -3
  15. package/dist/display/gfx.js +166 -154
  16. package/dist/display/index.js +20 -1
  17. package/dist/display/mipi-dbi-host.d.ts +9 -0
  18. package/dist/display/mipi-dbi-host.js +174 -0
  19. package/dist/display/profiles.d.ts +109 -4
  20. package/dist/display/profiles.js +270 -7
  21. package/dist/display/touch-adapter.js +119 -49
  22. package/dist/display/ui-adapter-eink.d.ts +2 -0
  23. package/dist/display/ui-adapter-eink.js +4 -0
  24. package/dist/display/ui-adapter-gray.d.ts +8 -0
  25. package/dist/display/ui-adapter-gray.js +170 -0
  26. package/dist/display/ui-adapter-mono.d.ts +13 -0
  27. package/dist/display/ui-adapter-mono.js +230 -0
  28. package/dist/display/ui-adapter-native.d.ts +10 -0
  29. package/dist/display/ui-adapter-native.js +295 -0
  30. package/dist/display/ui-adapter-shared.d.ts +11 -0
  31. package/dist/display/ui-adapter-shared.js +122 -0
  32. package/dist/display/ui-adapter.js +510 -558
  33. package/dist/doctor.js +4 -4
  34. package/dist/dt-config/custom-board.js +2 -2
  35. package/dist/dt-config/kconfig.d.ts +62 -1
  36. package/dist/dt-config/kconfig.js +141 -38
  37. package/dist/dt-config/overlay.d.ts +15 -2
  38. package/dist/dt-config/overlay.js +433 -18
  39. package/dist/framework.manifest.d.ts +10 -4
  40. package/dist/framework.manifest.js +133 -17
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.js +14 -6
  43. package/dist/licenses.d.ts +2 -2
  44. package/dist/licenses.js +13 -92
  45. package/dist/lowering/can.d.ts +25 -0
  46. package/dist/lowering/can.js +97 -0
  47. package/dist/lowering/clock.d.ts +17 -0
  48. package/dist/lowering/clock.js +58 -0
  49. package/dist/lowering/fs.js +1 -1
  50. package/dist/lowering/gpio.js +0 -32
  51. package/dist/lowering/hid.d.ts +27 -0
  52. package/dist/lowering/hid.js +244 -0
  53. package/dist/lowering/http.js +264 -32
  54. package/dist/lowering/i2c.d.ts +8 -0
  55. package/dist/lowering/i2c.js +137 -5
  56. package/dist/lowering/i2s.d.ts +27 -0
  57. package/dist/lowering/i2s.js +98 -0
  58. package/dist/lowering/index.d.ts +9 -1
  59. package/dist/lowering/index.js +25 -1
  60. package/dist/lowering/interrupts.js +6 -0
  61. package/dist/lowering/matrix.d.ts +15 -0
  62. package/dist/lowering/matrix.js +63 -0
  63. package/dist/lowering/mqtt.js +110 -8
  64. package/dist/lowering/power.d.ts +3 -2
  65. package/dist/lowering/power.js +20 -45
  66. package/dist/lowering/pwm.js +25 -0
  67. package/dist/lowering/sensor.d.ts +2 -2
  68. package/dist/lowering/sensor.js +8 -4
  69. package/dist/lowering/strip.d.ts +16 -0
  70. package/dist/lowering/strip.js +70 -0
  71. package/dist/lowering/thread.js +5 -1
  72. package/dist/lowering/trace.d.ts +44 -0
  73. package/dist/lowering/trace.js +239 -0
  74. package/dist/lowering/uart.js +6 -1
  75. package/dist/lowering/usb.d.ts +3 -1
  76. package/dist/lowering/usb.js +16 -13
  77. package/dist/lowering/wdt.js +2 -29
  78. package/dist/sbom.d.ts +181 -0
  79. package/dist/sbom.js +901 -0
  80. package/dist/sdk/board-catalog-sync.d.ts +1 -3
  81. package/dist/sdk/board-catalog-sync.js +4 -10
  82. package/dist/strategy.d.ts +82 -46
  83. package/dist/strategy.js +639 -182
  84. package/dist/tmp-probe.d.ts +2 -0
  85. package/dist/tmp-probe.js +9 -0
  86. package/dist/toolchain/debug-config.d.ts +50 -90
  87. package/dist/toolchain/debug-config.js +239 -510
  88. package/dist/toolchain/env-check.d.ts +1 -3
  89. package/dist/toolchain/env-check.js +2 -7
  90. package/dist/toolchain/index.d.ts +28 -3
  91. package/dist/toolchain/index.js +498 -62
  92. package/dist/toolchain/runners.d.ts +16 -0
  93. package/dist/toolchain/runners.js +75 -0
  94. package/dist/toolchain/scaffold.d.ts +5 -2
  95. package/dist/toolchain/scaffold.js +79 -15
  96. package/dist/toolchain/west-discover.d.ts +6 -0
  97. package/dist/toolchain/west-discover.js +36 -13
  98. package/dist/toolchain/west-spawn.js +8 -2
  99. package/dist/west-inventory.d.ts +25 -0
  100. package/dist/west-inventory.js +97 -0
  101. package/installer/README.md +328 -328
  102. package/installer/install.sh +2 -2
  103. package/installer/templates/project/.typecad/activate-zephyr.ps1 +1 -1
  104. package/installer/templates/project/.typecad/activate-zephyr.sh +1 -1
  105. package/installer/templates/project/.vscode/settings.json +1 -1
  106. package/installer/templates/project/README.md +2 -2
  107. package/package.json +5 -5
  108. package/src/as-built.ts +206 -206
  109. package/src/audit.ts +529 -0
  110. package/src/boardgen.ts +265 -50
  111. package/src/chips/resolve.ts +21 -0
  112. package/src/chips/types.ts +10 -1
  113. package/src/display/bindings.ts +347 -0
  114. package/src/display/gfx.ts +318 -306
  115. package/src/display/index.ts +87 -70
  116. package/src/display/mipi-dbi-host.ts +183 -0
  117. package/src/display/profiles.ts +458 -139
  118. package/src/display/touch-adapter.ts +119 -49
  119. package/src/display/ui-adapter-eink.ts +13 -0
  120. package/src/display/ui-adapter-gray.ts +178 -0
  121. package/src/display/ui-adapter-mono.ts +238 -0
  122. package/src/display/ui-adapter-native.ts +304 -0
  123. package/src/display/ui-adapter-shared.ts +125 -0
  124. package/src/display/ui-adapter.ts +732 -781
  125. package/src/doctor.ts +4 -4
  126. package/src/dt-config/custom-board.ts +2 -2
  127. package/src/dt-config/kconfig.ts +647 -505
  128. package/src/dt-config/overlay.ts +1475 -1058
  129. package/src/framework.manifest.ts +659 -535
  130. package/src/index.ts +16 -6
  131. package/src/licenses.ts +346 -425
  132. package/src/lowering/can.ts +140 -0
  133. package/src/lowering/clock.ts +91 -0
  134. package/src/lowering/fs.ts +135 -135
  135. package/src/lowering/gpio.ts +0 -33
  136. package/src/lowering/hid.ts +261 -0
  137. package/src/lowering/http.ts +264 -32
  138. package/src/lowering/i2c.ts +142 -5
  139. package/src/lowering/i2s.ts +143 -0
  140. package/src/lowering/index.ts +18 -1
  141. package/src/lowering/interrupts.ts +6 -0
  142. package/src/lowering/matrix.ts +70 -0
  143. package/src/lowering/mqtt.ts +109 -8
  144. package/src/lowering/power.ts +41 -0
  145. package/src/lowering/pwm.ts +192 -167
  146. package/src/lowering/sensor.ts +159 -155
  147. package/src/lowering/strip.ts +81 -0
  148. package/src/lowering/thread.ts +5 -1
  149. package/src/lowering/trace.ts +270 -0
  150. package/src/lowering/uart.ts +6 -1
  151. package/src/lowering/usb.ts +16 -13
  152. package/src/lowering/wdt.ts +2 -25
  153. package/src/sbom.ts +1117 -0
  154. package/src/sdk/board-catalog-sync.ts +4 -25
  155. package/src/strategy.ts +2680 -2309
  156. package/src/toolchain/debug-config.ts +262 -522
  157. package/src/toolchain/env-check.ts +279 -285
  158. package/src/toolchain/index.ts +1792 -1359
  159. package/src/toolchain/runners.ts +80 -0
  160. package/src/toolchain/scaffold.ts +355 -296
  161. package/src/toolchain/west-discover.ts +35 -13
  162. package/src/toolchain/west-spawn.ts +174 -168
  163. package/src/west-inventory.ts +102 -0
  164. package/dist/async/timer-polyfill.d.ts +0 -10
  165. package/dist/async/timer-polyfill.js +0 -95
  166. package/dist/chips/board-overrides.d.ts +0 -7
  167. package/dist/chips/board-overrides.js +0 -11
  168. package/dist/chips/esp32.d.ts +0 -2
  169. package/dist/chips/esp32.js +0 -71
  170. package/dist/chips/esp32s3.d.ts +0 -2
  171. package/dist/chips/esp32s3.js +0 -103
  172. package/dist/chips/soc/.d.ts +0 -2
  173. package/dist/chips/soc/.js +0 -129
  174. package/dist/chips/soc/esp32.d.ts +0 -2
  175. package/dist/chips/soc/esp32.js +0 -120
  176. package/dist/chips/soc/esp32c3.d.ts +0 -2
  177. package/dist/chips/soc/esp32c3.js +0 -90
  178. package/dist/chips/soc/esp32c6.d.ts +0 -2
  179. package/dist/chips/soc/esp32c6.js +0 -109
  180. package/dist/chips/soc/esp32s3.d.ts +0 -2
  181. package/dist/chips/soc/esp32s3.js +0 -189
  182. package/dist/chips/soc/index.d.ts +0 -2
  183. package/dist/chips/soc/index.js +0 -23
  184. package/dist/chips/soc/nrf52840.d.ts +0 -2
  185. package/dist/chips/soc/nrf52840.js +0 -130
  186. package/dist/chips/soc/rp2040.d.ts +0 -2
  187. package/dist/chips/soc/rp2040.js +0 -141
  188. package/dist/chips/soc/rp2350a.d.ts +0 -2
  189. package/dist/chips/soc/rp2350a.js +0 -145
  190. package/dist/chips/soc/samd21g18a.d.ts +0 -2
  191. package/dist/chips/soc/samd21g18a.js +0 -143
  192. package/dist/chips/soc/stm32f411xe.d.ts +0 -2
  193. package/dist/chips/soc/stm32f411xe.js +0 -251
  194. package/dist/chips/xiao-ble.d.ts +0 -2
  195. package/dist/chips/xiao-ble.js +0 -100
  196. package/dist/lowering/pulse.d.ts +0 -7
  197. package/dist/lowering/pulse.js +0 -51
  198. package/dist/lowering/tone.d.ts +0 -10
  199. package/dist/lowering/tone.js +0 -63
  200. package/dist/lowering/worker-backing.d.ts +0 -14
  201. package/dist/lowering/worker-backing.js +0 -79
  202. package/dist/lowering/worker.d.ts +0 -6
  203. package/dist/lowering/worker.js +0 -14
  204. package/dist/sdk/board-data.generated.d.ts +0 -2
  205. package/dist/sdk/board-data.generated.js +0 -4
  206. package/dist/sdk/catalog-walker.d.ts +0 -90
  207. package/dist/sdk/catalog-walker.js +0 -682
  208. package/dist/sdk/dts-reader.d.ts +0 -83
  209. package/dist/sdk/dts-reader.js +0 -596
  210. package/src/debug-codegen.ts +0 -207
@@ -1,70 +1,87 @@
1
- // ---------------------------------------------------------------------------
2
- // Zephyr display op resolver — maps DisplayHALOp → GFX runtime calls
3
- //
4
- // resolveZephyrDisplayOp is a pure function of (op, state): the strategy holds
5
- // a DisplayState (mirrors Arduino's _displayCtx) and passes it in. On
6
- // display.init the state is seeded with the active profile; subsequent ops
7
- // lower to the helpers in gfx.ts.
8
- //
9
- // Validator probes call fill_rect/draw_text/draw_rect/flush WITHOUT a prior
10
- // init, so when state is uninitialized we seed the DEFAULT profile and lower
11
- // anyway (the probe only checks code/expression is non-undefined).
12
- // ---------------------------------------------------------------------------
13
-
14
- import type { DisplayHALOp } from '@typecad/cuttlefish/api/shared';
15
- import { DEFAULT_ZEPHYR_DISPLAY_PROFILE, type ZephyrDisplayProfile } from './profiles.js';
16
-
17
- // Re-export the UI display/touch adapters + profile registry so the strategy
18
- // and consumers can reach them from the package barrel.
19
- export { zephyrUiDisplayAdapter, zephyrDisplayAdapterGenerator } from './ui-adapter.js';
20
- export { zephyrTouchAdapter } from './touch-adapter.js';
21
- export { ZEPHYR_DISPLAY_PROFILES, DEFAULT_ZEPHYR_DISPLAY_PROFILE, BUILT_IN_PROFILES } from './profiles.js';
22
- export type { ZephyrDisplayProfile } from './profiles.js';
23
-
24
- export interface DisplayState {
25
- initialized: boolean;
26
- profile: ZephyrDisplayProfile;
27
- }
28
-
29
- /** Fresh display state (used by the strategy per-build). */
30
- export function newDisplayState(): DisplayState {
31
- return { initialized: false, profile: DEFAULT_ZEPHYR_DISPLAY_PROFILE };
32
- }
33
-
34
- /**
35
- * Resolve a display.* op. Returns { code } for statement ops, undefined when
36
- * the op is not recognized.
37
- */
38
- export function resolveZephyrDisplayOp(
39
- op: DisplayHALOp,
40
- state: DisplayState,
41
- ): { code?: string; expression?: string } | undefined {
42
- const o = op as any;
43
-
44
- // display.init seeds the state. The probe payload for init has no profile
45
- // fields, so use the default profile.
46
- if (op.operation === 'display.init') {
47
- state.initialized = true;
48
- return { code: 'display_init();' };
49
- }
50
-
51
- // For non-init ops: if not initialized (validator probe path), seed the
52
- // default so the op still lowers.
53
- if (!state.initialized) {
54
- state.initialized = true;
55
- state.profile = DEFAULT_ZEPHYR_DISPLAY_PROFILE;
56
- }
57
-
58
- switch (op.operation) {
59
- case 'display.fill_rect':
60
- return { code: `display_fill_rect(${o.x}, ${o.y}, ${o.w}, ${o.h}, ${o.color});` };
61
- case 'display.draw_rect':
62
- return { code: `display_draw_rect(${o.x}, ${o.y}, ${o.w}, ${o.h}, ${o.color});` };
63
- case 'display.draw_text':
64
- return { code: `display_draw_text(${o.x}, ${o.y}, "${o.text}", ${o.color});` };
65
- case 'display.flush':
66
- return { code: 'display_flush();' };
67
- default:
68
- return undefined;
69
- }
70
- }
1
+ // ---------------------------------------------------------------------------
2
+ // Zephyr display op resolver — maps DisplayHALOp → GFX runtime calls
3
+ //
4
+ // resolveZephyrDisplayOp is a pure function of (op, state): the strategy holds
5
+ // a DisplayState (mirrors Arduino's _displayCtx) and passes it in. On
6
+ // display.init the state is seeded with the active profile; subsequent ops
7
+ // lower to the helpers in gfx.ts.
8
+ //
9
+ // Validator probes call fill_rect/draw_text/draw_rect/flush WITHOUT a prior
10
+ // init, so when state is uninitialized we seed the DEFAULT profile and lower
11
+ // anyway (the probe only checks code/expression is non-undefined).
12
+ // ---------------------------------------------------------------------------
13
+
14
+ import type { DisplayHALOp } from '@typecad/cuttlefish/api/shared';
15
+ import { DEFAULT_ZEPHYR_DISPLAY_PROFILE, ZEPHYR_DISPLAY_PROFILES, synthesizeZephyrProfile, type ZephyrDisplayProfile } from './profiles.js';
16
+
17
+ // Re-export the UI display/touch adapters + profile registry so the strategy
18
+ // and consumers can reach them from the package barrel.
19
+ export { zephyrUiDisplayAdapter, zephyrDisplayAdapterGenerator } from './ui-adapter.js';
20
+ export { zephyrTouchAdapter } from './touch-adapter.js';
21
+ export { ZEPHYR_DISPLAY_PROFILES, DEFAULT_ZEPHYR_DISPLAY_PROFILE, BUILT_IN_PROFILES } from './profiles.js';
22
+ export type { ZephyrDisplayProfile } from './profiles.js';
23
+
24
+ export interface DisplayState {
25
+ initialized: boolean;
26
+ profile: ZephyrDisplayProfile;
27
+ }
28
+
29
+ /** Fresh display state (used by the strategy per-build). */
30
+ export function newDisplayState(): DisplayState {
31
+ return { initialized: false, profile: DEFAULT_ZEPHYR_DISPLAY_PROFILE };
32
+ }
33
+
34
+ /**
35
+ * Resolve a display.* op. Returns { code } for statement ops, undefined when
36
+ * the op is not recognized.
37
+ */
38
+ export function resolveZephyrDisplayOp(
39
+ op: DisplayHALOp,
40
+ state: DisplayState,
41
+ ): { code?: string; expression?: string } | undefined {
42
+ const o = op as any;
43
+
44
+ // display.init seeds the state. The probe payload for init has no profile
45
+ // fields, so use the default profile.
46
+ if (op.operation === 'display.init') {
47
+ state.initialized = true;
48
+ // Drop-in panels (driver = DT compatible): seed the synthesized profile
49
+ // so the direct-op GFX runtime sizes itself for THIS panel (the mono
50
+ // framebuffer branch keys off the profile's colorFormat/geometry). The
51
+ // op carries the resolved profile's colorFormat — explicit mono wins;
52
+ // otherwise the compatible table decides (ssd1306-class → mono).
53
+ const registered = ZEPHYR_DISPLAY_PROFILES[o.driver as string];
54
+ if (registered) {
55
+ state.profile = registered;
56
+ } else {
57
+ const synth = synthesizeZephyrProfile({
58
+ driver: o.driver as string,
59
+ width: o.width as number,
60
+ height: o.height as number,
61
+ colorFormat: o.colorFormat as string | undefined,
62
+ });
63
+ if (synth) state.profile = synth;
64
+ }
65
+ return { code: 'display_init();' };
66
+ }
67
+
68
+ // For non-init ops: if not initialized (validator probe path), seed the
69
+ // default so the op still lowers.
70
+ if (!state.initialized) {
71
+ state.initialized = true;
72
+ state.profile = DEFAULT_ZEPHYR_DISPLAY_PROFILE;
73
+ }
74
+
75
+ switch (op.operation) {
76
+ case 'display.fill_rect':
77
+ return { code: `display_fill_rect(${o.x}, ${o.y}, ${o.w}, ${o.h}, ${o.color});` };
78
+ case 'display.draw_rect':
79
+ return { code: `display_draw_rect(${o.x}, ${o.y}, ${o.w}, ${o.h}, ${o.color});` };
80
+ case 'display.draw_text':
81
+ return { code: `display_draw_text(${o.x}, ${o.y}, "${o.text}", ${o.color});` };
82
+ case 'display.flush':
83
+ return { code: 'display_flush();' };
84
+ default:
85
+ return undefined;
86
+ }
87
+ }
@@ -0,0 +1,183 @@
1
+ // ---------------------------------------------------------------------------
2
+ // App-local CS-holding MIPI DBI host — the 'local-hold-cs' dbiHost.
3
+ //
4
+ // Implements the mipi-dbi host API for the overlay's zephyr,mipi-dbi-spi node
5
+ // with CS held across each command+data burst (GPIO-managed, DC toggled
6
+ // mid-burst — the Adafruit ST77xx protocol the direct transport verified on
7
+ // the clone-ST7796S rig). The stock mipi-dbi-spi driver issues the command
8
+ // byte and its parameters as two CS-deasserted SPI transactions, which
9
+ // scrambles clone ST77xx panels; the mipi-hold-cs DT knob that would fix it
10
+ // is documented by the binding but unimplemented in Zephyr 4.4.2. This host
11
+ // is that behavior, emitted app-locally — the stock bridge stays compiled
12
+ // out (CONFIG_MIPI_DBI_SPI=n) and the in-tree panel driver (e.g.
13
+ // sitronix,st7796s, CONFIG_ST7796S=y) binds on top unchanged.
14
+ //
15
+ // A bus host is a normal Zephyr extension point (11 ship in-tree); once
16
+ // upstream implements mipi-hold-cs, this section retires and the profile's
17
+ // dbiHost flips back to 'spi-bridge'.
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
+
23
+ import type { ZephyrDisplayProfile } from "./profiles.js";
24
+
25
+ /** Build the local CS-holding mipi-dbi host for a profile. Emits a device at
26
+ * the overlay's bridge node (bridgeLabel) implementing
27
+ * struct mipi_dbi_driver_api, plus the includes it needs. The panel driver
28
+ * reaches it via DEVICE_DT_GET(DT_INST_PARENT(display0)). */
29
+ export function localHoldCsDbiHost(profile: ZephyrDisplayProfile): {
30
+ includes: string[];
31
+ functions: string;
32
+ } {
33
+ const bus = profile.busLabel ?? 'spi2';
34
+ const bridge = profile.bridgeLabel ?? 'mipi_dbi';
35
+
36
+ const includes = [
37
+ `#include <zephyr/drivers/mipi_dbi.h>`,
38
+ `#include <zephyr/drivers/spi.h>`,
39
+ ];
40
+
41
+ const functions = `
42
+ // ── App-local MIPI DBI host (CS held across command+data) ───────────────
43
+ // The mipi-hold-cs behavior the zephyr,mipi-dbi-spi child binding documents:
44
+ // CS asserted by GPIO around each complete command+data burst (or whole
45
+ // display write), DC toggled mid-burst. The spi_config carried in the
46
+ // panel's mipi_dbi_config is used verbatim EXCEPT .cs, which this host
47
+ // nulls so the SPI driver never touches the CS line mid-burst.
48
+ static const struct gpio_dt_spec __tc_dbi_dc = GPIO_DT_SPEC_GET(DT_NODELABEL(${bridge}), dc_gpios);
49
+ static const struct gpio_dt_spec __tc_dbi_rst = GPIO_DT_SPEC_GET(DT_NODELABEL(${bridge}), reset_gpios);
50
+ // CS comes from the SPI bus node's cs-gpios, selected by the panel's
51
+ // dbi config .slave (= reg). The overlay puts the panel at index 0 and an
52
+ // SPI touch controller, if any, at index 1 — each owner drives its own CS.
53
+ static const struct gpio_dt_spec __tc_dbi_cs[] = {
54
+ #if DT_PROP_LEN(DT_NODELABEL(${bus}), cs_gpios) > 0
55
+ GPIO_DT_SPEC_GET_BY_IDX(DT_NODELABEL(${bus}), cs_gpios, 0),
56
+ #endif
57
+ #if DT_PROP_LEN(DT_NODELABEL(${bus}), cs_gpios) > 1
58
+ GPIO_DT_SPEC_GET_BY_IDX(DT_NODELABEL(${bus}), cs_gpios, 1),
59
+ #endif
60
+ };
61
+ #define __TC_DBI_CS_COUNT (sizeof(__tc_dbi_cs) / sizeof(__tc_dbi_cs[0]))
62
+ // Staging buffer for display writes: display_write buffers may live in PSRAM
63
+ // (not DMA-capable on ESP32); chunked SRAM staging is the pattern the direct
64
+ // transport proved on this rig. Reused, never per-frame (AGENTS.md).
65
+ static uint8_t __tc_dbi_bounce[4096];
66
+
67
+ static const struct gpio_dt_spec* __tc_dbi_cs_for(const struct mipi_dbi_config* cfg) {
68
+ if (cfg->config.slave >= __TC_DBI_CS_COUNT) return nullptr;
69
+ return &__tc_dbi_cs[cfg->config.slave];
70
+ }
71
+
72
+ // One command byte (DC low) + its parameters (DC high) under one CS burst.
73
+ static int __tc_dbi_command_write(const struct device* /*dev*/,
74
+ const struct mipi_dbi_config* cfg,
75
+ uint8_t cmd, const uint8_t* data, size_t len) {
76
+ const struct gpio_dt_spec* cs = __tc_dbi_cs_for(cfg);
77
+ if (cs == nullptr) return -EIO;
78
+ struct spi_config sc = cfg->config;
79
+ // This host owns CS — keep the SPI core off it. Both fields matter in
80
+ // Zephyr 4.4: the core gates on cs.cs_is_gpio (NOT the port pointer), and
81
+ // leaving it set makes spi_context_cs_control deref the port-less spec
82
+ // (EXCCAUSE 28 on the first panel init command — verified on hardware).
83
+ sc.cs.cs_is_gpio = false;
84
+ sc.cs.gpio.port = nullptr;
85
+ struct spi_buf __cb = { &cmd, 1 };
86
+ struct spi_buf_set __csb = { &__cb, 1 };
87
+ gpio_pin_set_dt(cs, 1);
88
+ gpio_pin_set_dt(&__tc_dbi_dc, 0);
89
+ int ret = spi_write(DEVICE_DT_GET(DT_NODELABEL(${bus})), &sc, &__csb);
90
+ if ((ret == 0) && (len > 0)) {
91
+ struct spi_buf __db = { const_cast<uint8_t*>(data), len };
92
+ struct spi_buf_set __dsb = { &__db, 1 };
93
+ gpio_pin_set_dt(&__tc_dbi_dc, 1);
94
+ ret = spi_write(DEVICE_DT_GET(DT_NODELABEL(${bus})), &sc, &__dsb);
95
+ }
96
+ gpio_pin_set_dt(cs, 0);
97
+ return ret;
98
+ }
99
+
100
+ // Write-only host: the bridge node carries 'write-only' (no MISO readback
101
+ // path is wired for these panels).
102
+ static int __tc_dbi_command_read(const struct device* /*dev*/,
103
+ const struct mipi_dbi_config* /*cfg*/,
104
+ uint8_t* /*cmds*/, size_t /*num_cmds*/,
105
+ uint8_t* /*response*/, size_t /*len*/) {
106
+ return -ENOTSUP;
107
+ }
108
+
109
+ // Stream desc->buf_size bytes to the panel (DC high) under one CS assertion,
110
+ // chunked through the SRAM bounce buffer.
111
+ static int __tc_dbi_write_display(const struct device* /*dev*/,
112
+ const struct mipi_dbi_config* cfg,
113
+ const uint8_t* framebuf,
114
+ struct display_buffer_descriptor* desc,
115
+ enum display_pixel_format /*pixfmt*/) {
116
+ const struct gpio_dt_spec* cs = __tc_dbi_cs_for(cfg);
117
+ if (cs == nullptr) return -EIO;
118
+ struct spi_config sc = cfg->config;
119
+ sc.cs.cs_is_gpio = false;
120
+ sc.cs.gpio.port = nullptr;
121
+ gpio_pin_set_dt(cs, 1);
122
+ gpio_pin_set_dt(&__tc_dbi_dc, 1);
123
+ const uint8_t* p = framebuf;
124
+ size_t remaining = desc->buf_size;
125
+ int ret = 0;
126
+ while ((remaining > 0) && (ret == 0)) {
127
+ size_t chunk = (remaining > sizeof(__tc_dbi_bounce)) ? sizeof(__tc_dbi_bounce) : remaining;
128
+ memcpy(__tc_dbi_bounce, p, chunk);
129
+ struct spi_buf __b = { __tc_dbi_bounce, chunk };
130
+ struct spi_buf_set __s = { &__b, 1 };
131
+ ret = spi_write(DEVICE_DT_GET(DT_NODELABEL(${bus})), &sc, &__s);
132
+ p += chunk;
133
+ remaining -= chunk;
134
+ }
135
+ gpio_pin_set_dt(cs, 0);
136
+ return ret;
137
+ }
138
+
139
+ // Assert reset for the requested delay, then release (the stock host's
140
+ // semantics — st7796s calls mipi_dbi_reset(host, 100) at init).
141
+ static int __tc_dbi_reset(const struct device* /*dev*/, k_timeout_t delay) {
142
+ gpio_pin_set_dt(&__tc_dbi_rst, 1);
143
+ k_sleep(delay);
144
+ return gpio_pin_set_dt(&__tc_dbi_rst, 0);
145
+ }
146
+
147
+ // Nothing is locked between calls (no per-panel spi reconfig here).
148
+ static int __tc_dbi_release(const struct device* /*dev*/,
149
+ const struct mipi_dbi_config* /*cfg*/) {
150
+ return 0;
151
+ }
152
+
153
+ static DEVICE_API(mipi_dbi, __tc_dbi_api) = {
154
+ __tc_dbi_command_write,
155
+ __tc_dbi_command_read,
156
+ __tc_dbi_write_display,
157
+ __tc_dbi_reset,
158
+ __tc_dbi_release,
159
+ };
160
+
161
+ static int __tc_dbi_host_init(const struct device* /*dev*/) {
162
+ if (!device_is_ready(__tc_dbi_dc.port)) return -ENODEV;
163
+ gpio_pin_configure_dt(&__tc_dbi_dc, GPIO_OUTPUT);
164
+ if (device_is_ready(__tc_dbi_rst.port)) {
165
+ gpio_pin_configure_dt(&__tc_dbi_rst, GPIO_OUTPUT_INACTIVE);
166
+ }
167
+ for (size_t i = 0; i < __TC_DBI_CS_COUNT; i++) {
168
+ if (device_is_ready(__tc_dbi_cs[i].port)) {
169
+ gpio_pin_configure_dt(&__tc_dbi_cs[i], GPIO_OUTPUT_INACTIVE);
170
+ }
171
+ }
172
+ return 0;
173
+ }
174
+
175
+ // Same init level/priority slot the stock bridge uses — panel drivers init
176
+ // later (CONFIG_DISPLAY_INIT_PRIORITY=85 > MIPI_DBI_INIT_PRIORITY=80), so
177
+ // the host's GPIOs are live before the panel's init commands run.
178
+ DEVICE_DT_DEFINE(DT_NODELABEL(${bridge}), __tc_dbi_host_init, NULL, NULL, NULL,
179
+ POST_KERNEL, CONFIG_MIPI_DBI_INIT_PRIORITY, &__tc_dbi_api);
180
+ `;
181
+
182
+ return { includes, functions };
183
+ }