mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-22 22:53:04 +00:00
display: Reallocate framebuffer if required for wanted display mode
If an explicitly specified display mode exceeds the allocated framebuffer allocate a new one from the top of RAM. Note: macOS panics immediately with a realloced framebuffer. Signed-off-by: Janne Grunau <j@jannau.net>
This commit is contained in:
parent
1ea31ae996
commit
7f4347ebe8
4 changed files with 123 additions and 30 deletions
|
@ -17,12 +17,14 @@ dcp_dev_t *dcp_init(const char *dcp_path, const char *dcp_dart_path, const char
|
|||
printf("dcp: failed to initialize DCP DART\n");
|
||||
goto out_free;
|
||||
}
|
||||
dart_setup_pt_region(dcp->dart_dcp, dcp_dart_path, 0);
|
||||
|
||||
dcp->dart_disp = dart_init_adt(disp_dart_path, 0, 0, true);
|
||||
if (!dcp->dart_disp) {
|
||||
printf("dcp: failed to initialize DISP DART\n");
|
||||
goto out_dart_dcp;
|
||||
}
|
||||
dart_setup_pt_region(dcp->dart_disp, disp_dart_path, 0);
|
||||
|
||||
dcp->iovad_dcp = iovad_init(0x10000000, 0x20000000);
|
||||
|
||||
|
|
143
src/display.c
143
src/display.c
|
@ -6,6 +6,7 @@
|
|||
#include "dcp.h"
|
||||
#include "dcp_iboot.h"
|
||||
#include "fb.h"
|
||||
#include "memory.h"
|
||||
#include "string.h"
|
||||
#include "utils.h"
|
||||
#include "xnuboot.h"
|
||||
|
@ -41,13 +42,16 @@ static void display_choose_timing_mode(dcp_timing_mode_t *modes, int cnt, dcp_ti
|
|||
|
||||
for (int i = 1; i < cnt; i++) {
|
||||
COMPARE(modes[i].valid, best->valid);
|
||||
COMPARE(display_mode_fb_size(&modes[i]) <= fb_size, display_mode_fb_size(best) <= fb_size);
|
||||
if (want && want->valid) {
|
||||
COMPARE(modes[i].width == want->width && modes[i].height == want->height,
|
||||
best->width == want->width && best->height == want->height);
|
||||
COMPARE(-abs((long)modes[i].fps - (long)want->fps),
|
||||
-abs((long)best->fps - (long)want->fps));
|
||||
} else {
|
||||
COMPARE(display_mode_fb_size(&modes[i]) <= fb_size,
|
||||
display_mode_fb_size(best) <= fb_size);
|
||||
}
|
||||
|
||||
COMPARE(modes[i].width <= 1920, best->width <= 1920);
|
||||
COMPARE(modes[i].height <= 1200, best->height <= 1200);
|
||||
COMPARE(modes[i].fps <= 60 << 16, best->fps <= 60 << 16);
|
||||
|
@ -107,37 +111,37 @@ int display_get_vram(u64 *paddr, u64 *size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static uintptr_t display_map_fb(u64 paddr, u64 size)
|
||||
static uintptr_t display_map_fb(uintptr_t iova, u64 paddr, u64 size)
|
||||
{
|
||||
int ret = 0;
|
||||
if (iova == 0) {
|
||||
s64 iova_disp0 = 0;
|
||||
s64 iova_dcp = 0;
|
||||
|
||||
s64 iova_disp0 = 0;
|
||||
s64 iova_dcp = 0;
|
||||
iova_dcp = dart_find_iova(dcp->dart_dcp, iova_dcp, size);
|
||||
if (iova_dcp < 0) {
|
||||
printf("display: failed to find IOVA for fb of %06zx bytes (dcp)\n", size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
iova_dcp = dart_find_iova(dcp->dart_dcp, iova_dcp, size);
|
||||
if (iova_dcp < 0) {
|
||||
printf("display: failed to find IOVA for fb of %06zx bytes (dcp)\n", size);
|
||||
return 0;
|
||||
// try to map the fb to the same IOVA on disp0
|
||||
iova_disp0 = dart_find_iova(dcp->dart_dcp, iova_dcp, size);
|
||||
if (iova_disp0 < 0) {
|
||||
printf("display: failed to find IOVA for fb of %06zx bytes (disp0)\n", size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// assume this results in the same IOVA, not sure if this is required but matches what iboot
|
||||
// does on other models.
|
||||
if (iova_disp0 != iova_dcp) {
|
||||
printf("display: IOVA mismatch for fb between dcp (%08lx) and disp0 (%08lx)\n",
|
||||
(u64)iova_dcp, (u64)iova_disp0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
iova = iova_dcp;
|
||||
}
|
||||
|
||||
// try to map the fb to the same IOVA on disp0
|
||||
iova_disp0 = dart_find_iova(dcp->dart_dcp, iova_dcp, size);
|
||||
if (iova_disp0 < 0) {
|
||||
printf("display: failed to find IOVA for fb of %06zx bytes (disp0)\n", size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// assume this results in the same IOVA, not sure if this is required but matches what iboot
|
||||
// does on other models.
|
||||
if (iova_disp0 != iova_dcp) {
|
||||
printf("display: IOVA mismatch for fb between dcp (%08lx) and disp0 (%08lx)\n",
|
||||
(u64)iova_dcp, (u64)iova_disp0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t iova = iova_dcp;
|
||||
|
||||
ret = dart_map(dcp->dart_disp, iova, (void *)paddr, size);
|
||||
int ret = dart_map(dcp->dart_disp, iova, (void *)paddr, size);
|
||||
if (ret < 0) {
|
||||
printf("display: failed to map fb to dart-disp0\n");
|
||||
return 0;
|
||||
|
@ -178,7 +182,7 @@ static int display_start_dcp(void)
|
|||
fb_dva = dart_search(dcp->dart_disp, (void *)cur_boot_args.video.base);
|
||||
// framebuffer is not mapped on the M1 Ultra Mac Studio
|
||||
if (!fb_dva)
|
||||
fb_dva = display_map_fb(pa, size);
|
||||
fb_dva = display_map_fb(0, pa, size);
|
||||
if (!fb_dva) {
|
||||
printf("display: failed to find display DVA\n");
|
||||
dcp_shutdown(dcp);
|
||||
|
@ -333,6 +337,76 @@ int display_configure(const char *config)
|
|||
return -1;
|
||||
}
|
||||
|
||||
u64 fb_pa = cur_boot_args.video.base;
|
||||
u64 tmp_dva = 0;
|
||||
|
||||
size_t size =
|
||||
ALIGN_UP(tbest.width * tbest.height * ((cbest.bpp + 7) / 8) + 24 * SZ_16K, SZ_16K);
|
||||
|
||||
if (fb_size < size) {
|
||||
printf("display: current framebuffer is too small for new mode\n");
|
||||
|
||||
/* rtkit uses 0x10000000 as DVA offset, FB starts in the first page */
|
||||
if ((s64)size > 7 * SZ_32M) {
|
||||
printf("display: not enough reserved L2 DVA space for fb size 0x%zx\n", size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cur_boot_args.mem_size -= size;
|
||||
fb_pa = cur_boot_args.phys_base + cur_boot_args.mem_size;
|
||||
/* add guard page between RAM and framebuffer */
|
||||
// TODO: update mapping?
|
||||
cur_boot_args.mem_size -= SZ_16K;
|
||||
|
||||
memset((void *)fb_pa, 0, size);
|
||||
|
||||
tmp_dva = iova_alloc(dcp->iovad_dcp, size);
|
||||
|
||||
ret = display_map_fb(tmp_dva, fb_pa, size);
|
||||
if (ret < 0) {
|
||||
printf("display: failed to map new fb\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Swap!
|
||||
u32 stride = tbest.width * 4;
|
||||
ret = display_swap(tmp_dva, stride, tbest.width, tbest.height);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* wait for swap durations + 1ms */
|
||||
u32 delay = (((1000 << 16) + tbest.fps - 1) / tbest.fps) + 1;
|
||||
mdelay(delay);
|
||||
dart_unmap(dcp->dart_disp, fb_dva, fb_size);
|
||||
dart_unmap(dcp->dart_dcp, fb_dva, fb_size);
|
||||
|
||||
ret = display_map_fb(fb_dva, fb_pa, size);
|
||||
if (ret < 0) {
|
||||
printf("display: failed to map new fb\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fb_size = size;
|
||||
mmu_map_framebuffer(fb_pa, fb_size);
|
||||
|
||||
/* update ADT with the physical address of the new framebuffer */
|
||||
u64 fb_reg[2] = {fb_pa, size};
|
||||
int node = adt_path_offset(adt, "vram");
|
||||
if (node >= 0) {
|
||||
// TODO: adt_set_reg(adt, node, "vram", fb_pa, size);?
|
||||
ret = adt_setprop(adt, node, "reg", &fb_reg, sizeof(fb_reg));
|
||||
if (ret < 0)
|
||||
printf("display: failed to update '/vram'\n");
|
||||
}
|
||||
node = adt_path_offset(adt, "/chosen/carveout-memory-map");
|
||||
if (node >= 0) {
|
||||
// TODO: adt_set_reg(adt, node, "vram", fb_pa, size);?
|
||||
ret = adt_setprop(adt, node, "region-id-14", &fb_reg, sizeof(fb_reg));
|
||||
if (ret < 0)
|
||||
printf("display: failed to update '/chosen/carveout-memory-map/region-id-14'\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Swap!
|
||||
u32 stride = tbest.width * 4;
|
||||
ret = display_swap(fb_dva, stride, tbest.width, tbest.height);
|
||||
|
@ -341,8 +415,10 @@ int display_configure(const char *config)
|
|||
|
||||
printf("display: swapped! (swap_id=%d)\n", ret);
|
||||
|
||||
if (cur_boot_args.video.stride != stride || cur_boot_args.video.width != tbest.width ||
|
||||
cur_boot_args.video.height != tbest.height || cur_boot_args.video.depth != 30) {
|
||||
if (fb_pa != cur_boot_args.video.base || cur_boot_args.video.stride != stride ||
|
||||
cur_boot_args.video.width != tbest.width || cur_boot_args.video.height != tbest.height ||
|
||||
cur_boot_args.video.depth != 30) {
|
||||
cur_boot_args.video.base = fb_pa;
|
||||
cur_boot_args.video.stride = stride;
|
||||
cur_boot_args.video.width = tbest.width;
|
||||
cur_boot_args.video.height = tbest.height;
|
||||
|
@ -353,6 +429,13 @@ int display_configure(const char *config)
|
|||
/* Update for python / subsequent stages */
|
||||
memcpy((void *)boot_args_addr, &cur_boot_args, sizeof(cur_boot_args));
|
||||
|
||||
if (tmp_dva) {
|
||||
// unmap / free temporary dva
|
||||
dart_unmap(dcp->dart_disp, tmp_dva, size);
|
||||
dart_unmap(dcp->dart_dcp, tmp_dva, size);
|
||||
iova_free(dcp->iovad_dcp, tmp_dva, size);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -379,6 +379,13 @@ static void mmu_remap_ranges(void)
|
|||
}
|
||||
}
|
||||
|
||||
void mmu_map_framebuffer(u64 addr, size_t size)
|
||||
{
|
||||
printf("MMU: Adding UC mapping at 0x%lx (0x%zx) for framebuffer\n", addr, size);
|
||||
dc_civac_range((void *)addr, size);
|
||||
mmu_add_mapping(addr, addr, size, MAIR_IDX_FRAMEBUFFER, PERM_RW_EL0);
|
||||
}
|
||||
|
||||
static void mmu_add_default_mappings(void)
|
||||
{
|
||||
mmu_map_mmio();
|
||||
|
|
|
@ -71,6 +71,7 @@ void mmu_init_secondary(int cpu);
|
|||
void mmu_shutdown(void);
|
||||
void mmu_add_mapping(u64 from, u64 to, size_t size, u8 attribute_index, u64 perms);
|
||||
void mmu_rm_mapping(u64 from, size_t size);
|
||||
void mmu_map_framebuffer(u64 addr, size_t size);
|
||||
|
||||
u64 mmu_disable(void);
|
||||
void mmu_restore(u64 state);
|
||||
|
|
Loading…
Reference in a new issue