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; + } + } +}