From cd849c181cc71e1498db4b137e678061d3abe212 Mon Sep 17 00:00:00 2001 From: Vincent Duvert Date: Sun, 27 Mar 2022 16:32:54 +0000 Subject: [PATCH] display: Add wait option to handle monitor disconnection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some monitors disconnect when getting out of sleep mode; they rely on the OS hotplug support to work properly. Since stage1 m1n1 initializes the display, the monitor is already getting out of sleep mode when stage2 m1n1 starts execution. For the display to work, stage2 m1n1 has to wait for the monitor to disconnect (which unfortunately can take close to 10 seconds), wait for reconnection, and then proceed as normal. Since this process delays boot for 10 seconds on monitors that don’t disconnect, it is not enabled by default. To enable it, use the following boot parameter: display=wait To specify the wait delay (in seconds): display=wait:5 To specify the resolution (can also include an explicit wait delay): display=wait,1980x1080 (The parameters cannot be swapped) Should fix #159. Signed-off-by: Vincent Duvert --- src/display.c | 106 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 17 deletions(-) diff --git a/src/display.c b/src/display.c index c603629..80bfa88 100644 --- a/src/display.c +++ b/src/display.c @@ -12,6 +12,7 @@ #define DISPLAY_STATUS_DELAY 100 #define DISPLAY_STATUS_RETRIES 20 +#define DISPLAY_WAIT_DELAY 10 #define COMPARE(a, b) \ if ((a) > (b)) { \ @@ -165,11 +166,37 @@ static int display_start_dcp(void) return 0; } -int display_parse_mode(const char *config, dcp_timing_mode_t *mode) +int display_parse_mode(const char *config, dcp_timing_mode_t *mode, int *wait_delay) { memset(mode, 0, sizeof(*mode)); + *wait_delay = 0; - if (!config || !strcmp(config, "auto")) + if (!config) + return 0; + + if (!strncmp(config, "wait", sizeof("wait") - 1)) { + int delay = 0; + config += sizeof("wait") - 1; + if (*config == ':') { + config += 1; + for (char c = *config; '0' <= c && c <= '9'; c = *(++config)) { + delay = (delay * 10) + (c - '0'); + } + } + + if (delay <= 0) { + delay = DISPLAY_WAIT_DELAY; + } + + printf("display: wait enabled (max delay %ds)\n", delay); + *wait_delay = delay; + + if (*config == ',') { + config += 1; + } + } + + if (!*config || !strcmp(config, "auto")) return 0; const char *s_w = config; @@ -237,11 +264,53 @@ static int display_swap(u64 fb_dva, u32 stride, u32 width, u32 height) return swap_id; } +int display_wait_connected(dcp_iboot_if_t *iboot, int *timing_cnt, int *color_cnt) +{ + int hpd; + + for (int retries = 0; retries < DISPLAY_STATUS_RETRIES; retries += 1) { + hpd = dcp_ib_get_hpd(iboot, timing_cnt, color_cnt); + if ((hpd > 0) && *timing_cnt && *color_cnt) { + printf("display: waited %d ms for display connected\n", retries * DISPLAY_STATUS_DELAY); + return 1; + } + + mdelay(DISPLAY_STATUS_DELAY); + } + + // hpd is 0 if no display, negative if an error occurred + return hpd; +} + +int display_wait_disconnected(dcp_iboot_if_t *iboot, int wait_delay) +{ + int hpd, timing_cnt, color_cnt; + int max_retries = wait_delay * 1000 / DISPLAY_STATUS_DELAY; + + for (int retries = 0; retries < max_retries; retries += 1) { + hpd = dcp_ib_get_hpd(iboot, &timing_cnt, &color_cnt); + if (hpd < 0) { + return hpd; + } + + if (!hpd) { + printf("display: waited %d ms for display disconnected\n", + retries * DISPLAY_STATUS_DELAY); + return 1; + } + + mdelay(DISPLAY_STATUS_DELAY); + } + + return 0; +} + int display_configure(const char *config) { dcp_timing_mode_t want; + int wait_delay; - display_parse_mode(config, &want); + display_parse_mode(config, &want, &wait_delay); int ret = display_start_dcp(); if (ret < 0) @@ -255,29 +324,32 @@ int display_configure(const char *config) // Detect if display is connected int timing_cnt, color_cnt; - int hpd = 0, retries = 0; + + if (wait_delay) { + // Some monitors disconnect when getting out of sleep mode. + // Wait a bit to see if that happens. + printf("display: waiting for monitor disconnect\n"); + if ((ret = display_wait_disconnected(iboot, wait_delay)) < 0) { + printf("display: failed to wait for disconnect\n"); + return -1; + } + + if (!ret) { + printf("display: did not disconnect\n"); + } + } /* After boot DCP does not immediately report a connected display. Retry getting display * information for 2 seconds. */ - while (retries++ < DISPLAY_STATUS_RETRIES) { - hpd = dcp_ib_get_hpd(iboot, &timing_cnt, &color_cnt); - if (hpd < 0) - ret = hpd; - else if (hpd && timing_cnt && color_cnt) - break; - if (retries < DISPLAY_STATUS_RETRIES) - mdelay(DISPLAY_STATUS_DELAY); - } - printf("display: waited %d ms for display status\n", (retries - 1) * DISPLAY_STATUS_DELAY); - if (ret < 0) { + if ((ret = display_wait_connected(iboot, &timing_cnt, &color_cnt)) < 0) { printf("display: failed to get display status\n"); return 0; } - printf("display: connected:%d timing_cnt:%d color_cnt:%d\n", hpd, timing_cnt, color_cnt); + printf("display: connected:%d timing_cnt:%d color_cnt:%d\n", ret, timing_cnt, color_cnt); - if (!hpd || !timing_cnt || !color_cnt) + if (!ret || !timing_cnt || !color_cnt) return 0; // Find best modes -- 2.30.2