display: Add logic to initialize the Mac Mini HDMI display

Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
Hector Martin 2022-01-17 04:29:51 +09:00
parent eaadc43fbb
commit 1c2a2db1dd
4 changed files with 190 additions and 0 deletions

View file

@ -62,6 +62,7 @@ OBJECTS := \
dart.o \
dcp.o \
dcp_iboot.o \
display.o \
exception.o exception_asm.o \
fb.o font.o font_retina.o \
gxf.o gxf_asm.o \

179
src/display.c Normal file
View file

@ -0,0 +1,179 @@
/* SPDX-License-Identifier: MIT */
#include "display.h"
#include "assert.h"
#include "dcp.h"
#include "dcp_iboot.h"
#include "utils.h"
#include "xnuboot.h"
#define COMPARE(a, b) \
if ((a) > (b)) { \
*best = modes[i]; \
continue; \
} else if ((a) < (b)) { \
continue; \
}
void display_choose_timing_mode(dcp_timing_mode_t *modes, int cnt, dcp_timing_mode_t *best)
{
*best = modes[0];
for (int i = 1; i < cnt; i++) {
COMPARE(modes[i].valid, best->valid);
COMPARE(modes[i].width <= 1920, best->width <= 1920);
COMPARE(modes[i].fps <= 60 << 16, best->fps <= 60 << 16);
COMPARE(modes[i].width, best->width);
COMPARE(modes[i].height, best->height);
COMPARE(modes[i].fps, best->fps);
}
printf("display: timing mode: valid=%d %dx%d %d.%02d Hz\n", best->valid, best->width,
best->height, best->fps >> 16, (best->fps & 0xffff) * 99 / 0xffff);
}
void display_choose_color_mode(dcp_color_mode_t *modes, int cnt, dcp_color_mode_t *best)
{
*best = modes[0];
for (int i = 1; i < cnt; i++) {
COMPARE(modes[i].valid, best->valid);
COMPARE(modes[i].bpp <= 32, best->bpp <= 32);
COMPARE(modes[i].bpp, best->bpp);
COMPARE(-modes[i].colorimetry, -best->colorimetry);
COMPARE(-modes[i].encoding, -best->encoding);
COMPARE(-modes[i].eotf, -best->eotf);
}
printf("display: color mode: valid=%d colorimetry=%d eotf=%d encoding=%d bpp=%d\n", best->valid,
best->colorimetry, best->eotf, best->encoding, best->bpp);
}
int display_configure(void)
{
int ret = -1;
dcp_dev_t *dcp = dcp_init("/arm-io/dcp", "/arm-io/dart-dcp", "/arm-io/dart-disp0");
if (!dcp) {
printf("display: failed to initialize DCP\n");
return -1;
}
// Find the framebuffer DVA
u64 fb_dva = dart_search(dcp->dart_disp, (void *)cur_boot_args.video.base);
if (!fb_dva) {
printf("display: failed to find display DVA\n");
goto err_shutdown;
}
dcp_iboot_if_t *iboot = dcp_ib_init(dcp);
if (!iboot) {
printf("display: failed to initialize DCP iBoot interface\n");
goto err_shutdown;
}
// Power on
if ((ret = dcp_ib_set_power(iboot, true)) < 0) {
printf("display: failed to set power\n");
goto err_iboot;
}
// Detect if display is connected
int timing_cnt, color_cnt;
int hpd = ret = dcp_ib_get_hpd(iboot, &timing_cnt, &color_cnt);
if (hpd < 0) {
printf("display: failed to get display status\n");
goto err_iboot;
}
printf("display: connected:%d timing_cnt:%d color_cnt:%d\n", hpd, timing_cnt, color_cnt);
if (!hpd || !timing_cnt || !color_cnt)
goto bail;
// Find best modes
dcp_timing_mode_t *tmodes, tbest;
if ((ret = dcp_ib_get_timing_modes(iboot, &tmodes)) < 0) {
printf("display: failed to get timing modes\n");
goto err_iboot;
}
assert(ret == timing_cnt);
display_choose_timing_mode(tmodes, timing_cnt, &tbest);
dcp_color_mode_t *cmodes, cbest;
if ((ret = dcp_ib_get_color_modes(iboot, &cmodes)) < 0) {
printf("display: failed to get color modes\n");
goto err_iboot;
}
assert(ret == color_cnt);
display_choose_color_mode(cmodes, color_cnt, &cbest);
// Set mode
if ((ret = dcp_ib_set_mode(iboot, &tbest, &cbest)) < 0) {
printf("display: failed to set mode\n");
goto err_iboot;
}
// Swap!
int swap_id = ret = dcp_ib_swap_begin(iboot);
if (swap_id < 0) {
printf("display: failed to start swap\n");
goto err_iboot;
}
dcp_layer_t layer = {
.planes = {{
.addr = fb_dva,
.stride = tbest.width * 4,
.addr_format = ADDR_PLANAR,
}},
.plane_cnt = 1,
.width = tbest.width,
.height = tbest.height,
.surface_fmt = FMT_w30r,
.colorspace = 2,
.eotf = EOTF_GAMMA_SDR,
.transform = XFRM_NONE,
};
dcp_rect_t rect = {tbest.width, tbest.height, 0, 0};
if ((ret = dcp_ib_swap_set_layer(iboot, 0, &layer, &rect, &rect)) < 0) {
printf("display: failed to set layer\n");
goto err_iboot;
}
if ((ret = dcp_ib_swap_end(iboot)) < 0) {
printf("display: failed to complete swap\n");
goto err_iboot;
}
printf("display: swapped! (swap_id=%d)\n", swap_id);
cur_boot_args.video.stride = layer.planes[0].stride;
cur_boot_args.video.width = layer.width;
cur_boot_args.video.height = layer.height;
cur_boot_args.video.depth = 30;
bail:
ret = 0;
err_iboot:
dcp_ib_shutdown(iboot);
err_shutdown:
dcp_shutdown(dcp);
return ret;
}
int display_init(void)
{
if (cur_boot_args.video.width == 640 && cur_boot_args.video.height == 1136) {
printf("display: Dummy framebuffer found, initializing display\n");
return display_configure();
} else {
printf("display: Display is already initialized (%ldx%ld)\n", cur_boot_args.video.width,
cur_boot_args.video.height);
return 0;
}
}

8
src/display.h Normal file
View file

@ -0,0 +1,8 @@
/* SPDX-License-Identifier: MIT */
#ifndef DISPLAY_H
#define DISPLAY_H
int display_init(void);
#endif

View file

@ -5,6 +5,7 @@
#include "adt.h"
#include "aic.h"
#include "cpufreq.h"
#include "display.h"
#include "exception.h"
#include "fb.h"
#include "gxf.h"
@ -89,6 +90,7 @@ void m1n1_main(void)
mmu_init();
#ifdef USE_FB
display_init();
fb_init();
fb_display_logo();
#endif