diff --git a/docs/environment.rst b/docs/environment.rst index 442c340f1a4d4..1720aae01f739 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -179,6 +179,27 @@ Example: Configure the display to 640x480 black and white (1 bit per pixel): `Adafruit Feather RP2350 `_ `Adafruit Metro RP2350 `_ +CIRCUITPY_DISPLAY_LIMIT +~~~~~~~~~~~~~~~~~~~~~~~ +Set the maximum supported number of displayio displays. This value overrides the +CIRCUITPY_DISPLAY_LIMIT compile-time flag defined in mpconfigport.h. +It specifies the maximum number of active display objects that can be supported simultaneously +on a CircuitPython board. + +By default, the value of CIRCUITPY_DISPLAY_LIMIT is set to 1 for most boards, meaning only +one display can be active at a time. Users can modify this value by adding a settings.toml entry +to support additional displays. + +The value of CIRCUITPY_DISPLAY_LIMIT should be set to a value that is supported by the +hardware and does not exceed the available memory and resources on the board. Setting the +value too high may result in unexpected behavior or crashes. + +Example: Set the maximum supported number of displayio displays to 2: + +.. code-block:: + + CIRCUITPY_DISPLAY_LIMIT=2 + CIRCUITPY_TERMINAL_SCALE ~~~~~~~~~~~~~~~~~~~~~~~~ Allows the entry of a display scaling factor used during the terminalio console construction. diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index 861ef37464633..4f5c12ab36968 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -13,6 +13,7 @@ CIRCUITPY_LTO = 1 CIRCUITPY_KEYPAD_DEMUX ?= 0 CIRCUITPY_LVFONTIO ?= 0 +CIRCUITPY_SET_DISPLAY_LIMIT ?= 0 ###################################################################### # Put samd21-only choices here. diff --git a/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk b/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk index 98d13f592ffcb..1c123c01fa869 100644 --- a/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk +++ b/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk @@ -24,3 +24,4 @@ CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_CODEOP = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_VECTORIO = 0 +CIRCUITPY_SET_DISPLAY_LIMIT = 0 diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 52520215b24d7..db87d014619ad 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -227,6 +227,9 @@ CFLAGS += -DCIRCUITPY_COUNTIO=$(CIRCUITPY_COUNTIO) CIRCUITPY_DISPLAYIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_DISPLAYIO=$(CIRCUITPY_DISPLAYIO) +CIRCUITPY_SET_DISPLAY_LIMIT ?= $(CIRCUITPY_DISPLAYIO) +CFLAGS += -DCIRCUITPY_SET_DISPLAY_LIMIT=$(CIRCUITPY_SET_DISPLAY_LIMIT) + CIRCUITPY_BUSDISPLAY ?= $(CIRCUITPY_DISPLAYIO) CFLAGS += -DCIRCUITPY_BUSDISPLAY=$(CIRCUITPY_BUSDISPLAY) diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 96735c8dbaa40..610a1bfb8381c 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -10,6 +10,10 @@ #include "mpconfigboard.h" #include "py/runtime.h" +#if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT +#include "shared-module/os/__init__.h" +#endif + #if CIRCUITPY_BUSIO #include "shared-bindings/busio/I2C.h" #include "shared-bindings/busio/SPI.h" @@ -172,12 +176,20 @@ mp_obj_t common_hal_board_create_uart(const mp_int_t instance) { #endif void reset_board_buses(void) { + #if (CIRCUITPY_BOARD_I2C && CIRCUITPY_I2CDISPLAYBUS) || (CIRCUITPY_BOARD_SPI && (CIRCUITPY_FOURWIRE || CIRCUITPY_SHARPDISPLAY || CIRCUITPY_AURORA_EPAPER)) + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + #define DYN_DISPLAY_BUSES(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? display_buses[indx] : display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) + #else + #define DYN_DISPLAY_BUSES(indx) (display_buses[indx]) + #endif + #endif + #if CIRCUITPY_BOARD_I2C for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_I2C; instance++) { bool display_using_i2c = false; #if CIRCUITPY_I2CDISPLAYBUS - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (display_buses[i].bus_base.type == &i2cdisplaybus_i2cdisplaybus_type && display_buses[i].i2cdisplay_bus.bus == &i2c_obj[instance]) { + for (uint8_t i = 0; i < max_allocated_display; i++) { + if (DYN_DISPLAY_BUSES(i).bus_base.type == &i2cdisplaybus_i2cdisplaybus_type && DYN_DISPLAY_BUSES(i).i2cdisplay_bus.bus == &i2c_obj[instance]) { display_using_i2c = true; break; } @@ -197,22 +209,22 @@ void reset_board_buses(void) { for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_SPI; instance++) { bool display_using_spi = false; #if CIRCUITPY_FOURWIRE || CIRCUITPY_SHARPDISPLAY || CIRCUITPY_AURORA_EPAPER - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t bus_type = display_buses[i].bus_base.type; + for (uint8_t i = 0; i < max_allocated_display; i++) { + mp_const_obj_t bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; #if CIRCUITPY_FOURWIRE - if (bus_type == &fourwire_fourwire_type && display_buses[i].fourwire_bus.bus == &spi_obj[instance]) { + if (bus_type == &fourwire_fourwire_type && DYN_DISPLAY_BUSES(i).fourwire_bus.bus == &spi_obj[instance]) { display_using_spi = true; break; } #endif #if CIRCUITPY_SHARPDISPLAY - if (bus_type == &sharpdisplay_framebuffer_type && display_buses[i].sharpdisplay.bus == &spi_obj[instance]) { + if (bus_type == &sharpdisplay_framebuffer_type && DYN_DISPLAY_BUSES(i).sharpdisplay.bus == &spi_obj[instance]) { display_using_spi = true; break; } #endif #if CIRCUITPY_AURORA_EPAPER - if (bus_type == &aurora_epaper_framebuffer_type && display_buses[i].aurora_epaper.bus == &spi_obj[instance]) { + if (bus_type == &aurora_epaper_framebuffer_type && DYN_DISPLAY_BUSES(i).aurora_epaper.bus == &spi_obj[instance]) { display_using_spi = true; break; } diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 4b467b75589d1..bf8979c6d8881 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -22,6 +22,11 @@ #include "py/mpconfig.h" +#if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT +#include "shared-module/os/__init__.h" +#include "py/misc.h" +#endif + #if CIRCUITPY_BUSDISPLAY #include "shared-bindings/busdisplay/BusDisplay.h" #endif @@ -44,11 +49,29 @@ #include "supervisor/spi_flash_api.h" #endif -// The default indicates no primary display -static int primary_display_number = -1; +// The default indicates the first display is the primary display +static int primary_display_number = 0; +mp_int_t max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; primary_display_bus_t display_buses[CIRCUITPY_DISPLAY_LIMIT]; primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; +#if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT +primary_display_bus_t *display_buses_dyn; +primary_display_t *displays_dyn; +#define DYN_DISPLAY_BUSES(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? display_buses[indx] : display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) +#define DYN_DISPLAY_BUSES_ADR(indx, membr) (indx < CIRCUITPY_DISPLAY_LIMIT ? &display_buses[indx].membr : &display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT].membr) +#define DYN_DISPLAY_BUSES_ADR0(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? &display_buses[indx] : &display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) +#define DYN_DISPLAYS(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? displays[indx] : displays_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) +#define DYN_DISPLAYS_ADR(indx, membr) (indx < CIRCUITPY_DISPLAY_LIMIT ? &displays[indx].membr : &displays_dyn[indx - CIRCUITPY_DISPLAY_LIMIT].membr) +#define DYN_DISPLAYS_ADR0(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? &displays[indx] : &displays_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) +#else +#define DYN_DISPLAY_BUSES(indx) (display_buses[indx]) +#define DYN_DISPLAY_BUSES_ADR(indx, membr) (&display_buses[indx].membr) +#define DYN_DISPLAY_BUSES_ADR0(indx) (&display_buses[indx]) +#define DYN_DISPLAYS(indx) (displays[indx]) +#define DYN_DISPLAYS_ADR(indx, membr) (&displays[indx].membr) +#define DYN_DISPLAYS_ADR0(indx) (&displays[indx]) +#endif displayio_buffer_transform_t null_transform = { .x = 0, @@ -65,9 +88,9 @@ displayio_buffer_transform_t null_transform = { #if CIRCUITPY_RGBMATRIX || CIRCUITPY_IS31FL3741 || CIRCUITPY_VIDEOCORE || CIRCUITPY_PICODVI static bool any_display_uses_this_framebuffer(mp_obj_base_t *obj) { - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (displays[i].display_base.type == &framebufferio_framebufferdisplay_type) { - framebufferio_framebufferdisplay_obj_t *display = &displays[i].framebuffer_display; + for (uint8_t i = 0; i < max_allocated_display; i++) { + if (DYN_DISPLAYS(i).display_base.type == &framebufferio_framebufferdisplay_type) { + framebufferio_framebufferdisplay_obj_t *display = DYN_DISPLAYS_ADR(i, framebuffer_display); if (display->framebuffer == obj) { return true; } @@ -77,7 +100,6 @@ static bool any_display_uses_this_framebuffer(mp_obj_base_t *obj) { } #endif - void displayio_background(void) { if (mp_hal_is_interrupted()) { return; @@ -87,8 +109,8 @@ void displayio_background(void) { return; } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t display_type = displays[i].display_base.type; + for (uint8_t i = 0; i < max_allocated_display; i++) { + mp_const_obj_t display_type = DYN_DISPLAYS(i).display_base.type; if (display_type == NULL || display_type == &mp_type_NoneType) { // Skip null display. continue; @@ -96,15 +118,15 @@ void displayio_background(void) { if (false) { #if CIRCUITPY_BUSDISPLAY } else if (display_type == &busdisplay_busdisplay_type) { - busdisplay_busdisplay_background(&displays[i].display); + busdisplay_busdisplay_background(DYN_DISPLAYS_ADR(i, display)); #endif #if CIRCUITPY_FRAMEBUFFERIO } else if (display_type == &framebufferio_framebufferdisplay_type) { - framebufferio_framebufferdisplay_background(&displays[i].framebuffer_display); + framebufferio_framebufferdisplay_background(DYN_DISPLAYS_ADR(i, framebuffer_display)); #endif #if CIRCUITPY_EPAPERDISPLAY } else if (display_type == &epaperdisplay_epaperdisplay_type) { - epaperdisplay_epaperdisplay_background(&displays[i].epaper_display); + epaperdisplay_epaperdisplay_background(DYN_DISPLAYS_ADR(i, epaper_display)); #endif } } @@ -117,82 +139,131 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { if (!keep_primary) { primary_display_number = -1; } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { + for (uint8_t i = (keep_primary ? CIRCUITPY_DISPLAY_LIMIT: 0); i < max_allocated_display; i++) { if (i == primary_display_number) { continue; } - mp_const_obj_t display_type = displays[i].display_base.type; + mp_const_obj_t display_type = DYN_DISPLAYS(i).display_base.type; if (display_type == NULL || display_type == &mp_type_NoneType) { continue; #if CIRCUITPY_BUSDISPLAY } else if (display_type == &busdisplay_busdisplay_type) { - release_busdisplay(&displays[i].display); + release_busdisplay(DYN_DISPLAYS_ADR(i, display)); #endif #if CIRCUITPY_EPAPERDISPLAY } else if (display_type == &epaperdisplay_epaperdisplay_type) { - release_epaperdisplay(&displays[i].epaper_display); + release_epaperdisplay(DYN_DISPLAYS_ADR(i, epaper_display)); #endif #if CIRCUITPY_FRAMEBUFFERIO } else if (display_type == &framebufferio_framebufferdisplay_type) { - release_framebufferdisplay(&displays[i].framebuffer_display); + release_framebufferdisplay(DYN_DISPLAYS_ADR(i, framebuffer_display)); #endif } + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + displays[i].display_base.type = &mp_type_NoneType; + } else { + displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].display_base.type = &mp_type_NoneType; + } + #else displays[i].display_base.type = &mp_type_NoneType; + #endif } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t bus_type = display_buses[i].bus_base.type; + for (uint8_t i = (keep_primary ? CIRCUITPY_DISPLAY_LIMIT: 0); i < max_allocated_display; i++) { + if (i == primary_display_number) { + continue; + } + mp_const_obj_t bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; if (bus_type == NULL || bus_type == &mp_type_NoneType) { continue; #if CIRCUITPY_FOURWIRE } else if (bus_type == &fourwire_fourwire_type) { - common_hal_fourwire_fourwire_deinit(&display_buses[i].fourwire_bus); + common_hal_fourwire_fourwire_deinit(DYN_DISPLAY_BUSES_ADR(i, fourwire_bus)); #endif #if CIRCUITPY_I2CDISPLAYBUS } else if (bus_type == &i2cdisplaybus_i2cdisplaybus_type) { - common_hal_i2cdisplaybus_i2cdisplaybus_deinit(&display_buses[i].i2cdisplay_bus); + common_hal_i2cdisplaybus_i2cdisplaybus_deinit(DYN_DISPLAY_BUSES_ADR(i, i2cdisplay_bus)); #endif #if CIRCUITPY_DOTCLOCKFRAMEBUFFER } else if (bus_type == &dotclockframebuffer_framebuffer_type) { - common_hal_dotclockframebuffer_framebuffer_deinit(&display_buses[i].dotclock); + common_hal_dotclockframebuffer_framebuffer_deinit(DYN_DISPLAY_BUSES_ADR(i, dotclock)); #endif #if CIRCUITPY_PARALLELDISPLAYBUS } else if (bus_type == ¶lleldisplaybus_parallelbus_type) { - common_hal_paralleldisplaybus_parallelbus_deinit(&display_buses[i].parallel_bus); + common_hal_paralleldisplaybus_parallelbus_deinit(DYN_DISPLAY_BUSES_ADR(i, parallel_bus)); #endif #if CIRCUITPY_RGBMATRIX } else if (bus_type == &rgbmatrix_RGBMatrix_type) { - common_hal_rgbmatrix_rgbmatrix_deinit(&display_buses[i].rgbmatrix); + common_hal_rgbmatrix_rgbmatrix_deinit(DYN_DISPLAY_BUSES_ADR(i, rgbmatrix)); #endif #if CIRCUITPY_IS31FL3741 } else if (bus_type == &is31fl3741_framebuffer_type) { - common_hal_is31fl3741_framebuffer_deinit(&display_buses[i].is31fl3741); + common_hal_is31fl3741_framebuffer_deinit(DYN_DISPLAY_BUSES_ADR(i, is31fl3741)); #endif #if CIRCUITPY_SHARPDISPLAY } else if (bus_type == &sharpdisplay_framebuffer_type) { - common_hal_sharpdisplay_framebuffer_deinit(&display_buses[i].sharpdisplay); + common_hal_sharpdisplay_framebuffer_deinit(DYN_DISPLAY_BUSES_ADR(i, sharpdisplay)); #endif #if CIRCUITPY_VIDEOCORE } else if (bus_type == &videocore_framebuffer_type) { - common_hal_videocore_framebuffer_deinit(&display_buses[i].videocore); + common_hal_videocore_framebuffer_deinit(DYN_DISPLAY_BUSES_ADR(i, videocore)); #endif #if CIRCUITPY_PICODVI } else if (bus_type == &picodvi_framebuffer_type) { - common_hal_picodvi_framebuffer_deinit(&display_buses[i].picodvi); + common_hal_picodvi_framebuffer_deinit(DYN_DISPLAY_BUSES_ADR(i, picodvi)); #endif } + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + display_buses[i].bus_base.type = &mp_type_NoneType; + } else { + display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT].bus_base.type = &mp_type_NoneType; + } + #else display_buses[i].bus_base.type = &mp_type_NoneType; + #endif + } + if (!keep_primary) { + supervisor_stop_terminal(); } - - supervisor_stop_terminal(); } void common_hal_displayio_release_displays(void) { common_hal_displayio_release_displays_impl(false); } +void malloc_display_memory(void) { + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; + (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_LIMIT", &max_num_displays); + if (max_num_displays > CIRCUITPY_DISPLAY_LIMIT) { + display_buses_dyn = m_new0(primary_display_bus_t, (max_num_displays - CIRCUITPY_DISPLAY_LIMIT)); + displays_dyn = m_new0(primary_display_t, (max_num_displays - CIRCUITPY_DISPLAY_LIMIT)); + + for (uint8_t i = CIRCUITPY_DISPLAY_LIMIT; i < max_num_displays; i++) { + display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT].bus_base.type = &mp_type_NoneType; + displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].display_base.type = &mp_type_NoneType; + } + max_allocated_display = max_num_displays; + } + #endif +} + void reset_displays(void) { // In CircuitPython 10, release secondary displays before doing anything else: - // common_hal_displayio_release_displays_impl(true); + common_hal_displayio_release_displays_impl(true); + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + // Does this need to be done or does port_free effectively do this? + if (max_allocated_display > CIRCUITPY_DISPLAY_LIMIT) { + // Free the dynamically allocated display buses and displays. + m_del(primary_display_bus_t, display_buses_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); + m_del(primary_display_t, displays_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); + display_buses_dyn = NULL; + displays_dyn = NULL; + // Set dynamically allocated displays to 0 (CIRCUITPY_DISPLAY_LIMIT) + max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; + } + #endif // The SPI buses used by FourWires may be allocated on the heap so we need to move them inline. for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { @@ -358,35 +429,35 @@ void reset_displays(void) { } void displayio_gc_collect(void) { - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t display_bus_type = display_buses[i].bus_base.type; + for (uint8_t i = 0; i < max_allocated_display; i++) { + mp_const_obj_t display_bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { continue; } #if CIRCUITPY_RGBMATRIX if (display_bus_type == &rgbmatrix_RGBMatrix_type) { - rgbmatrix_rgbmatrix_collect_ptrs(&display_buses[i].rgbmatrix); + rgbmatrix_rgbmatrix_collect_ptrs(DYN_DISPLAY_BUSES_ADR(i, rgbmatrix)); } #endif #if CIRCUITPY_IS31FL3741 if (display_bus_type == &is31fl3741_framebuffer_type) { - is31fl3741_framebuffer_collect_ptrs(&display_buses[i].is31fl3741); + is31fl3741_framebuffer_collect_ptrs(DYN_DISPLAY_BUSES_ADR(i, is31fl3741)); } #endif #if CIRCUITPY_SHARPDISPLAY if (display_bus_type == &sharpdisplay_framebuffer_type) { - common_hal_sharpdisplay_framebuffer_collect_ptrs(&display_buses[i].sharpdisplay); + common_hal_sharpdisplay_framebuffer_collect_ptrs(DYN_DISPLAY_BUSES_ADR(i, sharpdisplay)); } #endif #if CIRCUITPY_AURORA_EPAPER if (display_bus_type == &aurora_framebuffer_type) { - common_hal_aurora_epaper_framebuffer_collect_ptrs(&display_buses[i].aurora_epaper); + common_hal_aurora_epaper_framebuffer_collect_ptrs(DYN_DISPLAY_BUSES_ADR(i, aurora_epaper)); } #endif } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t display_type = displays[i].display_base.type; + for (uint8_t i = 0; i < max_allocated_display; i++) { + mp_const_obj_t display_type = DYN_DISPLAYS(i).display_base.type; if (display_type == NULL || display_type == &mp_type_NoneType) { continue; @@ -394,15 +465,15 @@ void displayio_gc_collect(void) { // but this is more precise, and is the only field that needs marking. #if CIRCUITPY_BUSDISPLAY } else if (display_type == &busdisplay_busdisplay_type) { - busdisplay_busdisplay_collect_ptrs(&displays[i].display); + busdisplay_busdisplay_collect_ptrs(DYN_DISPLAYS_ADR(i, display)); #endif #if CIRCUITPY_FRAMEBUFFERIO } else if (display_type == &framebufferio_framebufferdisplay_type) { - framebufferio_framebufferdisplay_collect_ptrs(&displays[i].framebuffer_display); + framebufferio_framebufferdisplay_collect_ptrs(DYN_DISPLAYS_ADR(i, framebuffer_display)); #endif #if CIRCUITPY_EPAPERDISPLAY } else if (display_type == &epaperdisplay_epaperdisplay_type) { - epaperdisplay_epaperdisplay_collect_ptrs(&displays[i].epaper_display); + epaperdisplay_epaperdisplay_collect_ptrs(DYN_DISPLAYS_ADR(i, epaper_display)); #endif } } @@ -413,15 +484,35 @@ static bool is_display_active(mp_obj_base_t *display_maybe) { } primary_display_t *allocate_display(void) { - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (!is_display_active(&displays[i].display_base)) { + for (uint8_t i = 0; i < max_allocated_display; i++) { + if (!is_display_active(DYN_DISPLAYS_ADR(i, display_base))) { // Clear this memory so it is in a known state before init. - memset(&displays[i], 0, sizeof(displays[i])); + memset(DYN_DISPLAYS_ADR0(i), 0, sizeof(displays[0])); // Default to None so that it works as board.DISPLAY. + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + displays[i].display_base.type = &mp_type_NoneType; + } else { + displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].display_base.type = &mp_type_NoneType; + } + #else displays[i].display_base.type = &mp_type_NoneType; - return &displays[i]; + #endif + if (i == 0) { + // The first display is the primary display. + primary_display_number = 0; + } + return DYN_DISPLAYS_ADR0(i); } } + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + // malloc_display_memory allocates both displays and buses + if (max_allocated_display == CIRCUITPY_DISPLAY_LIMIT) { + // See if we can allocate more displays and buses + malloc_display_memory(); + return &displays_dyn[0]; + } + #endif return NULL; } @@ -434,15 +525,31 @@ primary_display_t *allocate_display_or_raise(void) { } primary_display_bus_t *allocate_display_bus(void) { - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t display_bus_type = display_buses[i].bus_base.type; + for (uint8_t i = 0; i < max_allocated_display; i++) { + mp_const_obj_t display_bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { // Clear this memory so it is in a known state before init. - memset(&display_buses[i], 0, sizeof(display_buses[i])); + memset(DYN_DISPLAY_BUSES_ADR0(i), 0, sizeof(display_buses[0])); + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + display_buses[i].bus_base.type = &mp_type_NoneType; + } else { + display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT].bus_base.type = &mp_type_NoneType; + } + #else display_buses[i].bus_base.type = &mp_type_NoneType; - return &display_buses[i]; + #endif + return DYN_DISPLAY_BUSES_ADR0(i); } } + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + // malloc_display_memory allocates both displays and buses + if (max_allocated_display == CIRCUITPY_DISPLAY_LIMIT) { + // See if e can allocate more displays and buses + malloc_display_memory(); + return &display_buses_dyn[0]; + } + #endif return NULL; } @@ -455,10 +562,10 @@ primary_display_bus_t *allocate_display_bus_or_raise(void) { } mp_obj_t common_hal_displayio_get_primary_display(void) { - if (primary_display_number == -1 || primary_display_number >= CIRCUITPY_DISPLAY_LIMIT) { + if (primary_display_number == -1 || primary_display_number >= max_allocated_display) { return mp_const_none; } - mp_obj_base_t *primary_display = &displays[primary_display_number].display_base; + mp_obj_base_t *primary_display = DYN_DISPLAYS_ADR(primary_display_number, display_base); if (is_display_active(primary_display)) { return MP_OBJ_FROM_PTR(primary_display); } @@ -470,8 +577,8 @@ void common_hal_displayio_set_primary_display(mp_obj_t new_primary_display) { primary_display_number = -1; return; } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_obj_t display = MP_OBJ_FROM_PTR(&displays[i]); + for (uint8_t i = 0; i < max_allocated_display; i++) { + mp_obj_t display = MP_OBJ_FROM_PTR(DYN_DISPLAYS_ADR0(i)); if (new_primary_display == display && is_display_active(display)) { primary_display_number = i; return; @@ -485,8 +592,8 @@ void common_hal_displayio_auto_primary_display(void) { if (primary_display_number != -1) { return; } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (is_display_active(&displays[i].display_base)) { + for (uint8_t i = 0; i < max_allocated_display; i++) { + if (is_display_active(DYN_DISPLAYS_ADR(i, display_base))) { primary_display_number = i; return; } diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index 29b8c64c97240..4d774c3c927d8 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -101,11 +101,17 @@ typedef struct { extern primary_display_bus_t display_buses[CIRCUITPY_DISPLAY_LIMIT]; extern primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; +extern mp_int_t max_allocated_display; +#if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT +extern primary_display_bus_t *display_buses_dyn; +extern primary_display_t *displays_dyn; +#endif extern displayio_group_t circuitpython_splash; void displayio_background(void); void reset_displays(void); +void malloc_display_memory(void); void displayio_gc_collect(void); primary_display_t *allocate_display(void); diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index 14fbc3341b5ff..570a6ec657ae3 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -20,6 +20,10 @@ #include "supervisor/usb.h" #endif +#if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT +#include "shared-module/os/__init__.h" +#endif + #include #include @@ -501,13 +505,24 @@ void epaperdisplay_epaperdisplay_collect_ptrs(epaperdisplay_epaperdisplay_obj_t } size_t maybe_refresh_epaperdisplay(void) { - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (displays[i].epaper_display.base.type != &epaperdisplay_epaperdisplay_type || - displays[i].epaper_display.core.current_group != &circuitpython_splash) { + for (uint8_t i = 0; i < max_allocated_display; i++) { + epaperdisplay_epaperdisplay_obj_t *display; + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + display = &displays[i].epaper_display; + } else { + display = &displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].epaper_display; + } + #else + display = &displays[i].epaper_display; + #endif + + if (display->base.type != &epaperdisplay_epaperdisplay_type || + display->core.current_group != &circuitpython_splash) { // Skip regular displays and those not showing the splash. continue; } - epaperdisplay_epaperdisplay_obj_t *display = &displays[i].epaper_display; + size_t time_to_refresh = common_hal_epaperdisplay_epaperdisplay_get_time_to_refresh(display); if (time_to_refresh > 0) { return time_to_refresh; diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index eea11bd5fb4af..87b73cb8992b6 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -1554,24 +1554,32 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request) } static bool supervisor_filesystem_access_could_block(void) { + bool could_block = false; #if CIRCUITPY_FOURWIRE mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); if (!vfs->next) { // Assume that the CIRCUITPY root is not sharing a SPI bus with the display SPI bus return false; } - // Check display 0 to see if it's on a fourwire (SPI) bus. If it is, blocking is possible - // in theory other displays could block but also in reality there's generally 0 or 1 displays - for (size_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (display_buses[i].bus_base.type != &fourwire_fourwire_type) { + + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + #define DYN_DISPLAY_BUSES(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? display_buses[indx] : display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) + #define DYN_DISPLAY_BUSES_ADR(indx, membr) (indx < CIRCUITPY_DISPLAY_LIMIT ? &display_buses[indx].membr : &display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT].membr) + #else + #define DYN_DISPLAY_BUSES(indx) (display_buses[indx]) + #define DYN_DISPLAY_BUSES_ADR(indx, membr) (&display_buses[indx].membr) + #endif + // Check displays to see if it's on a fourwire (SPI) bus. If it is, blocking is possible + for (mp_int_t i = 0; i < max_allocated_display; i++) { + if (DYN_DISPLAY_BUSES(i).bus_base.type != &fourwire_fourwire_type) { continue; } - if (!common_hal_fourwire_fourwire_bus_free(MP_OBJ_FROM_PTR(&display_buses[i].bus_base))) { - return true; + if (!common_hal_fourwire_fourwire_bus_free(MP_OBJ_FROM_PTR(DYN_DISPLAY_BUSES_ADR(i, bus_base)))) { + could_block = true; } } #endif - return false; + return could_block; } void supervisor_web_workflow_background(void *data) {