Skip to content

Commit 518cace

Browse files
committed
zephyr: Introduce Zephyr displays API.
Allows using zephyr displays. Signed-off-by: Vdragon <mail@massdriver.space>
1 parent b7cfafc commit 518cace

File tree

4 files changed

+361
-0
lines changed

4 files changed

+361
-0
lines changed

ports/zephyr/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ set(MICROPY_SOURCE_PORT
5454
uart_core.c
5555
zephyr_device.c
5656
zephyr_storage.c
57+
zephyr_display.c
5758
mpthreadport.c
5859
)
5960
list(TRANSFORM MICROPY_SOURCE_PORT PREPEND ${MICROPY_PORT_DIR}/)

ports/zephyr/modzephyr.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ static const mp_rom_map_elem_t mp_module_time_globals_table[] = {
8686
#ifdef CONFIG_FLASH_MAP
8787
{ MP_ROM_QSTR(MP_QSTR_FlashArea), MP_ROM_PTR(&zephyr_flash_area_type) },
8888
#endif
89+
#if defined(CONFIG_DISPLAY)
90+
{ MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&zephyr_display_type) },
91+
#endif
8992
};
9093

9194
static MP_DEFINE_CONST_DICT(mp_module_time_globals, mp_module_time_globals_table);

ports/zephyr/modzephyr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,8 @@ extern const mp_obj_type_t zephyr_disk_access_type;
3737
extern const mp_obj_type_t zephyr_flash_area_type;
3838
#endif
3939

40+
#ifdef CONFIG_DISPLAY
41+
extern const mp_obj_type_t zephyr_display_type;
42+
#endif
43+
4044
#endif // MICROPY_INCLUDED_ZEPHYR_MODZEPHYR_H

