diff --git a/docs/environment.rst b/docs/environment.rst
index 7ee914e0a16ec..461fd9273acd8 100644
--- a/docs/environment.rst
+++ b/docs/environment.rst
@@ -101,15 +101,14 @@ Wi-Fi SSID to auto-connect to even if user code is not running.
Additional board specific keys
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-`MaTouch ESP32-S3 Parallel TFT with Touch 7“ `_
-
-CIRCUITPY_DISPLAY_WIDTH
-~~~~~~~~~~~~~~~~~~~~~~~
+CIRCUITPY_DISPLAY_WIDTH (Sunton, MaTouch)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Selects the correct screen resolution (1024x600 or 800x640) for the particular board variant.
If the CIRCUITPY_DISPLAY_WIDTH parameter is set to a value of 1024 the display is initialized
during power up at 1024x600 otherwise the display will be initialized at a resolution
of 800x480.
+`MaTouch ESP32-S3 Parallel TFT with Touch 7“ `_
`Sunton ESP32-2432S028 `_
`Sunton ESP32-2432S024C `_
@@ -122,6 +121,8 @@ a rotation of 0. Attempting to initialize the screen with a rotation other than
90, 180 or 270 is not supported and will result in an unexpected screen rotation.
`Sunton ESP32-8048S050 `_
+`Adafruit Feather RP2350 `_
+`Adafruit Metro RP2350 `_
CIRCUITPY_DISPLAY_FREQUENCY
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -130,3 +131,46 @@ If a valid frequency is not defined the board will initialize the framebuffer wi
frequency of 12500000hz (12.5Mhz). The value should be entered as an integer in hertz
i.e. CIRCUITPY_DISPLAY_FREQUENCY=16000000 will override the default value with a 16Mhz
display frequency.
+
+`Sunton ESP32-8048S050 `_
+
+
+CIRCUITPY_PICODVI_ENABLE
+~~~~~~~~~~~~~~~~~~~~~~~~
+Whether to configure the display at board initialization time, one of the following:
+
+.. code-block::
+
+ CIRCUITPY_PICODVI_ENABLE="detect" # when EDID EEPROM is detected (default)
+ CIRCUITPY_PICODVI_ENABLE="always"
+ CIRCUITPY_PICODVI_ENABLE="never"
+
+A display configured in this manner is available at ``supervisor.runtime.display``
+until it is released by ``displayio.release_displays()``. It does not appear at
+``board.DISPLAY``.
+
+`Adafruit Feather RP2350 `_
+`Adafruit Metro RP2350 `_
+
+CIRCUITPY_DISPLAY_WIDTH, CIRCUITPY_DISPLAY_HEIGHT, and CIRCUITPY_DISPLAY_COLOR_DEPTH (RP2350 boards with DVI or HSTX connector)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Selects the desired resolution and color depth.
+
+Supported resolutions are:
+ * 640x480 with color depth 1, 2, 4 or 8 bits per pixel
+ * 320x240 with color depth 8 or 16 bits per pixel
+
+The default value, if unspecified, is 320x240 with 16 bits per pixel.
+
+If height is unspecified, it is set from the width. For example, a width of 640
+implies a height of 480.
+
+Example: Configure the display to 640x480 black and white (1 bit per pixel):
+
+.. code-block::
+
+ CIRCUITPY_DISPLAY_WIDTH=640
+ CIRCUITPY_DISPLAY_COLOR_DEPTH=1
+
+`Adafruit Feather RP2350 `_
+`Adafruit Metro RP2350 `_
diff --git a/main.c b/main.c
index 7ac4194a75f04..763a3c1e0c197 100644
--- a/main.c
+++ b/main.c
@@ -772,6 +772,9 @@ static bool __attribute__((noinline)) run_code_py(safe_mode_t safe_mode, bool *s
#if CIRCUITPY_ALARM
if (fake_sleeping) {
board_init();
+ #if CIRCUITPY_DISPLAYIO
+ common_hal_displayio_auto_primary_display();
+ #endif
// Pretend that the next run is the first run, as if we were reset.
*simulate_reset = true;
}
@@ -1053,6 +1056,10 @@ int __attribute__((used)) main(void) {
// displays init after filesystem, since they could share the flash SPI
board_init();
+ #if CIRCUITPY_DISPLAYIO
+ common_hal_displayio_auto_primary_display();
+ #endif
+
mp_hal_stdout_tx_str(line_clear);
// This is first time we are running CircuitPython after a reset or power-up.
diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile
index cf40af7baba64..9e36a93490d3a 100644
--- a/ports/raspberrypi/Makefile
+++ b/ports/raspberrypi/Makefile
@@ -556,6 +556,7 @@ ifeq ($(CIRCUITPY_PICODVI),1)
SRC_C += \
bindings/picodvi/__init__.c \
bindings/picodvi/Framebuffer.c \
+ common-hal/picodvi/__init__.c \
common-hal/picodvi/Framebuffer_$(CHIP_VARIANT).c \
ifeq ($(CHIP_VARIANT),RP2040)
diff --git a/ports/raspberrypi/bindings/picodvi/Framebuffer.h b/ports/raspberrypi/bindings/picodvi/Framebuffer.h
index 7cb335e91e767..280c3c9b8bf92 100644
--- a/ports/raspberrypi/bindings/picodvi/Framebuffer.h
+++ b/ports/raspberrypi/bindings/picodvi/Framebuffer.h
@@ -12,6 +12,9 @@
extern const mp_obj_type_t picodvi_framebuffer_type;
+bool common_hal_picodvi_framebuffer_preflight(
+ mp_uint_t width, mp_uint_t height,
+ mp_uint_t color_depth);
void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
mp_uint_t width, mp_uint_t height,
const mcu_pin_obj_t *clk_dp, const mcu_pin_obj_t *clk_dn,
diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2350/board.c b/ports/raspberrypi/boards/adafruit_feather_rp2350/board.c
index e6a868ab21226..fddd2572c1fcd 100644
--- a/ports/raspberrypi/boards/adafruit_feather_rp2350/board.c
+++ b/ports/raspberrypi/boards/adafruit_feather_rp2350/board.c
@@ -4,6 +4,13 @@
//
// SPDX-License-Identifier: MIT
+#include "py/obj.h"
#include "supervisor/board.h"
+#include "common-hal/picodvi/__init__.h"
+
// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.
+
+void board_init(void) {
+ picodvi_autoconstruct();
+}
diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2350/mpconfigboard.h b/ports/raspberrypi/boards/adafruit_feather_rp2350/mpconfigboard.h
index 1e238789a3dde..d0c114a97e4ef 100644
--- a/ports/raspberrypi/boards/adafruit_feather_rp2350/mpconfigboard.h
+++ b/ports/raspberrypi/boards/adafruit_feather_rp2350/mpconfigboard.h
@@ -20,3 +20,12 @@
#define DEFAULT_UART_BUS_TX (&pin_GPIO0)
#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO8)
+
+#define DEFAULT_DVI_BUS_CLK_DN (&pin_GPIO15)
+#define DEFAULT_DVI_BUS_CLK_DP (&pin_GPIO14)
+#define DEFAULT_DVI_BUS_RED_DN (&pin_GPIO19)
+#define DEFAULT_DVI_BUS_RED_DP (&pin_GPIO18)
+#define DEFAULT_DVI_BUS_GREEN_DN (&pin_GPIO17)
+#define DEFAULT_DVI_BUS_GREEN_DP (&pin_GPIO16)
+#define DEFAULT_DVI_BUS_BLUE_DN (&pin_GPIO13)
+#define DEFAULT_DVI_BUS_BLUE_DP (&pin_GPIO12)
diff --git a/ports/raspberrypi/boards/adafruit_metro_rp2350/board.c b/ports/raspberrypi/boards/adafruit_metro_rp2350/board.c
index 111fc5175139c..f85d1076c0b4f 100644
--- a/ports/raspberrypi/boards/adafruit_metro_rp2350/board.c
+++ b/ports/raspberrypi/boards/adafruit_metro_rp2350/board.c
@@ -9,6 +9,8 @@
#include "shared-bindings/usb_host/Port.h"
#include "supervisor/board.h"
+#include "common-hal/picodvi/__init__.h"
+
// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.
@@ -29,8 +31,9 @@ bool board_reset_pin_number(uint8_t pin_number) {
}
#endif
-#if defined(DEFAULT_USB_HOST_DATA_PLUS)
void board_init(void) {
+ #if defined(DEFAULT_USB_HOST_DATA_PLUS)
common_hal_usb_host_port_construct(DEFAULT_USB_HOST_DATA_PLUS, DEFAULT_USB_HOST_DATA_MINUS);
+ #endif
+ picodvi_autoconstruct();
}
-#endif
diff --git a/ports/raspberrypi/boards/adafruit_metro_rp2350/mpconfigboard.h b/ports/raspberrypi/boards/adafruit_metro_rp2350/mpconfigboard.h
index c8e73600d07df..694a07027354d 100644
--- a/ports/raspberrypi/boards/adafruit_metro_rp2350/mpconfigboard.h
+++ b/ports/raspberrypi/boards/adafruit_metro_rp2350/mpconfigboard.h
@@ -26,3 +26,12 @@
#define DEFAULT_USB_HOST_DATA_MINUS (&pin_GPIO33)
#define DEFAULT_USB_HOST_5V_POWER (&pin_GPIO29)
#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO47)
+
+#define DEFAULT_DVI_BUS_CLK_DN (&pin_GPIO15)
+#define DEFAULT_DVI_BUS_CLK_DP (&pin_GPIO14)
+#define DEFAULT_DVI_BUS_RED_DN (&pin_GPIO19)
+#define DEFAULT_DVI_BUS_RED_DP (&pin_GPIO18)
+#define DEFAULT_DVI_BUS_GREEN_DN (&pin_GPIO17)
+#define DEFAULT_DVI_BUS_GREEN_DP (&pin_GPIO16)
+#define DEFAULT_DVI_BUS_BLUE_DN (&pin_GPIO13)
+#define DEFAULT_DVI_BUS_BLUE_DP (&pin_GPIO12)
diff --git a/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c b/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c
index c3d2b7bea2f17..d5845b1182202 100644
--- a/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c
+++ b/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c
@@ -129,6 +129,20 @@ static void __not_in_flash_func(dma_irq_handler)(void) {
ch->al3_read_addr_trig = (uintptr_t)active_picodvi->dma_commands;
}
+bool common_hal_picodvi_framebuffer_preflight(
+ mp_uint_t width, mp_uint_t height,
+ mp_uint_t color_depth) {
+
+ // for each supported resolution, check the color depth is supported
+ if (width == 640 && height == 640) {
+ return color_depth == 1 || color_depth == 2 || color_depth == 4 || color_depth == 8;
+ }
+ if (width == 320 && height == 240) {
+ return color_depth == 8 || color_depth == 16;
+ }
+ return false;
+}
+
void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
mp_uint_t width, mp_uint_t height,
const mcu_pin_obj_t *clk_dp, const mcu_pin_obj_t *clk_dn,
@@ -140,7 +154,7 @@ void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("%q in use"), MP_QSTR_picodvi);
}
- if (!(width == 640 && height == 480) && !(width == 320 && height == 240 && (color_depth == 16 || color_depth == 8))) {
+ if (!common_hal_picodvi_framebuffer_preflight(width, height, color_depth)) {
mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q and %q"), MP_QSTR_width, MP_QSTR_height);
}
diff --git a/ports/raspberrypi/common-hal/picodvi/__init__.c b/ports/raspberrypi/common-hal/picodvi/__init__.c
new file mode 100644
index 0000000000000..bf448dd16688d
--- /dev/null
+++ b/ports/raspberrypi/common-hal/picodvi/__init__.c
@@ -0,0 +1,119 @@
+// This file is part of the CircuitPython project: https://circuitpython.org
+//
+// SPDX-FileCopyrightText: Copyright (c) 2025 Jeff Epler for Adafruit Industries
+//
+// SPDX-License-Identifier: MIT
+
+#include "common-hal/picodvi/__init__.h"
+#include "common-hal/picodvi/Framebuffer.h"
+#include "bindings/picodvi/Framebuffer.h"
+#include "shared-bindings/busio/I2C.h"
+#include "shared-bindings/board/__init__.h"
+#include "shared-module/displayio/__init__.h"
+#include "shared-module/os/__init__.h"
+#include "supervisor/shared/safe_mode.h"
+#include "py/gc.h"
+#include "py/runtime.h"
+#include "supervisor/port_heap.h"
+
+#if defined(DEFAULT_DVI_BUS_CLK_DP)
+static bool picodvi_autoconstruct_enabled(void) {
+ char buf[sizeof("detect")];
+ buf[0] = 0;
+
+ // (any failure leaves the content of buf untouched: an empty nul-terminated string
+ (void)common_hal_os_getenv_str("CIRCUITPY_PICODVI_ENABLE", buf, sizeof(buf));
+
+ if (!strcasecmp(buf, "never")) {
+ return false;
+ }
+ if (!strcasecmp(buf, "always")) {
+ return true;
+ }
+
+ // It's "detect" or else an invalid value which is treated the same as "detect".
+
+ // check if address 0x50 is live on the I2C bus
+ busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0);
+ if (!i2c) {
+ return false;
+ }
+ if (!common_hal_busio_i2c_try_lock(i2c)) {
+ return false;
+ }
+ bool probed = common_hal_busio_i2c_probe(i2c, 0x50);
+ common_hal_busio_i2c_unlock(i2c);
+ return probed;
+}
+
+// For picodvi_autoconstruct to work, the 8 DVI/HSTX pin names must be defined, AND
+// i2c bus 0 must also be connected to DVI with on-board pull ups
+void picodvi_autoconstruct(void) {
+ if (get_safe_mode() != SAFE_MODE_NONE) {
+ return;
+ }
+
+ if (!picodvi_autoconstruct_enabled()) {
+ return;
+ }
+
+ mp_int_t width = 320;
+ mp_int_t height = 0;
+ mp_int_t color_depth = 16;
+ mp_int_t rotation = 0;
+
+ (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_WIDTH", &width);
+ (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_HEIGHT", &height);
+ (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_COLOR_DEPTH", &color_depth);
+ (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_ROTATION", &rotation);
+
+ if (height == 0) {
+ switch (width) {
+ case 640:
+ height = 480;
+ break;
+ case 320:
+ height = 240;
+ break;
+ }
+ }
+
+ if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) {
+ // invalid rotation
+ rotation = 0;
+ }
+
+ if (!common_hal_picodvi_framebuffer_preflight(width, height, color_depth)) {
+ // invalid configuration, set back to default
+ width = 320;
+ height = 240;
+ color_depth = 16;
+ }
+
+ // construct framebuffer and display
+ picodvi_framebuffer_obj_t *fb = &allocate_display_bus_or_raise()->picodvi;
+ fb->base.type = &picodvi_framebuffer_type;
+ common_hal_picodvi_framebuffer_construct(fb,
+ width, height,
+ DEFAULT_DVI_BUS_CLK_DP,
+ DEFAULT_DVI_BUS_CLK_DN,
+ DEFAULT_DVI_BUS_RED_DP,
+ DEFAULT_DVI_BUS_RED_DN,
+ DEFAULT_DVI_BUS_GREEN_DP,
+ DEFAULT_DVI_BUS_GREEN_DN,
+ DEFAULT_DVI_BUS_BLUE_DP,
+ DEFAULT_DVI_BUS_BLUE_DN,
+ color_depth);
+
+ framebufferio_framebufferdisplay_obj_t *display = &allocate_display()->framebuffer_display;
+ display->base.type = &framebufferio_framebufferdisplay_type;
+ common_hal_framebufferio_framebufferdisplay_construct(
+ display,
+ MP_OBJ_FROM_PTR(fb),
+ rotation,
+ true);
+}
+#else
+void picodvi_autoconstruct(void) {
+}
+#endif
diff --git a/ports/raspberrypi/common-hal/picodvi/__init__.h b/ports/raspberrypi/common-hal/picodvi/__init__.h
new file mode 100644
index 0000000000000..41f3656339a50
--- /dev/null
+++ b/ports/raspberrypi/common-hal/picodvi/__init__.h
@@ -0,0 +1,9 @@
+// This file is part of the CircuitPython project: https://circuitpython.org
+//
+// SPDX-FileCopyrightText: Copyright (c) 2025 Jeff Epler for Adafruit Industries
+//
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+extern void picodvi_autoconstruct(void);
diff --git a/shared-bindings/displayio/__init__.h b/shared-bindings/displayio/__init__.h
index 33987ebe38ae5..88e9650cf44a9 100644
--- a/shared-bindings/displayio/__init__.h
+++ b/shared-bindings/displayio/__init__.h
@@ -23,6 +23,9 @@ typedef enum displayio_colorspace {
} displayio_colorspace_t;
void common_hal_displayio_release_displays(void);
+mp_obj_t common_hal_displayio_get_primary_display(void);
+void common_hal_displayio_set_primary_display(mp_obj_t o);
+void common_hal_displayio_auto_primary_display(void);
extern const mp_obj_type_t displayio_colorspace_type;
extern const cp_enum_obj_t displayio_colorspace_RGB888_obj;
diff --git a/shared-bindings/supervisor/Runtime.c b/shared-bindings/supervisor/Runtime.c
index 9332cacb3eccd..15a0096361013 100644
--- a/shared-bindings/supervisor/Runtime.c
+++ b/shared-bindings/supervisor/Runtime.c
@@ -20,6 +20,10 @@
#include "supervisor/shared/status_leds.h"
#include "supervisor/shared/bluetooth/bluetooth.h"
+#if CIRCUITPY_DISPLAYIO
+#include "shared-bindings/displayio/__init__.h"
+#endif
+
#if CIRCUITPY_TINYUSB
#include "tusb.h"
#endif
@@ -183,7 +187,6 @@ MP_PROPERTY_GETSET(supervisor_runtime_ble_workflow_obj,
//| after the current code finishes and the status LED is used to show
//| the finish state."""
//|
-//|
static mp_obj_t supervisor_runtime_get_rgb_status_brightness(mp_obj_t self) {
return MP_OBJ_NEW_SMALL_INT(get_status_brightness());
}
@@ -204,6 +207,39 @@ MP_PROPERTY_GETSET(supervisor_runtime_rgb_status_brightness_obj,
(mp_obj_t)&supervisor_runtime_get_rgb_status_brightness_obj,
(mp_obj_t)&supervisor_runtime_set_rgb_status_brightness_obj);
+#if CIRCUITPY_DISPLAYIO
+//| display: Any
+//| """The primary configured displayio display, if any. Read-only.
+//|
+//| If the board has a display that is hard coded, or that was explicitly set
+//| in boot.py or code.py (including a previous run of code.py), it is
+//| available here until it is released with ``displayio.releasee_displays()``.
+//|
+//| The display can be of any supported display type, such as `busdisplay.BusDisplay`.
+//|
+//| If no display is configured, this property is `None`.
+//|
+//| In a future release of CircuitPython, any display that is not the primary display
+//| will be automatically released at the end of running a code file.
+//|
+//| On boards without displayio, this property is present but the value is always `None`."""
+//|
+//|
+static mp_obj_t supervisor_runtime_get_display(mp_obj_t self) {
+ return common_hal_displayio_get_primary_display();
+}
+MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_display_obj, supervisor_runtime_get_display);
+static mp_obj_t supervisor_runtime_set_display(mp_obj_t self, mp_obj_t new_primary_display) {
+ common_hal_displayio_set_primary_display(new_primary_display);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(supervisor_runtime_set_display_obj, supervisor_runtime_set_display);
+
+MP_PROPERTY_GETSET(supervisor_runtime_display_obj,
+ (mp_obj_t)&supervisor_runtime_get_display_obj,
+ (mp_obj_t)&supervisor_runtime_set_display_obj);
+#endif
+
static const mp_rom_map_elem_t supervisor_runtime_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_usb_connected), MP_ROM_PTR(&supervisor_runtime_usb_connected_obj) },
{ MP_ROM_QSTR(MP_QSTR_serial_connected), MP_ROM_PTR(&supervisor_runtime_serial_connected_obj) },
@@ -213,6 +249,11 @@ static const mp_rom_map_elem_t supervisor_runtime_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_autoreload), MP_ROM_PTR(&supervisor_runtime_autoreload_obj) },
{ MP_ROM_QSTR(MP_QSTR_ble_workflow), MP_ROM_PTR(&supervisor_runtime_ble_workflow_obj) },
{ MP_ROM_QSTR(MP_QSTR_rgb_status_brightness), MP_ROM_PTR(&supervisor_runtime_rgb_status_brightness_obj) },
+ #if CIRCUITPY_DISPLAYIO
+ { MP_ROM_QSTR(MP_QSTR_display), MP_ROM_PTR(&supervisor_runtime_display_obj) },
+ #else
+ { MP_ROM_QSTR(MP_QSTR_display), MP_ROM_NONE },
+ #endif
};
static MP_DEFINE_CONST_DICT(supervisor_runtime_locals_dict, supervisor_runtime_locals_dict_table);
diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c
index 3e1246b087600..4b467b75589d1 100644
--- a/shared-module/displayio/__init__.c
+++ b/shared-module/displayio/__init__.c
@@ -44,6 +44,9 @@
#include "supervisor/spi_flash_api.h"
#endif
+// The default indicates no primary display
+static int primary_display_number = -1;
+
primary_display_bus_t display_buses[CIRCUITPY_DISPLAY_LIMIT];
primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT];
@@ -108,10 +111,16 @@ void displayio_background(void) {
}
-void common_hal_displayio_release_displays(void) {
+static void common_hal_displayio_release_displays_impl(bool keep_primary) {
// Release displays before busses so that they can send any final commands to turn the display
// off properly.
+ if (!keep_primary) {
+ primary_display_number = -1;
+ }
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ if (i == primary_display_number) {
+ continue;
+ }
mp_const_obj_t display_type = displays[i].display_base.type;
if (display_type == NULL || display_type == &mp_type_NoneType) {
continue;
@@ -177,7 +186,14 @@ void common_hal_displayio_release_displays(void) {
supervisor_stop_terminal();
}
+void common_hal_displayio_release_displays(void) {
+ common_hal_displayio_release_displays_impl(false);
+}
+
void reset_displays(void) {
+ // In CircuitPython 10, release secondary displays before doing anything else:
+ // common_hal_displayio_release_displays_impl(true);
+
// 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++) {
mp_const_obj_t display_bus_type = display_buses[i].bus_base.type;
@@ -392,10 +408,13 @@ void displayio_gc_collect(void) {
}
}
+static bool is_display_active(mp_obj_base_t *display_maybe) {
+ return display_maybe->type != &mp_type_NoneType && display_maybe->type != NULL;
+}
+
primary_display_t *allocate_display(void) {
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
- mp_const_obj_t display_type = displays[i].display_base.type;
- if (display_type == NULL || display_type == &mp_type_NoneType) {
+ if (!is_display_active(&displays[i].display_base)) {
// Clear this memory so it is in a known state before init.
memset(&displays[i], 0, sizeof(displays[i]));
// Default to None so that it works as board.DISPLAY.
@@ -434,3 +453,42 @@ primary_display_bus_t *allocate_display_bus_or_raise(void) {
}
mp_raise_RuntimeError(MP_ERROR_TEXT("Too many display busses; forgot displayio.release_displays() ?"));
}
+
+mp_obj_t common_hal_displayio_get_primary_display(void) {
+ if (primary_display_number == -1 || primary_display_number >= CIRCUITPY_DISPLAY_LIMIT) {
+ return mp_const_none;
+ }
+ mp_obj_base_t *primary_display = &displays[primary_display_number].display_base;
+ if (is_display_active(primary_display)) {
+ return MP_OBJ_FROM_PTR(primary_display);
+ }
+ return mp_const_none;
+}
+
+void common_hal_displayio_set_primary_display(mp_obj_t new_primary_display) {
+ if (new_primary_display == mp_const_none) {
+ 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]);
+ if (new_primary_display == display && is_display_active(display)) {
+ primary_display_number = i;
+ return;
+ }
+ }
+ // object was not a display after all...
+ mp_raise_TypeError_varg(MP_ERROR_TEXT("%q must be of type %q or %q, not %q"), MP_QSTR_Display, MP_QSTR_AnyDisplay, MP_QSTR_None, mp_obj_get_type(new_primary_display)->name);
+}
+
+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)) {
+ primary_display_number = i;
+ return;
+ }
+ }
+}