Skip to content

Commit a1c04cd

Browse files
committed
Add support for fixed Zephyr displays
This adds zephyr_display. It acts similar to BusDisplay because we write regions to update to Zephyr.
1 parent fd4226b commit a1c04cd

19 files changed

Lines changed: 1082 additions & 25 deletions

File tree

locale/circuitpython.pot

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ msgstr ""
165165
msgid "%q must be %d"
166166
msgstr ""
167167

168-
#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c
169-
#: shared-bindings/displayio/Bitmap.c
168+
#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c
169+
#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c
170170
#: shared-bindings/framebufferio/FramebufferDisplay.c
171171
#: shared-bindings/is31fl3741/FrameBuffer.c
172172
#: shared-bindings/rgbmatrix/RGBMatrix.c
@@ -458,6 +458,7 @@ msgstr ""
458458
msgid ", in %q\n"
459459
msgstr ""
460460

461+
#: ports/zephyr-cp/bindings/zephyr_display/Display.c
461462
#: shared-bindings/busdisplay/BusDisplay.c
462463
#: shared-bindings/epaperdisplay/EPaperDisplay.c
463464
#: shared-bindings/framebufferio/FramebufferDisplay.c
@@ -646,6 +647,7 @@ msgstr ""
646647
msgid "Baudrate not supported by peripheral"
647648
msgstr ""
648649

650+
#: ports/zephyr-cp/common-hal/zephyr_display/Display.c
649651
#: shared-module/busdisplay/BusDisplay.c
650652
#: shared-module/framebufferio/FramebufferDisplay.c
651653
msgid "Below minimum frame rate"
@@ -668,6 +670,7 @@ msgstr ""
668670
msgid "Both RX and TX required for flow control"
669671
msgstr ""
670672

673+
#: ports/zephyr-cp/bindings/zephyr_display/Display.c
671674
#: shared-bindings/busdisplay/BusDisplay.c
672675
#: shared-bindings/framebufferio/FramebufferDisplay.c
673676
msgid "Brightness not adjustable"
@@ -941,6 +944,10 @@ msgstr ""
941944
msgid "Display must have a 16 bit colorspace."
942945
msgstr ""
943946

947+
#: ports/zephyr-cp/common-hal/zephyr_display/Display.c
948+
msgid "Display not ready"
949+
msgstr ""
950+
944951
#: shared-bindings/busdisplay/BusDisplay.c
945952
#: shared-bindings/epaperdisplay/EPaperDisplay.c
946953
#: shared-bindings/framebufferio/FramebufferDisplay.c
@@ -1144,6 +1151,7 @@ msgstr ""
11441151
msgid "Generic Failure"
11451152
msgstr ""
11461153

1154+
#: ports/zephyr-cp/bindings/zephyr_display/Display.c
11471155
#: shared-bindings/framebufferio/FramebufferDisplay.c
11481156
#: shared-module/busdisplay/BusDisplay.c
11491157
#: shared-module/framebufferio/FramebufferDisplay.c
@@ -2404,6 +2412,10 @@ msgstr ""
24042412
msgid "Update failed"
24052413
msgstr ""
24062414

