Skip to content

Commit a2f01eb

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

File tree

4 files changed

+362
-0
lines changed

4 files changed

+362
-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: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
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+
}
76+
dev = zephyr_display_devices[id];
77+
}
78+
if (!device_is_ready(dev)) {
79+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Display is not ready"));
80+
}
81+
82+
zephyr_display_obj_t *self = mp_obj_malloc(zephyr_display_obj_t, type);
83+
self->dev = dev;
84+
return MP_OBJ_FROM_PTR(self);
85+
}
86+
87+
static mp_obj_t zephyr_display_write(size_t n_args, const mp_obj_t *args_in) {
88+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(args_in[0]);
89+
struct display_capabilities capabilities;
90+
int ret;
91+
size_t len;
92+
mp_buffer_info_t bufinfo;
93+
int x = 0;
94+
int y = 0;
95+
int sx;
96+
int sy;
97+
98+
mp_get_buffer_raise(args_in[1], &bufinfo, MP_BUFFER_READ);
99+
100+
display_get_capabilities(self->dev, &capabilities);
101+
sx = capabilities.x_resolution;
102+
103+
if (n_args > 2) {
104+
x = mp_obj_get_int(args_in[2]);
105+
y = mp_obj_get_int(args_in[3]);
106+
}
107+
108+
if (n_args > 4) {
109+
sx = mp_obj_get_int(args_in[4]);
110+
}
111+
sy = bufinfo.len / (sx * DISPLAY_BITS_PER_PIXEL(capabilities.current_pixel_format) / 8);
112+
113+
if (n_args > 5) {
114+
sy = mp_obj_get_int(args_in[5]);
115+
}
116+
117+
len = sx * sy * DISPLAY_BITS_PER_PIXEL(capabilities.current_pixel_format) / 8;
118+
119+
if (len > bufinfo.len) {
120+
mp_raise_ValueError(MP_ERROR_TEXT("Buffer is shorter than size requires"));
121+
}
122+
123+
const struct display_buffer_descriptor desc = {
124+
.buf_size = len,
125+
.width = sx,
126+
.height = sy,
127+
.pitch = sx,
128+
};
129+
130+
ret = display_write(self->dev, x, y, &desc, bufinfo.buf);
131+
if (ret < 0) {
132+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't write to display"));
133+
}
134+
return mp_obj_new_int(ret);
135+
}
136+
137+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(zephyr_display_write_obj, 2, 6, zephyr_display_write);
138+
139+
140+
static mp_obj_t zephyr_display_rgb(size_t n_args, const mp_obj_t *args_in) {
141+
(void)n_args;
142+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(args_in[0]);
143+
struct display_capabilities capabilities;
144+
145+
int r = mp_obj_get_int(args_in[1]);
146+
int g = mp_obj_get_int(args_in[2]);
147+
int b = mp_obj_get_int(args_in[3]);
148+
149+
display_get_capabilities(self->dev, &capabilities);
150+
151+
switch (capabilities.current_pixel_format) {
152+
case PIXEL_FORMAT_MONO10:
153+
if (r > 127 || g > 127 || b > 127) {
154+
return mp_obj_new_int(0x0);
155+
} else {
156+
return mp_obj_new_int(0xFF);
157+
}
158+
case PIXEL_FORMAT_MONO01:
159+
if (r > 127 || g > 127 || b > 127) {
160+
return mp_obj_new_int(0xFF);
161+
} else {
162+
return mp_obj_new_int(0x0);
163+
}
164+
case PIXEL_FORMAT_L_8:
165+
return mp_obj_new_int((r + g + b) / 3);
166+
case PIXEL_FORMAT_RGB_565:
167+
return mp_obj_new_int((r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6));
168+
default:
169+
break;
170+
}
171+
mp_raise_ValueError(MP_ERROR_TEXT("Not a framebuf pixel format"));
172+
}
173+
174+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(zephyr_display_rgb_obj, 4, 4, zephyr_display_rgb);
175+
176+
static mp_obj_t zephyr_display_framebuf_current_format(const mp_obj_t self_in) {
177+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
178+
struct display_capabilities capabilities;
179+
int format = -1;
180+
181+
display_get_capabilities(self->dev, &capabilities);
182+
183+
switch (capabilities.current_pixel_format) {
184+
case PIXEL_FORMAT_MONO10:
185+
case PIXEL_FORMAT_MONO01:
186+
if (capabilities.screen_info & SCREEN_INFO_MONO_VTILED
187+
&& capabilities.screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
188+
format = -1; // no VMSB
189+
} else if (capabilities.screen_info & SCREEN_INFO_MONO_VTILED) {
190+
format = 0; // FRAMEBUF_MVLSB
191+
} else if (capabilities.screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
192+
format = 4; // FRAMEBUF_MHMSB
193+
} else {
194+
format = 3; // FRAMEBUF_MHLSB
195+
}
196+
break;
197+
case PIXEL_FORMAT_L_8:
198+
format = 6; // FRAMEBUF_GS8
199+
break;
200+
case PIXEL_FORMAT_RGB_565:
201+
format = 1; // FRAMEBUF_RGB565
202+
break;
203+
default:
204+
format = -1;
205+
}
206+
if (format < 0) {
207+
mp_raise_ValueError(MP_ERROR_TEXT("Not a framebuf pixel format"));
208+
}
209+
return mp_obj_new_int(format);
210+
}
211+
212+
static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_framebuf_current_format_obj, zephyr_display_framebuf_current_format);
213+
214+
static mp_obj_t zephyr_display_formats(const mp_obj_t self_in) {
215+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
216+
struct display_capabilities capabilities;
217+
mp_obj_t tuple_buf[32];
218+
size_t tuple_cnt = 0;
219+
220+
display_get_capabilities(self->dev, &capabilities);
221+
222+
for (int i = 0; i < 32; i++) {
223+
if (capabilities.supported_pixel_formats & 1U << i) {
224+
tuple_buf[tuple_cnt] = mp_obj_new_int(1U << i);
225+
tuple_cnt++;
226+
}
227+
}
228+
229+
return mp_obj_new_tuple(tuple_cnt, tuple_buf);
230+
}
231+
232+
static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_formats_obj, zephyr_display_formats);
233+
234+
static mp_obj_t zephyr_display_current_format(size_t n_args, const mp_obj_t *args_in) {
235+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(args_in[0]);
236+
struct display_capabilities capabilities;
237+
int ret;
238+
239+
if (n_args > 1) {
240+
ret = display_set_pixel_format(self->dev, mp_obj_get_int(args_in[1]));
241+
if (ret < 0) {
242+
mp_raise_ValueError(MP_ERROR_TEXT("Invalid pixel format"));
243+
}
244+
}
245+
246+
display_get_capabilities(self->dev, &capabilities);
247+
248+
return mp_obj_new_int(capabilities.current_pixel_format);
249+
}
250+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(zephyr_display_current_format_obj, 1, 2, zephyr_display_current_format);
251+
252+
static mp_obj_t zephyr_display_blanking_on(const mp_obj_t self_in) {
253+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
254+
255+
if (display_blanking_on(self->dev) < 0) {
256+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't set blanking"));
257+
}
258+
259+
return mp_const_none;
260+
}
261+
262+
static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_blanking_on_obj, zephyr_display_blanking_on);
263+
264+
static mp_obj_t zephyr_display_blanking_off(const mp_obj_t self_in) {
265+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
266+
267+
if (display_blanking_off(self->dev) < 0) {
268+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't set blanking"));
269+
}
270+
271+
return mp_const_none;
272+
}
273+
274+
static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_blanking_off_obj, zephyr_display_blanking_off);
275+
276+
static mp_obj_t zephyr_display_set_brightness(const mp_obj_t self_in, mp_obj_t arg) {
277+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
278+
279+
if (display_set_brightness(self->dev, mp_obj_get_int(arg)) < 0) {
280+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't set brightness"));
281+
}
282+
283+
return mp_const_none;
284+
}
285+
286+
static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_set_brightness_obj, zephyr_display_set_brightness);
287+
288+
static mp_obj_t zephyr_display_set_contrast(const mp_obj_t self_in, mp_obj_t arg) {
289+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
290+
291+
if (display_set_contrast(self->dev, mp_obj_get_int(arg)) < 0) {
292+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't set contrast"));
293+
}
294+
295+
return mp_const_none;
296+
}
297+
298+
static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_set_contrast_obj, zephyr_display_set_contrast);
299+
300+
static mp_obj_t zephyr_display_set_orientation(const mp_obj_t self_in, mp_obj_t arg) {
301+
zephyr_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
302+
303+
if (mp_obj_get_int(arg) > DISPLAY_ORIENTATION_ROTATED_270 || mp_obj_get_int(arg) < 0) {
304+
mp_raise_ValueError(MP_ERROR_TEXT("Invalid orientation"));
305+
}
306+
307+
if (display_set_orientation(self->dev, mp_obj_get_int(arg)) < 0) {
308+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Couldn't set Orientation"));
309+
}
310+
311+
return mp_const_none;
312+
}
313+
314+
static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_set_orientation_obj, zephyr_display_set_orientation);
315+
316+
static const mp_rom_map_elem_t zephyr_display_locals_dict_table[] = {
317+
/* this class */
318+
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&zephyr_display_write_obj) },
319+
{ MP_ROM_QSTR(MP_QSTR_rgb), MP_ROM_PTR(&zephyr_display_rgb_obj) },
320+
{ MP_ROM_QSTR(MP_QSTR_framebuf_current_format), MP_ROM_PTR(&zephyr_display_framebuf_current_format_obj) },
321+
{ MP_ROM_QSTR(MP_QSTR_formats), MP_ROM_PTR(&zephyr_display_formats_obj) },
322+
{ MP_ROM_QSTR(MP_QSTR_current_format), MP_ROM_PTR(&zephyr_display_current_format_obj) },
323+
{ MP_ROM_QSTR(MP_QSTR_blanking_on), MP_ROM_PTR(&zephyr_display_blanking_on_obj) },
324+
{ MP_ROM_QSTR(MP_QSTR_blanking_off), MP_ROM_PTR(&zephyr_display_blanking_off_obj) },
325+
{ MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&zephyr_display_set_brightness_obj) },
326+
{ MP_ROM_QSTR(MP_QSTR_set_contrast), MP_ROM_PTR(&zephyr_display_set_contrast_obj) },
327+
{ MP_ROM_QSTR(MP_QSTR_set_orientation), MP_ROM_PTR(&zephyr_display_set_orientation_obj) },
328+
{ MP_ROM_QSTR(MP_QSTR_display_count), MP_ROM_INT(DT_ZEPHYR_DISPLAYS_COUNT) },
329+
{ MP_ROM_QSTR(MP_QSTR_ORIENTATION_NORMAL), MP_ROM_INT(DISPLAY_ORIENTATION_NORMAL) },
330+
{ MP_ROM_QSTR(MP_QSTR_ORIENTATION_90), MP_ROM_INT(DISPLAY_ORIENTATION_ROTATED_90) },
331+
{ MP_ROM_QSTR(MP_QSTR_ORIENTATION_180), MP_ROM_INT(DISPLAY_ORIENTATION_ROTATED_180) },
332+
{ MP_ROM_QSTR(MP_QSTR_ORIENTATION_270), MP_ROM_INT(DISPLAY_ORIENTATION_ROTATED_270) },
333+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_RGB_888), MP_ROM_INT(PIXEL_FORMAT_RGB_888) },
334+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_MONO01), MP_ROM_INT(PIXEL_FORMAT_MONO01) },
335+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_MONO10), MP_ROM_INT(PIXEL_FORMAT_MONO10) },
336+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_ARGB_8888), MP_ROM_INT(PIXEL_FORMAT_ARGB_8888) },
337+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_RGB_565), MP_ROM_INT(PIXEL_FORMAT_RGB_565) },
338+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_BGR_565), MP_ROM_INT(PIXEL_FORMAT_BGR_565) },
339+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_L_8), MP_ROM_INT(PIXEL_FORMAT_L_8) },
340+
#ifdef PIXEL_FORMAT_AL_88 /* will be in Zephyr 4.3 */
341+
{ MP_ROM_QSTR(MP_QSTR_FORMAT_AL_88), MP_ROM_INT(PIXEL_FORMAT_AL_88) },
342+
#endif
343+
};
344+
static MP_DEFINE_CONST_DICT(zephyr_display_locals_dict, zephyr_display_locals_dict_table);
345+
346+
MP_DEFINE_CONST_OBJ_TYPE(
347+
zephyr_display_type,
348+
MP_QSTR_Display,
349+
MP_TYPE_FLAG_NONE,
350+
make_new, zephyr_display_make_new,
351+
print, zephyr_display_print,
352+
locals_dict, &zephyr_display_locals_dict
353+
);
354+
#endif // defined(CONFIG_DISPLAY)

0 commit comments

Comments
 (0)