Description
I've been poking on a project that fits a Raspberry Pi 3 CM inside the shell of a Gameboy Advance (video), running a Waveshare32b SPI based display. In this configuration, there is no display connected to HDMI, but instead I run a custom software driver fbcp-ili9341 that interfaces with the dispmanx API to grab framebuffer display and push the pixel data out to SPI.
One issue that is happening is that it looks like the dispmanx_
APIs stop working if the system changes resolution, by an application, or also when changed via tvservice
command line interface. To reproduce, try
test.cpp
#include <stdio.h>
#include <bcm_host.h>
int main()
{
bcm_host_init();
DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0);
if (!display) { printf("vc_dispmanx_display_open failed!\n"); exit(1); }
DISPMANX_MODEINFO_T display_info;
int ret = vc_dispmanx_display_get_info(display, &display_info);
if (ret) { printf("vc_dispmanx_display_get_info failed!\n"); exit(1); }
int framebufferSizeBytes = display_info.width * display_info.height * 2;
uint16_t *videoCoreFramebuffer = (uint16_t *)malloc(framebufferSizeBytes);
memset(videoCoreFramebuffer, 0, framebufferSizeBytes);
uint32_t image_prt;
DISPMANX_RESOURCE_HANDLE_T screen_resource = vc_dispmanx_resource_create(VC_IMAGE_RGB565, display_info.width, display_info.height, &image_prt);
if (!screen_resource) { printf("vc_dispmanx_resource_create failed!\n"); exit(1); }
VC_RECT_T rect;
vc_dispmanx_rect_set(&rect, 0, 0, display_info.width, display_info.height);
vc_dispmanx_snapshot(display, screen_resource, (DISPMANX_TRANSFORM_T)0);
vc_dispmanx_resource_read_data(screen_resource, &rect, videoCoreFramebuffer, display_info.width*2);
uint32_t data = 0;
for(int i = 0; i < framebufferSizeBytes/2; ++i)
data += videoCoreFramebuffer[i];
printf("Data: %u\n", data);
}
and build with g++ test.cpp -o test -I/opt/vc/include -L/opt/vc/lib -lbcm_host
.
Running with the following /boot/config.txt
for display configuration:
hdmi_force_hotplug=1
hdmi_group=2
hdmi_mode=87
hdmi_cvt=320 240 60 1 0 0 0
and after boot of the Pi, with no HDMI display connected, no applications open but the Pi open in console, the above test code gives
pi@retropie:~ $ tvservice -s
state 0x12000a [HDMI DMT (87) RGB full 4:3 x4], 320x240 @ 59.00Hz, progressive
pi@retropie:~ $ ./test
Data: 3470187170
which is good, the application was able to capture some pixel data.
However, the following scenarios cause the above test code to begin to fail:
Change resolution
pi@retropie:~ $ tvservice -s
state 0x12000a [HDMI DMT (87) RGB full 4:3 x4], 320x240 @ 59.00Hz, progressive
pi@retropie:~ $ ./test
Data: 3470187170 ## Good
pi@retropie:~ $ tvservice --modes=CEA
Group CEA has 2 modes:
mode 4: 1280x720 @ 60Hz 16:9, clock:74MHz progressive
mode 16: 1920x1080 @ 60Hz 16:9, clock:148MHz progressive
pi@retropie:~ $ tvservice --explicit="CEA 4 HDMI"
Powering on HDMI with explicit settings (CEA mode 4)
pi@retropie:~ $ tvservice -s
state 0x12000a [HDMI CEA (4) RGB lim 16:9], 1280x720 @ 60.00Hz, progressive
pi@retropie:~ $ ./test
Data: 0 ## Bad
Here after changing the display mode, dispmanx snapshots no longer work but start to return black frames.
Change resolution, but change it back to original
Changing resolution back to "what was working originally" 320x240 does not restore dispmanx to work:
pi@retropie:~ $ tvservice -s
state 0x12000a [HDMI DMT (87) RGB full 4:3 x4], 320x240 @ 59.00Hz, progressive
pi@retropie:~ $ ./test
Data: 3470187170
pi@retropie:~ $ tvservice --explicit="CEA 4 HDMI"
Powering on HDMI with explicit settings (CEA mode 4)
pi@retropie:~ $ tvservice --explicit="DMT 87 HDMI"
Powering on HDMI with explicit settings (DMT mode 87)
pi@retropie:~ $ tvservice -s
state 0x12000a [HDMI DMT (87) RGB full 4:3 x4], 320x240 @ 59.00Hz, progressive
pi@retropie:~ $ ./test
Data: 0
Change resolution to what is already set
It looks like changing the resolution to the same mode that is already currently set causes dispmanx to start outputting black as well:
pi@retropie:~ $ tvservice -s
state 0x12000a [HDMI DMT (87) RGB full 4:3 x4], 320x240 @ 59.00Hz, progressive
pi@retropie:~ $ ./test
Data: 3470187170
pi@retropie:~ $ tvservice --explicit="DMT 87 HDMI"
Powering on HDMI with explicit settings (DMT mode 87)
pi@retropie:~ $ tvservice -s
state 0x12000a [HDMI DMT (87) RGB full 4:3 x4], 320x240 @ 59.00Hz, progressive
pi@retropie:~ $ ./test
Data: 0
Powering HDMI off and then on
pi@retropie:~ $ tvservice -s
state 0x12000a [HDMI DMT (87) RGB full 4:3 x4], 320x240 @ 59.00Hz, progressive
pi@retropie:~ $ ./test
Data: 3470187170
pi@retropie:~ $ tvservice -o
Powering off HDMI
pi@retropie:~ $ tvservice -s
state 0x120002 [TV is off]
pi@retropie:~ $ tvservice --explicit="DMT 87 HDMI"
Powering on HDMI with explicit settings (DMT mode 87)
pi@retropie:~ $ tvservice -s
state 0x12000a [HDMI DMT (87) RGB full 4:3 x4], 320x240 @ 59.00Hz, progressive
pi@retropie:~ $ ./test
vc_dispmanx_display_open failed!
causes dispmanx to no longer initialize.
Are the above scenarios expected to work? Or I wonder if the relationship between tvservice
and dispmanx
is not as 1:1, but there's some initialization/setup steps missing when changing the display?
Also, what is the role of tvservice
versus other methods for changing the HDMI display resolution? Is tvservice
the utility through which all applications must go when switching display modes, or are there other utilities? (I wonder if the above test method is flawed for example if tvservice
should not be used in this kind of way, or something similar)
Thanks for reading through, any suggestions would be most welcome!