display: Put DCP to sleep if display is external

This stops DCP from killing our modeset if the connection cycles.

Also force a (potential) configure cycle if the display is external;
this makes sure updated stage2s will have a chance at fixing issues of
old stage1s. Modesetting is fast when it's the same mode as before.

Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
Hector Martin 2022-06-01 01:54:11 +09:00
parent 9748b5525e
commit 714420a694
8 changed files with 77 additions and 22 deletions

View file

@ -66,6 +66,11 @@ class EXC_RET(IntEnum):
EXIT_GUEST = 3
STEP = 4
class DCP_SHUTDOWN_MODE(IntEnum):
QUIESCED = 0
SLEEP_IF_EXTERNAL = 1
SLEEP = 2
ExcInfo = Struct(
"regs" / Array(32, Int64ul),
"spsr" / RegAdapter(SPSR),
@ -1043,8 +1048,8 @@ class M1N1Proxy(Reloadable):
return self.request(self.P_DISPLAY_INIT)
def display_configure(self, cfg):
return self.request(self.P_DISPLAY_CONFIGURE, cfg)
def display_shutdown(self):
return self.request(self.P_DISPLAY_SHUTDOWN)
def display_shutdown(self, mode):
return self.request(self.P_DISPLAY_SHUTDOWN, mode)
__all__.extend(k for k, v in globals().items()
if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)

View file

@ -3,6 +3,7 @@
#include "dcp.h"
#include "adt.h"
#include "malloc.h"
#include "pmgr.h"
#include "rtkit.h"
#include "utils.h"
@ -59,9 +60,14 @@ out_free:
return NULL;
}
int dcp_shutdown(dcp_dev_t *dcp)
int dcp_shutdown(dcp_dev_t *dcp, bool sleep)
{
rtkit_quiesce(dcp->rtkit);
if (sleep) {
rtkit_sleep(dcp->rtkit);
pmgr_reset(0, "DISP0_CPU0");
} else {
rtkit_quiesce(dcp->rtkit);
}
rtkit_free(dcp->rtkit);
dart_shutdown(dcp->dart_disp);
iovad_shutdown(dcp->iovad_dcp, dcp->dart_dcp);

View file

@ -17,6 +17,6 @@ typedef struct {
dcp_dev_t *dcp_init(const char *dcp_path, const char *dcp_dart_path, const char *disp_dart_path);
int dcp_shutdown(dcp_dev_t *dcp);
int dcp_shutdown(dcp_dev_t *dcp, bool sleep);
#endif

View file

@ -22,10 +22,11 @@
continue; \
}
dcp_dev_t *dcp;
dcp_iboot_if_t *iboot;
u64 fb_dva;
u64 fb_size;
static dcp_dev_t *dcp;
static dcp_iboot_if_t *iboot;
static u64 fb_dva;
static u64 fb_size;
static bool display_is_external;
#define abs(x) ((x) >= 0 ? (x) : -(x))
@ -157,7 +158,7 @@ static uintptr_t display_map_fb(uintptr_t iova, u64 paddr, u64 size)
return iova;
}
static int display_start_dcp(void)
int display_start_dcp(void)
{
if (iboot)
return 0;
@ -185,14 +186,14 @@ static int display_start_dcp(void)
fb_dva = display_map_fb(0, pa, size);
if (!fb_dva) {
printf("display: failed to find display DVA\n");
dcp_shutdown(dcp);
dcp_shutdown(dcp, false);
return -1;
}
iboot = dcp_ib_init(dcp);
if (!iboot) {
printf("display: failed to initialize DCP iBoot interface\n");
dcp_shutdown(dcp);
dcp_shutdown(dcp, false);
return -1;
}
@ -453,9 +454,24 @@ int display_configure(const char *config)
int display_init(void)
{
int node = adt_path_offset(adt, "/arm-io/disp0");
if (node < 0) {
printf("DISP0 node not found!\n");
return -1;
}
display_is_external = adt_getprop(adt, node, "external", NULL);
if (display_is_external)
printf("display: Display is external\n");
else
printf("display: Display is internal\n");
if (cur_boot_args.video.width == 640 && cur_boot_args.video.height == 1136) {
printf("display: Dummy framebuffer found, initializing display\n");
return display_configure(NULL);
} else if (display_is_external) {
printf("display: External display found, reconfiguring\n");
return display_configure(NULL);
} else {
printf("display: Display is already initialized (%ldx%ld)\n", cur_boot_args.video.width,
@ -464,11 +480,27 @@ int display_init(void)
}
}
void display_shutdown(void)
void display_shutdown(dcp_shutdown_mode mode)
{
if (iboot) {
dcp_ib_shutdown(iboot);
dcp_shutdown(dcp);
switch (mode) {
case DCP_QUIESCED:
printf("display: Quiescing DCP (unconditional)\n");
dcp_shutdown(dcp, false);
break;
case DCP_SLEEP_IF_EXTERNAL:
if (!display_is_external)
printf("display: Quiescing DCP (internal)\n");
else
printf("display: Sleeping DCP (external)\n");
dcp_shutdown(dcp, display_is_external);
break;
case DCP_SLEEP:
printf("display: Sleeping DCP (unconditional)\n");
dcp_shutdown(dcp, true);
break;
}
iboot = NULL;
}
}

View file

@ -3,8 +3,15 @@
#ifndef DISPLAY_H
#define DISPLAY_H
typedef enum _dcp_shutdown_mode {
DCP_QUIESCED = 0,
DCP_SLEEP_IF_EXTERNAL = 1,
DCP_SLEEP = 2,
} dcp_shutdown_mode;
int display_init(void);
int display_start_dcp(void);
int display_configure(const char *config);
void display_shutdown(void);
void display_shutdown(dcp_shutdown_mode mode);
#endif

View file

@ -50,7 +50,9 @@ static struct hv_secondary_info_t hv_secondary_info;
void hv_init(void)
{
pcie_shutdown();
display_shutdown();
// Make sure we wake up DCP if we put it to sleep, just quiesce it to match ADT
if (display_start_dcp() >= 0)
display_shutdown(DCP_QUIESCED);
// reenable hpm interrupts for the guest for unused iodevs
usb_hpm_restore_irqs(0);
smp_start_secondaries();

View file

@ -132,9 +132,15 @@ void m1n1_main(void)
gxf_init();
mcc_init();
mmu_init();
aic_init();
wdt_disable();
pmgr_init();
#ifdef USE_FB
display_init();
// Kick DCP to sleep, so dodgy monitors which cause reconnect cycles don't cause us to lose the
// framebuffer.
display_shutdown(DCP_SLEEP_IF_EXTERNAL);
fb_init(false);
fb_display_logo();
#ifdef FB_SILENT_MODE
@ -144,9 +150,6 @@ void m1n1_main(void)
#endif
#endif
aic_init();
wdt_disable();
pmgr_init();
clk_init();
cpufreq_init();
sep_init();
@ -164,7 +167,7 @@ void m1n1_main(void)
nvme_shutdown();
exception_shutdown();
usb_iodev_shutdown();
display_shutdown();
display_shutdown(DCP_SLEEP_IF_EXTERNAL);
#ifdef USE_FB
fb_shutdown(next_stage.restore_logo);
#endif

View file

@ -531,7 +531,7 @@ int proxy_process(ProxyRequest *request, ProxyReply *reply)
reply->retval = display_configure((char *)request->args[0]);
break;
case P_DISPLAY_SHUTDOWN:
display_shutdown();
display_shutdown(request->args[0]);
break;
default: