Skip to content

Add supervisor.runtime.display & RP2350 DVI autoconfig (rebased version) #10062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 48 additions & 4 deletions docs/environment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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“ <https://circuitpython.org/board/makerfabs_tft7/>`_

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“ <https://circuitpython.org/board/makerfabs_tft7/>`_
`Sunton ESP32-2432S028 <https://circuitpython.org/board/sunton_esp32_2432S028/>`_
`Sunton ESP32-2432S024C <https://circuitpython.org/board/sunton_esp32_2432S024C/>`_

Expand All @@ -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 <https://circuitpython.org/board/sunton_esp32_8048S050/>`_
`Adafruit Feather RP2350 <https://circuitpython.org/board/adafruit_feather_rp2350/>`_
`Adafruit Metro RP2350 <https://circuitpython.org/board/adafruit_metro_rp2350/>`_

CIRCUITPY_DISPLAY_FREQUENCY
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -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 <https://circuitpython.org/board/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 <https://circuitpython.org/board/adafruit_feather_rp2350/>`_
`Adafruit Metro RP2350 <https://circuitpython.org/board/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 <https://circuitpython.org/board/adafruit_feather_rp2350/>`_
`Adafruit Metro RP2350 <https://circuitpython.org/board/adafruit_metro_rp2350/>`_
7 changes: 7 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions ports/raspberrypi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions ports/raspberrypi/bindings/picodvi/Framebuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions ports/raspberrypi/boards/adafruit_feather_rp2350/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
7 changes: 5 additions & 2 deletions ports/raspberrypi/boards/adafruit_metro_rp2350/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.


Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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)
16 changes: 15 additions & 1 deletion ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
}

Expand Down
119 changes: 119 additions & 0 deletions ports/raspberrypi/common-hal/picodvi/__init__.c
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions ports/raspberrypi/common-hal/picodvi/__init__.h
Original file line number Diff line number Diff line change
@@ -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);
3 changes: 3 additions & 0 deletions shared-bindings/displayio/__init__.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading
Loading