diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/boot/Makefile.version | 2 | ||||
-rw-r--r-- | usr/src/boot/sys/boot/common/gfx_fb.c | 82 | ||||
-rw-r--r-- | usr/src/boot/sys/boot/common/gfx_fb.h | 22 | ||||
-rw-r--r-- | usr/src/boot/sys/boot/efi/loader/framebuffer.c | 100 | ||||
-rw-r--r-- | usr/src/boot/sys/boot/i386/libi386/vbe.c | 48 |
5 files changed, 192 insertions, 62 deletions
diff --git a/usr/src/boot/Makefile.version b/usr/src/boot/Makefile.version index 8644947a6e..76b7a4056d 100644 --- a/usr/src/boot/Makefile.version +++ b/usr/src/boot/Makefile.version @@ -33,4 +33,4 @@ LOADER_VERSION = 1.1 # Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes. # The version is processed from left to right, the version number can only # be increased. -BOOT_VERSION = $(LOADER_VERSION)-2020.09.24.1 +BOOT_VERSION = $(LOADER_VERSION)-2020.09.25.1 diff --git a/usr/src/boot/sys/boot/common/gfx_fb.c b/usr/src/boot/sys/boot/common/gfx_fb.c index 84d5e50fe2..78d3910e39 100644 --- a/usr/src/boot/sys/boot/common/gfx_fb.c +++ b/usr/src/boot/sys/boot/common/gfx_fb.c @@ -2011,3 +2011,85 @@ command_font(int argc, char *argv[]) } return (rc); } + +bool +gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res) +{ + struct resolution *rp, *p; + + /* + * Walk detailed timings tables (4). + */ + if ((edid->display.supported_features + & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) { + /* Walk detailed timing descriptors (4) */ + for (int i = 0; i < DET_TIMINGS; i++) { + /* + * Reserved value 0 is not used for display decriptor. + */ + if (edid->detailed_timings[i].pixel_clock == 0) + continue; + if ((rp = malloc(sizeof (*rp))) == NULL) + continue; + rp->width = GET_EDID_INFO_WIDTH(edid, i); + rp->height = GET_EDID_INFO_HEIGHT(edid, i); + if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS && + rp->height > 0 && rp->height <= EDID_MAX_LINES) + TAILQ_INSERT_TAIL(res, rp, next); + else + free(rp); + } + } + + /* + * Walk standard timings list (8). + */ + for (int i = 0; i < STD_TIMINGS; i++) { + /* Is this field unused? */ + if (edid->standard_timings[i] == 0x0101) + continue; + + if ((rp = malloc(sizeof (*rp))) == NULL) + continue; + + rp->width = HSIZE(edid->standard_timings[i]); + switch (RATIO(edid->standard_timings[i])) { + case RATIO1_1: + rp->height = HSIZE(edid->standard_timings[i]); + if (edid->header.version > 1 || + edid->header.revision > 2) { + rp->height = rp->height * 10 / 16; + } + break; + case RATIO4_3: + rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4; + break; + case RATIO5_4: + rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5; + break; + case RATIO16_9: + rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16; + break; + } + + /* + * Create resolution list in decreasing order, except keep + * first entry (preferred timing mode). + */ + TAILQ_FOREACH(p, res, next) { + if (p->width * p->height < rp->width * rp->height) { + /* Keep preferred mode first */ + if (TAILQ_FIRST(res) == p) + TAILQ_INSERT_AFTER(res, p, rp, next); + else + TAILQ_INSERT_BEFORE(p, rp, next); + break; + } + if (TAILQ_NEXT(p, next) == NULL) { + TAILQ_INSERT_TAIL(res, rp, next); + break; + } + } + } + return (!TAILQ_EMPTY(res)); +} diff --git a/usr/src/boot/sys/boot/common/gfx_fb.h b/usr/src/boot/sys/boot/common/gfx_fb.h index cb2d37042d..e242931a44 100644 --- a/usr/src/boot/sys/boot/common/gfx_fb.h +++ b/usr/src/boot/sys/boot/common/gfx_fb.h @@ -19,6 +19,7 @@ #include <stdbool.h> #include <sys/visual_io.h> #include <sys/multiboot2.h> +#include <sys/queue.h> #include <pnglite.h> #ifdef __cplusplus @@ -93,6 +94,18 @@ struct vesa_edid_info { uint8_t checksum; } __packed; +#define STD_TIMINGS 8 +#define DET_TIMINGS 4 + +#define HSIZE(x) (((x & 0xff) + 31) * 8) +#define RATIO(x) ((x & 0xC000) >> 14) +#define RATIO1_1 0 +/* EDID Ver. 1.3 redefined this */ +#define RATIO16_10 RATIO1_1 +#define RATIO4_3 1 +#define RATIO5_4 2 +#define RATIO16_9 3 + /* * Number of pixels and lines is 12-bit int, valid values 0-4095. */ @@ -109,9 +122,18 @@ struct vesa_edid_info { (((uint_t)(edid_info)->detailed_timings[(timings_num)].vertical_hi & \ 0xf0) << 4)) +struct resolution { + uint32_t width; + uint32_t height; + TAILQ_ENTRY(resolution) next; +}; + +typedef TAILQ_HEAD(edid_resolution, resolution) edid_res_list_t; + extern multiboot_tag_framebuffer_t gfx_fb; void bios_text_font(bool); +bool gfx_get_edid_resolution(struct vesa_edid_info *, edid_res_list_t *); void gfx_framework_init(struct visual_ops *); uint32_t gfx_fb_color_map(uint8_t); void gfx_fb_display_cursor(struct vis_conscursor *); diff --git a/usr/src/boot/sys/boot/efi/loader/framebuffer.c b/usr/src/boot/sys/boot/efi/loader/framebuffer.c index f063cc1639..534e00cf9c 100644 --- a/usr/src/boot/sys/boot/efi/loader/framebuffer.c +++ b/usr/src/boot/sys/boot/efi/loader/framebuffer.c @@ -50,10 +50,11 @@ EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID; EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID; static EFI_GUID active_edid_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID; -static EFI_GUID discovered_edid_guid = EFI_EDID_DISCOVERED_PROTOCOL_GUID; /* Saved initial GOP mode. */ static uint32_t default_mode = UINT32_MAX; +/* Cached EDID. */ +static struct vesa_edid_info *edid_info; static uint32_t gop_default_mode(void); static int efifb_set_mode(EFI_GRAPHICS_OUTPUT *, uint_t); @@ -451,63 +452,54 @@ efifb_gop_get_edid(EFI_HANDLE gop) { const uint8_t magic[] = EDID_MAGIC; EFI_EDID_ACTIVE_PROTOCOL *edid; - struct vesa_edid_info *edid_info; + struct vesa_edid_info *edid_infop; EFI_GUID *guid; EFI_STATUS status; size_t size; - edid_info = calloc(1, sizeof (*edid_info)); - if (edid_info == NULL) - return (NULL); - guid = &active_edid_guid; - status = BS->OpenProtocol(gop, guid, (void **)&edid, IH, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (status != EFI_SUCCESS) { - guid = &discovered_edid_guid; - status = BS->OpenProtocol(gop, guid, (void **)&edid, IH, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL); - } + status = OpenProtocolByHandle(gop, guid, (void **)&edid); if (status != EFI_SUCCESS) - goto error; + return (NULL); + + size = sizeof (*edid_infop); + if (size < edid->SizeOfEdid) + size = edid->SizeOfEdid; - size = edid->SizeOfEdid; - if (size > sizeof (*edid_info)) - size = sizeof (*edid_info); + edid_infop = calloc(1, size); + if (edid_infop == NULL) { + status = BS->CloseProtocol(gop, guid, IH, NULL); + return (NULL); + } - memcpy(edid_info, edid->Edid, size); + memcpy(edid_infop, edid->Edid, edid->SizeOfEdid); status = BS->CloseProtocol(gop, guid, IH, NULL); /* Validate EDID */ - if (memcmp(edid_info, magic, sizeof (magic)) != 0) + if (memcmp(edid_infop, magic, sizeof (magic)) != 0) goto error; - if (edid_info->header.version == 1 && - (edid_info->display.supported_features - & EDID_FEATURE_PREFERRED_TIMING_MODE) && - edid_info->detailed_timings[0].pixel_clock) { - return (edid_info); - } + if (edid_infop->header.version != 1) + goto error; + return (edid_infop); error: - free(edid_info); + free(edid_infop); return (NULL); } -static int -efifb_get_edid(UINT32 *pwidth, UINT32 *pheight) +static bool +efifb_get_edid(edid_res_list_t *res) { extern EFI_GRAPHICS_OUTPUT *gop; - struct vesa_edid_info *edid_info; - int rv = 1; - - edid_info = efifb_gop_get_edid(gop); - if (edid_info != NULL) { - *pwidth = GET_EDID_INFO_WIDTH(edid_info, 0); - *pheight = GET_EDID_INFO_HEIGHT(edid_info, 0); - rv = 0; - } - free(edid_info); + bool rv = false; + + if (edid_info == NULL) + edid_info = efifb_gop_get_edid(gop); + + if (edid_info != NULL) + rv = gfx_get_edid_resolution(edid_info, res); + return (rv); } @@ -548,13 +540,22 @@ static void print_efifb(int mode, struct efi_fb *efifb, int verbose) { uint_t depth; - UINT32 width, height; + edid_res_list_t res; + struct resolution *rp; + TAILQ_INIT(&res); if (verbose == 1) { printf("Framebuffer mode: %s\n", plat_stdout_is_framebuffer() ? "on" : "off"); - if (efifb_get_edid(&width, &height) == 0) - printf("EDID mode: %dx%d\n\n", width, height); + if (efifb_get_edid(&res)) { + printf("EDID"); + while ((rp = TAILQ_FIRST(&res)) != NULL) { + printf(" %dx%d", rp->width, rp->height); + TAILQ_REMOVE(&res, rp, next); + free(rp); + } + printf("\n"); + } } if (mode >= 0) { @@ -665,12 +666,23 @@ efifb_find_mode(char *str) static uint32_t gop_default_mode(void) { + edid_res_list_t res; + struct resolution *rp; extern EFI_GRAPHICS_OUTPUT *gop; - UINT32 mode, width = 0, height = 0; + UINT32 mode; mode = gop->Mode->MaxMode; - if (efifb_get_edid(&width, &height) == 0) - mode = efifb_find_mode_xydm(width, height, -1, -1); + TAILQ_INIT(&res); + if (efifb_get_edid(&res)) { + while ((rp = TAILQ_FIRST(&res)) != NULL) { + if (mode == gop->Mode->MaxMode) { + mode = efifb_find_mode_xydm( + rp->width, rp->height, -1, -1); + } + TAILQ_REMOVE(&res, rp, next); + free(rp); + } + } if (mode == gop->Mode->MaxMode) mode = default_mode; diff --git a/usr/src/boot/sys/boot/i386/libi386/vbe.c b/usr/src/boot/sys/boot/i386/libi386/vbe.c index c3cc09dd53..d0831d1599 100644 --- a/usr/src/boot/sys/boot/i386/libi386/vbe.c +++ b/usr/src/boot/sys/boot/i386/libi386/vbe.c @@ -494,7 +494,7 @@ vbe_dump_mode(int modenum, struct modeinfoblock *mi) } static bool -vbe_get_edid(uint_t *pwidth, uint_t *pheight) +vbe_get_edid(edid_res_list_t *res) { struct vesa_edid_info *edid_info; const uint8_t magic[] = EDID_MAGIC; @@ -517,18 +517,11 @@ vbe_get_edid(uint_t *pwidth, uint_t *pheight) if (memcmp(edid_info, magic, sizeof (magic)) != 0) goto done; - if (!(edid_info->header.version == 1 && - (edid_info->display.supported_features - & EDID_FEATURE_PREFERRED_TIMING_MODE) && - edid_info->detailed_timings[0].pixel_clock)) + /* Unknown EDID version. */ + if (edid_info->header.version != 1) goto done; - *pwidth = GET_EDID_INFO_WIDTH(edid_info, 0); - *pheight = GET_EDID_INFO_HEIGHT(edid_info, 0); - - if (*pwidth > 0 && *pwidth <= EDID_MAX_PIXELS && - *pheight > 0 && *pheight <= EDID_MAX_LINES) - ret = true; + ret = gfx_get_edid_resolution(edid_info, res); done: bio_free(edid_info, sizeof (*edid_info)); return (ret); @@ -596,6 +589,8 @@ vbe_modelist(int depth) int ddc_caps; uint_t width, height; bool edid = false; + edid_res_list_t res; + struct resolution *rp; if (!vbe_check()) return; @@ -608,11 +603,19 @@ vbe_modelist(int depth) if (ddc_caps & 2) printf(" [DDC2]"); - edid = vbe_get_edid(&width, &height); - if (edid) - printf(": EDID %dx%d\n", width, height); - else + TAILQ_INIT(&res); + edid = vbe_get_edid(&res); + if (edid) { + printf(": EDID"); + while ((rp = TAILQ_FIRST(&res)) != NULL) { + printf(" %dx%d", rp->width, rp->height); + TAILQ_REMOVE(&res, rp, next); + free(rp); + } + printf("\n"); + } else { printf(": no EDID information\n"); + } } if (!edid) if (vbe_get_flatpanel(&width, &height)) @@ -744,12 +747,23 @@ vbe_print_mode(void) int vbe_default_mode(void) { + edid_res_list_t res; + struct resolution *rp; int modenum; uint_t width, height; modenum = 0; - if (vbe_get_edid(&width, &height)) - modenum = vbe_find_mode_xydm(width, height, -1, -1); + TAILQ_INIT(&res); + if (vbe_get_edid(&res)) { + while ((rp = TAILQ_FIRST(&res)) != NULL) { + if (modenum == 0) { + modenum = vbe_find_mode_xydm( + rp->width, rp->height, -1, -1); + } + TAILQ_REMOVE(&res, rp, next); + free(rp); + } + } if (modenum == 0 && vbe_get_flatpanel(&width, &height)) { |