2415+
#: ports/zephyr-cp/bindings/zephyr_display/Display.c
2416+
msgid "Use board.DISPLAY"
2417+
msgstr ""
2418+
24072419
#: ports/zephyr-cp/common-hal/busio/I2C.c
24082420
#: ports/zephyr-cp/common-hal/busio/SPI.c
24092421
#: ports/zephyr-cp/common-hal/busio/UART.c
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#include "bindings/zephyr_display/Display.h"
8+
9+
#include "py/objproperty.h"
10+
#include "py/objtype.h"
11+
#include "py/runtime.h"
12+
#include "shared-bindings/displayio/Group.h"
13+
#include "shared-module/displayio/__init__.h"
14+
15+
static mp_obj_t zephyr_display_display_make_new(const mp_obj_type_t *type,
16+
size_t n_args,
17+
size_t n_kw,
18+
const mp_obj_t *all_args) {
19+
(void)type;
20+
(void)n_args;
21+
(void)n_kw;
22+
(void)all_args;
23+
mp_raise_NotImplementedError(MP_ERROR_TEXT("Use board.DISPLAY"));
24+
return mp_const_none;
25+
}
26+
27+
static zephyr_display_display_obj_t *native_display(mp_obj_t display_obj) {
28+
mp_obj_t native = mp_obj_cast_to_native_base(display_obj, &zephyr_display_display_type);
29+
mp_obj_assert_native_inited(native);
30+
return MP_OBJ_TO_PTR(native);
31+
}
32+
33+
static mp_obj_t zephyr_display_display_obj_show(mp_obj_t self_in, mp_obj_t group_in) {
34+
(void)self_in;
35+
(void)group_in;
36+
mp_raise_AttributeError(MP_ERROR_TEXT(".show(x) removed. Use .root_group = x"));
37+
return mp_const_none;
38+
}
39+
MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_show_obj, zephyr_display_display_obj_show);
40+
41+
static mp_obj_t zephyr_display_display_obj_refresh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
42+
enum {
43+
ARG_target_frames_per_second,
44+
ARG_minimum_frames_per_second,
45+
};
46+
static const mp_arg_t allowed_args[] = {
47+
{ MP_QSTR_target_frames_per_second, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} },
48+
{ MP_QSTR_minimum_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
49+
};
50+
51+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
52+
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
53+
54+
zephyr_display_display_obj_t *self = native_display(pos_args[0]);
55+
56+
uint32_t maximum_ms_per_real_frame = NO_FPS_LIMIT;
57+
mp_int_t minimum_frames_per_second = args[ARG_minimum_frames_per_second].u_int;
58+
if (minimum_frames_per_second > 0) {
59+
maximum_ms_per_real_frame = 1000 / minimum_frames_per_second;
60+
}
61+
62+
uint32_t target_ms_per_frame;
63+
if (args[ARG_target_frames_per_second].u_obj == mp_const_none) {
64+
target_ms_per_frame = NO_FPS_LIMIT;
65+
} else {
66+
target_ms_per_frame = 1000 / mp_obj_get_int(args[ARG_target_frames_per_second].u_obj);
67+
}
68+
69+
return mp_obj_new_bool(common_hal_zephyr_display_display_refresh(
70+
self,
71+
target_ms_per_frame,
72+
maximum_ms_per_real_frame));
73+
}
74+
MP_DEFINE_CONST_FUN_OBJ_KW(zephyr_display_display_refresh_obj, 1, zephyr_display_display_obj_refresh);
75+
76+
static mp_obj_t zephyr_display_display_obj_get_auto_refresh(mp_obj_t self_in) {
77+
zephyr_display_display_obj_t *self = native_display(self_in);
78+
return mp_obj_new_bool(common_hal_zephyr_display_display_get_auto_refresh(self));
79+
}
80+
MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_auto_refresh_obj, zephyr_display_display_obj_get_auto_refresh);
81+
82+
static mp_obj_t zephyr_display_display_obj_set_auto_refresh(mp_obj_t self_in, mp_obj_t auto_refresh) {
83+
zephyr_display_display_obj_t *self = native_display(self_in);
84+
common_hal_zephyr_display_display_set_auto_refresh(self, mp_obj_is_true(auto_refresh));
85+
return mp_const_none;
86+
}
87+
MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_auto_refresh_obj, zephyr_display_display_obj_set_auto_refresh);
88+
89+
MP_PROPERTY_GETSET(zephyr_display_display_auto_refresh_obj,
90+
(mp_obj_t)&zephyr_display_display_get_auto_refresh_obj,
91+
(mp_obj_t)&zephyr_display_display_set_auto_refresh_obj);
92+
93+
static mp_obj_t zephyr_display_display_obj_get_brightness(mp_obj_t self_in) {
94+
zephyr_display_display_obj_t *self = native_display(self_in);
95+
mp_float_t brightness = common_hal_zephyr_display_display_get_brightness(self);
96+
if (brightness < 0) {
97+
mp_raise_RuntimeError(MP_ERROR_TEXT("Brightness not adjustable"));
98+
}
99+
return mp_obj_new_float(brightness);
100+
}
101+
MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_brightness_obj, zephyr_display_display_obj_get_brightness);
102+
103+
static mp_obj_t zephyr_display_display_obj_set_brightness(mp_obj_t self_in, mp_obj_t brightness_obj) {
104+
zephyr_display_display_obj_t *self = native_display(self_in);
105+
mp_float_t brightness = mp_obj_get_float(brightness_obj);
106+
if (brightness < 0.0f || brightness > 1.0f) {
107+
mp_raise_ValueError_varg(MP_ERROR_TEXT("%q must be %d-%d"), MP_QSTR_brightness, 0, 1);
108+
}
109+
bool ok = common_hal_zephyr_display_display_set_brightness(self, brightness);
110+
if (!ok) {
111+
mp_raise_RuntimeError(MP_ERROR_TEXT("Brightness not adjustable"));
112+
}
113+
return mp_const_none;
114+
}
115+
MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_brightness_obj, zephyr_display_display_obj_set_brightness);
116+
117+
MP_PROPERTY_GETSET(zephyr_display_display_brightness_obj,
118+
(mp_obj_t)&zephyr_display_display_get_brightness_obj,
119+
(mp_obj_t)&zephyr_display_display_set_brightness_obj);
120+
121+
static mp_obj_t zephyr_display_display_obj_get_width(mp_obj_t self_in) {
122+
zephyr_display_display_obj_t *self = native_display(self_in);
123+
return MP_OBJ_NEW_SMALL_INT(common_hal_zephyr_display_display_get_width(self));
124+
}
125+
MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_width_obj, zephyr_display_display_obj_get_width);
126+
MP_PROPERTY_GETTER(zephyr_display_display_width_obj, (mp_obj_t)&zephyr_display_display_get_width_obj);
127+
128+
static mp_obj_t zephyr_display_display_obj_get_height(mp_obj_t self_in) {
129+
zephyr_display_display_obj_t *self = native_display(self_in);
130+
return MP_OBJ_NEW_SMALL_INT(common_hal_zephyr_display_display_get_height(self));
131+
}
132+
MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_height_obj, zephyr_display_display_obj_get_height);
133+
MP_PROPERTY_GETTER(zephyr_display_display_height_obj, (mp_obj_t)&zephyr_display_display_get_height_obj);
134+
135+
static mp_obj_t zephyr_display_display_obj_get_rotation(mp_obj_t self_in) {
136+
zephyr_display_display_obj_t *self = native_display(self_in);
137+
return MP_OBJ_NEW_SMALL_INT(common_hal_zephyr_display_display_get_rotation(self));
138+
}
139+
MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_rotation_obj, zephyr_display_display_obj_get_rotation);
140+
141+
static mp_obj_t zephyr_display_display_obj_set_rotation(mp_obj_t self_in, mp_obj_t value) {
142+
zephyr_display_display_obj_t *self = native_display(self_in);
143+
common_hal_zephyr_display_display_set_rotation(self, mp_obj_get_int(value));
144+
return mp_const_none;
145+
}
146+
MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_rotation_obj, zephyr_display_display_obj_set_rotation);
147+
148+
MP_PROPERTY_GETSET(zephyr_display_display_rotation_obj,
149+
(mp_obj_t)&zephyr_display_display_get_rotation_obj,
150+
(mp_obj_t)&zephyr_display_display_set_rotation_obj);
151+
152+
static mp_obj_t zephyr_display_display_obj_get_root_group(mp_obj_t self_in) {
153+
zephyr_display_display_obj_t *self = native_display(self_in);
154+
return common_hal_zephyr_display_display_get_root_group(self);
155+
}
156+
MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_root_group_obj, zephyr_display_display_obj_get_root_group);
157+
158+
static mp_obj_t zephyr_display_display_obj_set_root_group(mp_obj_t self_in, mp_obj_t group_in) {
159+
zephyr_display_display_obj_t *self = native_display(self_in);
160+
displayio_group_t *group = NULL;
161+
if (group_in != mp_const_none) {
162+
group = MP_OBJ_TO_PTR(native_group(group_in));
163+
}
164+
165+
bool ok = common_hal_zephyr_display_display_set_root_group(self, group);
166+
if (!ok) {
167+
mp_raise_ValueError(MP_ERROR_TEXT("Group already used"));
168+
}
169+
return mp_const_none;
170+
}
171+
MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_root_group_obj, zephyr_display_display_obj_set_root_group);
172+
173+
MP_PROPERTY_GETSET(zephyr_display_display_root_group_obj,
174+
(mp_obj_t)&zephyr_display_display_get_root_group_obj,
175+
(mp_obj_t)&zephyr_display_display_set_root_group_obj);
176+
177+
static const mp_rom_map_elem_t zephyr_display_display_locals_dict_table[] = {
178+
{ MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&zephyr_display_display_show_obj) },
179+
{ MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&zephyr_display_display_refresh_obj) },
180+
181+
{ MP_ROM_QSTR(MP_QSTR_auto_refresh), MP_ROM_PTR(&zephyr_display_display_auto_refresh_obj) },
182+
{ MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&zephyr_display_display_brightness_obj) },
183+
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&zephyr_display_display_width_obj) },
184+
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&zephyr_display_display_height_obj) },
185+
{ MP_ROM_QSTR(MP_QSTR_rotation), MP_ROM_PTR(&zephyr_display_display_rotation_obj) },
186+
{ MP_ROM_QSTR(MP_QSTR_root_group), MP_ROM_PTR(&zephyr_display_display_root_group_obj) },
187+
};
188+
static MP_DEFINE_CONST_DICT(zephyr_display_display_locals_dict, zephyr_display_display_locals_dict_table);
189+
190+
MP_DEFINE_CONST_OBJ_TYPE(
191+
zephyr_display_display_type,
192+
MP_QSTR_Display,
193+
MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS,
194+
make_new, zephyr_display_display_make_new,
195+
locals_dict, &zephyr_display_display_locals_dict);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#pragma once
8+
9+
#include "shared-module/displayio/Group.h"
10+
#include "common-hal/zephyr_display/Display.h"
11+
12+
extern const mp_obj_type_t zephyr_display_display_type;
13+
14+
#define NO_FPS_LIMIT 0xffffffff
15+
16+
void common_hal_zephyr_display_display_construct_from_device(zephyr_display_display_obj_t *self,
17+
const struct device *device,
18+
uint16_t rotation,
19+
bool auto_refresh);
20+
21+
bool common_hal_zephyr_display_display_refresh(zephyr_display_display_obj_t *self,
22+
uint32_t target_ms_per_frame,
23+
uint32_t maximum_ms_per_real_frame);
24+
25+
bool common_hal_zephyr_display_display_get_auto_refresh(zephyr_display_display_obj_t *self);
26+
void common_hal_zephyr_display_display_set_auto_refresh(zephyr_display_display_obj_t *self, bool auto_refresh);
27+
28+
uint16_t common_hal_zephyr_display_display_get_width(zephyr_display_display_obj_t *self);
29+
uint16_t common_hal_zephyr_display_display_get_height(zephyr_display_display_obj_t *self);
30+
uint16_t common_hal_zephyr_display_display_get_rotation(zephyr_display_display_obj_t *self);
31+
void common_hal_zephyr_display_display_set_rotation(zephyr_display_display_obj_t *self, int rotation);
32+
33+
mp_float_t common_hal_zephyr_display_display_get_brightness(zephyr_display_display_obj_t *self);
34+
bool common_hal_zephyr_display_display_set_brightness(zephyr_display_display_obj_t *self, mp_float_t brightness);
35+
36+
mp_obj_t common_hal_zephyr_display_display_get_root_group(zephyr_display_display_obj_t *self);
37+
bool common_hal_zephyr_display_display_set_root_group(zephyr_display_display_obj_t *self, displayio_group_t *root_group);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#include <zephyr/drivers/display.h>
8+
9+
#include "py/obj.h"
10+
#include "py/runtime.h"
11+
12+
#include "bindings/zephyr_display/Display.h"
13+
14+
static const mp_rom_map_elem_t zephyr_display_module_globals_table[] = {
15+
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr_display) },
16+
{ MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&zephyr_display_display_type) },
17+
18+
{ MP_ROM_QSTR(MP_QSTR_PIXEL_FORMAT_RGB_888), MP_ROM_INT(PIXEL_FORMAT_RGB_888) },
19+
{ MP_ROM_QSTR(MP_QSTR_PIXEL_FORMAT_MONO01), MP_ROM_INT(PIXEL_FORMAT_MONO01) },
20+
{ MP_ROM_QSTR(MP_QSTR_PIXEL_FORMAT_MONO10), MP_ROM_INT(PIXEL_FORMAT_MONO10) },
21+
{ MP_ROM_QSTR(MP_QSTR_PIXEL_FORMAT_ARGB_8888), MP_ROM_INT(PIXEL_FORMAT_ARGB_8888) },
22+
{ MP_ROM_QSTR(MP_QSTR_PIXEL_FORMAT_RGB_565), MP_ROM_INT(PIXEL_FORMAT_RGB_565) },
23+
{ MP_ROM_QSTR(MP_QSTR_PIXEL_FORMAT_BGR_565), MP_ROM_INT(PIXEL_FORMAT_BGR_565) },
24+
{ MP_ROM_QSTR(MP_QSTR_PIXEL_FORMAT_L_8), MP_ROM_INT(PIXEL_FORMAT_L_8) },
25+
{ MP_ROM_QSTR(MP_QSTR_PIXEL_FORMAT_AL_88), MP_ROM_INT(PIXEL_FORMAT_AL_88) },
26+
};
27+
28+
static MP_DEFINE_CONST_DICT(zephyr_display_module_globals, zephyr_display_module_globals_table);
29+
30+
const mp_obj_module_t zephyr_display_module = {
31+
.base = { &mp_type_module },
32+
.globals = (mp_obj_dict_t *)&zephyr_display_module_globals,
33+
};
34+
35+
MP_REGISTER_MODULE(MP_QSTR_zephyr_display, zephyr_display_module);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#pragma once

ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ canio = false
3737
codeop = false
3838
countio = false
3939
digitalio = true
40-
displayio = true # Zephyr board has busio
40+
displayio = true # Zephyr board has displayio
4141
dotclockframebuffer = false
4242
dualbank = false
4343
epaperdisplay = true # Zephyr board has busio
@@ -111,5 +111,6 @@ vectorio = true # Zephyr board has busio
111111
warnings = true
112112
watchdog = false
113113
wifi = false
114+
zephyr_display = true # Zephyr board has zephyr_display
114115
zephyr_kernel = false
115116
zlib = false

ports/zephyr-cp/boards/native_sim.conf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@ CONFIG_TRACING_GPIO=y
1414
# I2C emulation for testing
1515
CONFIG_I2C_EMUL=y
1616

17+
# Display emulation for display/terminal golden tests.
18+
CONFIG_DISPLAY=y
19+
1720
# EEPROM emulation for testing
1821
CONFIG_EEPROM=y
1922
CONFIG_EEPROM_AT24=y
2023
CONFIG_EEPROM_AT2X_EMUL=y
24+
25+
# Enable native_sim SDL display so board.DISPLAY is backed by zephyr,sdl-dc.
26+
CONFIG_DISPLAY=y
27+
CONFIG_SDL_DISPLAY=y

0 commit comments

Comments
 (0)