ports/zephyr/zephyr_display.c

Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2025 MASSDRIVER EI (massdriver.space)
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include <zephyr/device.h>
28+
#include <zephyr/drivers/display.h>
29+
#include <stdlib.h>
30+
31+
#include "modzephyr.h"
32+
#include "zephyr_device.h"
33+
#include "py/runtime.h"
34+
35+
#if defined(CONFIG_DISPLAY)
36+
37+
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_displays)
38+
#define ENUMERATE_DISPLAY_DEVS(node_id, prop, idx) DEVICE_DT_GET(DT_PROP_BY_IDX(node_id, prop, idx)),
39+
40+
static const struct device *zephyr_display_devices[DT_ZEPHYR_DISPLAYS_COUNT] = {
41+
DT_FOREACH_PROP_ELEM(DT_COMPAT_GET_ANY_STATUS_OKAY(zephyr_displays), displays, ENUMERATE_DISPLAY_DEVS)
42+
};
43+
#elif DT_HAS_CHOSEN(zephyr_display)
44+
static const struct device *zephyr_display_devices[] = {
45+
DEVICE_DT_GET(DT_CHOSEN(zephyr_display)),
46+
};
47+
#else
48+
static const struct device *zephyr_display_devices[1];
49+
#endif
50+
51+
const mp_obj_type_t zephyr_display_type;
52+
53+
typedef struct _zephyr_display_obj_t {
54+
mp_obj_base_t base;
55+
const struct device *dev;
56+
} zephyr_display_obj_t;
57+
58+
static void zephyr_display_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
59+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
60+
mp_printf(print, "Display(\"%s\")", self->dev->name);
61+
}
62+
63+
static mp_obj_t zephyr_display_make_new(const mp_obj_type_t *type,
64+
size_t n_args, size_t n_kw, const mp_obj_t *args) {
65+
mp_arg_check_num(n_args, n_kw, 1, 1, false);
66+
const struct device *dev;
67+
68+
if (mp_obj_is_str(args[0])) {
69+
dev = zephyr_device_find(args[0]);
70+
} else {
71+
int id = mp_obj_get_int(args[0]);
72+
73+
if (id < 0 || id >= DT_ZEPHYR_DISPLAYS_COUNT) {
74+
mp_raise_ValueError(MP_ERROR_TEXT("Invalid display ID"));
75+
return mp_const_none;
76+
}
77+
dev = zephyr_display_devices[id];
78+
}
79+
if (!device_is_ready(dev)) {
80+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Display is not ready"));
81+
return mp_const_none;
82+
}
83+
84+
zephyr_display_obj_t *self = mp_obj_malloc(zephyr_display_obj_t, type);
85+
self->dev = dev;
86+
return MP_OBJ_FROM_PTR(self);
87+
}
88+
89+
static mp_obj_t zephyr_display_write(size_t n_args, const mp_obj_t *args_in) {
90+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(args_in[0]);
91+
struct display_capabilities capabilities;
92+
int ret;
93+
size_t len;
94+
mp_buffer_info_t bufinfo;
95+
int x = mp_obj_get_int(args_in[2]);
96+
int y = mp_obj_get_int(args_in[3]);
97+
int sx;
98+
int sy;
99+
100+
mp_get_buffer_raise(args_in[1], &bufinfo, MP_BUFFER_READ);
101+
102+
display_get_capabilities(self->dev, &capabilities);
103+
sx = capabilities.x_resolution;
104+
105+
if (n_args > 4) {
106+
sx = mp_obj_get_int(args_in[4]);
107+
}
108+
sy = bufinfo.len / (sx * DISPLAY_BITS_PER_PIXEL(capabilities.current_pixel_format) / 8);
109+
110+
if (n_args > 5) {
111+
sy = mp_obj_get_int(args_in[5]);
112+
}
113+
114+
len = sx * sy * DISPLAY_BITS_PER_PIXEL(capabilities.current_pixel_format) / 8;
115+
116+
if (len > bufinfo.len) {
117+
mp_raise_ValueError(MP_ERROR_TEXT("Buffer is shorter than size requires"));
118+
}
119+
120+
const struct display_buffer_descriptor desc = {
121+
.buf_size = len,
122+
.width = sx,
123+
.height = sy,
124+
.pitch = sx,
125+
};
126+
127+
ret = display_write(self->dev, x, y, &desc, bufinfo.buf);
128+
if (ret < 0) {
129+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't write to display"));
130+
}
131+
return mp_obj_new_int(ret);
132+
}
133+
134+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(zephyr_display_write_obj, 2, 6, zephyr_display_write);
135+
136+
137+
static mp_obj_t zephyr_display_rgb(size_t n_args, const mp_obj_t *args_in) {
138+
(void)n_args;
139+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(args_in[0]);
140+
struct display_capabilities capabilities;
141+
142+
int r = mp_obj_get_int(args_in[1]);
143+
int g = mp_obj_get_int(args_in[2]);
144+
int b = mp_obj_get_int(args_in[3]);
145+
146+
display_get_capabilities(self->dev, &capabilities);
147+
148+
switch (capabilities.current_pixel_format) {
149+
case PIXEL_FORMAT_MONO10:
150+
if (r > 127 || g > 127 || b > 127) {
151+
return mp_obj_new_int(0x0);
152+
} else {
153+
return mp_obj_new_int(0xFF);
154+
}
155+
case PIXEL_FORMAT_MONO01:
156+
if (r > 127 || g > 127 || b > 127) {
157+
return mp_obj_new_int(0xFF);
158+
} else {
159+
return mp_obj_new_int(0x0);
160+
}
161+
case PIXEL_FORMAT_L_8:
162+
return mp_obj_new_int((r + g + b) / 3);
163+
case PIXEL_FORMAT_RGB_565:
164+
return mp_obj_new_int((r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6));
165+
default:
166+
break;
167+
}
168+
mp_raise_ValueError(MP_ERROR_TEXT("Not a framebuf pixel format"));
169+
return mp_const_none;
170+
}
171+
172+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(zephyr_display_rgb_obj, 4, 4, zephyr_display_rgb);
173+
174+
static mp_obj_t zephyr_display_framebuf_current_format(const mp_obj_t self_in) {
175+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
176+
struct display_capabilities capabilities;
177+
int format = -1;
178+
179+
display_get_capabilities(self->dev, &capabilities);
180+
181+
switch (capabilities.current_pixel_format) {
182+
case PIXEL_FORMAT_MONO10:
183+
case PIXEL_FORMAT_MONO01:
184+
if (capabilities.screen_info & SCREEN_INFO_MONO_VTILED
185+
&& capabilities.screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
186+
format = -1; // no VMSB
187+
} else if (capabilities.screen_info & SCREEN_INFO_MONO_VTILED) {
188+
format = 0; // FRAMEBUF_MVLSB
189+
} else if (capabilities.screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
190+
format = 4; // FRAMEBUF_MHMSB
191+
} else {
192+
format = 3; // FRAMEBUF_MHLSB
193+
}
194+
break;
195+
case PIXEL_FORMAT_L_8:
196+
format = 6; // FRAMEBUF_GS8
197+
break;
198+
case PIXEL_FORMAT_RGB_565:
199+
format = 1; // FRAMEBUF_RGB565
200+
break;
201+
default:
202+
format = -1;
203+
}
204+
if (format < 0) {
205+
mp_raise_ValueError(MP_ERROR_TEXT("Not a framebuf pixel format"));
206+
return mp_const_none;
207+
}
208+
return mp_obj_new_int(format);
209+
}
210+
211+
static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_framebuf_current_format_obj, zephyr_display_framebuf_current_format);
212+
213+
static mp_obj_t zephyr_display_formats(const mp_obj_t self_in) {
214+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
215+
struct display_capabilities capabilities;
216+
mp_obj_t tuple_buf[32];
217+
size_t tuple_cnt = 0;
218+
219+
display_get_capabilities(self->dev, &capabilities);
220+
221+
for (int i = 0; i < 32; i++) {
222+
if (capabilities.supported_pixel_formats & 1U << i) {
223+
tuple_buf[tuple_cnt] = mp_obj_new_int(1U << i);
224+
tuple_cnt++;
225+
}
226+
}
227+
228+
return mp_obj_new_tuple(tuple_cnt, tuple_buf);
229+
}
230+
231+
static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_formats_obj, zephyr_display_formats);
232+
233+
static mp_obj_t zephyr_display_current_format(size_t n_args, const mp_obj_t *args_in) {
234+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(args_in[0]);
235+
struct display_capabilities capabilities;
236+
int ret;
237+
238+
if (n_args > 1) {
239+
ret = display_set_pixel_format(self->dev, mp_obj_get_int(args_in[1]));
240+
if (ret < 0) {
241+
mp_raise_ValueError(MP_ERROR_TEXT("Invalid pixel format"));
242+
}
243+
}
244+
245+
display_get_capabilities(self->dev, &capabilities);
246+
247+
return mp_obj_new_int(capabilities.current_pixel_format);
248+
}
249+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(zephyr_display_current_format_obj, 1, 2, zephyr_display_current_format);
250+
251+
static mp_obj_t zephyr_display_blanking_on(const mp_obj_t self_in) {
252+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
253+
254+
if (display_blanking_on(self->dev) < 0) {
255+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't set blanking"));
256+
}
257+
258+
return mp_const_none;
259+
}
260+
261+
static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_blanking_on_obj, zephyr_display_blanking_on);
262+
263+
static mp_obj_t zephyr_display_blanking_off(const mp_obj_t self_in) {
264+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
265+
266+
if (display_blanking_off(self->dev) < 0) {
267+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't set blanking"));
268+
}
269+
270+
return mp_const_none;
271+
}
272+
273+
static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_blanking_off_obj, zephyr_display_blanking_off);
274+
275+
static mp_obj_t zephyr_display_set_brightness(const mp_obj_t self_in, mp_obj_t arg) {
276+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
277+
278+
if (display_set_brightness(self->dev, mp_obj_get_int(arg)) < 0) {
279+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't set brightness"));
280+
}
281+
282+
return mp_const_none;
283+
}
284+
285+
static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_set_brightness_obj, zephyr_display_set_brightness);
286+
287+
static mp_obj_t zephyr_display_set_contrast(const mp_obj_t self_in, mp_obj_t arg) {
288+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
289+
290+
if (display_set_contrast(self->dev, mp_obj_get_int(arg)) < 0) {
291+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't set contrast"));
292+
}
293+
294+
return mp_const_none;
295+
}
296+
297+
static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_set_contrast_obj, zephyr_display_set_contrast);
298+
299+
static mp_obj_t zephyr_display_set_orientation(const mp_obj_t self_in, mp_obj_t arg) {
300+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
301+
302+
if (mp_obj_get_int(arg) > DISPLAY_ORIENTATION_ROTATED_270 || mp_obj_get_int(arg) < 0) {
303+
mp_raise_ValueError(MP_ERROR_TEXT("Invalid orientation"));
304+
}
305+
306+
if (display_set_orientation(self->dev, mp_obj_get_int(arg)) < 0) {
307+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't set Orientation"));
308+
}
309+
310+
return mp_const_none;
311+
}
312+
313+
static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_set_orientation_obj, zephyr_display_set_orientation);
314+
315+
static const mp_rom_map_elem_t zephyr_display_locals_dict_table[] = {
316+
/* this class */
317+
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&zephyr_display_write_obj) },
318+
{ MP_ROM_QSTR(MP_QSTR_rgb), MP_ROM_PTR(&zephyr_display_rgb_obj) },
319+
{ MP_ROM_QSTR(MP_QSTR_framebuf_current_format), MP_ROM_PTR(&zephyr_display_framebuf_current_format_obj) },
320+
{ MP_ROM_QSTR(MP_QSTR_formats), MP_ROM_PTR(&zephyr_display_formats_obj) },
321+
{ MP_ROM_QSTR(MP_QSTR_current_format), MP_ROM_PTR(&zephyr_display_current_format_obj) },
322+
{ MP_ROM_QSTR(MP_QSTR_blanking_on), MP_ROM_PTR(&zephyr_display_blanking_on_obj) },
323+
{ MP_ROM_QSTR(MP_QSTR_blanking_off), MP_ROM_PTR(&zephyr_display_blanking_off_obj) },
324+
{ MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&zephyr_display_set_brightness_obj) },
325+
{ MP_ROM_QSTR(MP_QSTR_set_contrast), MP_ROM_PTR(&zephyr_display_set_contrast_obj) },
326+
{ MP_ROM_QSTR(MP_QSTR_set_orientation), MP_ROM_PTR(&zephyr_display_set_orientation_obj) },
327+
{ MP_ROM_QSTR(MP_QSTR_display_count), MP_ROM_INT(DT_ZEPHYR_DISPLAYS_COUNT) },
328+
{ MP_ROM_QSTR(MP_QSTR_ORIENTATION_NORMAL), MP_ROM_INT(DISPLAY_ORIENTATION_NORMAL) },
329+
{ MP_ROM_QSTR(MP_QSTR_ORIENTATION_90), MP_ROM_INT(DISPLAY_ORIENTATION_ROTATED_90) },
330+
{ MP_ROM_QSTR(MP_QSTR_ORIENTATION_180), MP_ROM_INT(DISPLAY_ORIENTATION_ROTATED_180) },
331+
{ MP_ROM_QSTR(MP_QSTR_ORIENTATION_270), MP_ROM_INT(DISPLAY_ORIENTATION_ROTATED_270) },
332+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_RGB_888), MP_ROM_INT(PIXEL_FORMAT_RGB_888) },
333+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_MONO01), MP_ROM_INT(PIXEL_FORMAT_MONO01) },
334+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_MONO10), MP_ROM_INT(PIXEL_FORMAT_MONO10) },
335+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_ARGB_8888), MP_ROM_INT(PIXEL_FORMAT_ARGB_8888) },
336+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_RGB_565), MP_ROM_INT(PIXEL_FORMAT_RGB_565) },
337+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_BGR_565), MP_ROM_INT(PIXEL_FORMAT_BGR_565) },
338+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_L_8), MP_ROM_INT(PIXEL_FORMAT_L_8) },
339+
#ifdef PIXEL_FORMAT_AL_88 /* will be in Zephyr 4.3 */
340+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_AL_88), MP_ROM_INT(PIXEL_FORMAT_AL_88) },
341+
#endif
342+
};
343+
static MP_DEFINE_CONST_DICT(zephyr_display_locals_dict, zephyr_display_locals_dict_table);
344+
345+
MP_DEFINE_CONST_OBJ_TYPE(
346+
zephyr_display_type,
347+
MP_QSTR_Display,
348+
MP_TYPE_FLAG_NONE,
349+
make_new, zephyr_display_make_new,
350+
print, zephyr_display_print,
351+
locals_dict, &zephyr_display_locals_dict
352+
);
353+
#endif // defined(CONFIG_DISPLAY)

0 commit comments

Comments
 (0)