summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/boot/Makefile.version2
-rw-r--r--usr/src/boot/sys/boot/common/gfx_fb.c82
-rw-r--r--usr/src/boot/sys/boot/common/gfx_fb.h22
-rw-r--r--usr/src/boot/sys/boot/efi/loader/framebuffer.c100
-rw-r--r--usr/src/boot/sys/boot/i386/libi386/vbe.c48
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)) {