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) {