Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 4 Mar 2021 21:00:23 GMT
From:      Toomas Soome <tsoome@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: 209247b9614f - stable/13 - loader: use display pixel density for font autoselection
Message-ID:  <202103042100.124L0NJ2017649@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by tsoome:

URL: https://cgit.FreeBSD.org/src/commit/?id=209247b9614f157c17a1e3aebefca4292653536b

commit 209247b9614f157c17a1e3aebefca4292653536b
Author:     Toomas Soome <tsoome@FreeBSD.org>
AuthorDate: 2021-02-20 08:51:28 +0000
Commit:     Toomas Soome <tsoome@FreeBSD.org>
CommitDate: 2021-03-04 15:38:37 +0000

    loader: use display pixel density for font autoselection
    
    Calculate font size from 16 density independent pixels (dp) by using:
    size = 16 * ppi/160 * display_factor
    
    We are specifying font size 16dp, and assuming 1dp = 160ppi.
    Also apply scaling factor 2 (display_factor).
    
    (cherry picked from commit becaac3972f1fde4e3c44516399468ba5ca65c9b)
---
 stand/common/gfx_fb.c          | 110 +++++++++++++++++++++++++++++++++++++++++
 stand/common/gfx_fb.h          |   2 +
 stand/efi/libefi/efi_console.c |  23 ++++++---
 stand/efi/loader/framebuffer.c |  96 +++++++++++++++++++++++++++++++++--
 stand/i386/libi386/vbe.c       |  25 ++++++----
 5 files changed, 237 insertions(+), 19 deletions(-)

diff --git a/stand/common/gfx_fb.c b/stand/common/gfx_fb.c
index 02a0a3d2be22..77cf1d39854f 100644
--- a/stand/common/gfx_fb.c
+++ b/stand/common/gfx_fb.c
@@ -1863,6 +1863,113 @@ reset_font_flags(void)
 	}
 }
 
+/* Return  w^2 + h^2 or 0, if the dimensions are unknown */
+static unsigned
+edid_diagonal_squared(void)
+{
+	unsigned w, h;
+
+	if (edid_info == NULL)
+		return (0);
+
+	w = edid_info->display.max_horizontal_image_size;
+	h = edid_info->display.max_vertical_image_size;
+
+	/* If either one is 0, we have aspect ratio, not size */
+	if (w == 0 || h == 0)
+		return (0);
+
+	/*
+	 * some monitors encode the aspect ratio instead of the physical size.
+	 */
+	if ((w == 16 && h == 9) || (w == 16 && h == 10) ||
+	    (w == 4 && h == 3) || (w == 5 && h == 4))
+		return (0);
+
+	/*
+	 * translate cm to inch, note we scale by 100 here.
+	 */
+	w = w * 100 / 254;
+	h = h * 100 / 254;
+
+	/* Return w^2 + h^2 */
+	return (w * w + h * h);
+}
+
+/*
+ * calculate pixels per inch.
+ */
+static unsigned
+gfx_get_ppi(void)
+{
+	unsigned dp, di;
+
+	di = edid_diagonal_squared();
+	if (di == 0)
+		return (0);
+
+	dp = gfx_state.tg_fb.fb_width *
+	    gfx_state.tg_fb.fb_width +
+	    gfx_state.tg_fb.fb_height *
+	    gfx_state.tg_fb.fb_height;
+
+	return (isqrt(dp / di));
+}
+
+/*
+ * Calculate font size from density independent pixels (dp):
+ * ((16dp * ppi) / 160) * display_factor.
+ * Here we are using fixed constants: 1dp == 160 ppi and
+ * display_factor 2.
+ *
+ * We are rounding font size up and are searching for font which is
+ * not smaller than calculated size value.
+ */
+static vt_font_bitmap_data_t *
+gfx_get_font(void)
+{
+	unsigned ppi, size;
+	vt_font_bitmap_data_t *font = NULL;
+	struct fontlist *fl, *next;
+
+	/* Text mode is not supported here. */
+	if (gfx_state.tg_fb_type == FB_TEXT)
+		return (NULL);
+
+	ppi = gfx_get_ppi();
+	if (ppi == 0)
+		return (NULL);
+
+	/*
+	 * We will search for 16dp font.
+	 * We are using scale up by 10 for roundup.
+	 */
+	size = (16 * ppi * 10) / 160;
+	/* Apply display factor 2.  */
+	size = roundup(size * 2, 10) / 10;
+
+	STAILQ_FOREACH(fl, &fonts, font_next) {
+		next = STAILQ_NEXT(fl, font_next);
+
+		/*
+		 * If this is last font or, if next font is smaller,
+		 * we have our font. Make sure, it actually is loaded.
+		 */
+		if (next == NULL || next->font_data->vfbd_height < size) {
+			font = fl->font_data;
+			if (font->vfbd_font == NULL ||
+			    fl->font_flags == FONT_RELOAD) {
+				if (fl->font_load != NULL &&
+				    fl->font_name != NULL)
+					font = fl->font_load(fl->font_name);
+			}
+			break;
+		}
+	}
+
+	return (font);
+}
+
 static vt_font_bitmap_data_t *
 set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
 {
@@ -1887,6 +1994,9 @@ set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
 		}
 	}
 
+	if (font == NULL)
+		font = gfx_get_font();
+
 	if (font != NULL) {
 		*rows = height / font->vfbd_height;
 		*cols = width / font->vfbd_width;
diff --git a/stand/common/gfx_fb.h b/stand/common/gfx_fb.h
index 04076a2c6d38..ac63d7939cef 100644
--- a/stand/common/gfx_fb.h
+++ b/stand/common/gfx_fb.h
@@ -109,6 +109,8 @@ struct vesa_edid_info {
 	uint8_t checksum;
 } __packed;
 
+extern struct vesa_edid_info *edid_info;
+
 #define	STD_TIMINGS	8
 #define	DET_TIMINGS	4
 
diff --git a/stand/efi/libefi/efi_console.c b/stand/efi/libefi/efi_console.c
index 3cbd121c41da..0c40b362f276 100644
--- a/stand/efi/libefi/efi_console.c
+++ b/stand/efi/libefi/efi_console.c
@@ -952,13 +952,24 @@ cons_update_mode(bool use_gfx_mode)
 
 			/*
 			 * setup_font() can adjust terminal size.
-			 * Note, we do use UEFI terminal dimensions first,
-			 * this is because the font selection will attempt
-			 * to achieve at least this terminal dimension and
-			 * we do not end up with too small font.
+			 * We can see two kind of bad happening.
+			 * We either can get too small console font - requested
+			 * terminal size is large, display resolution is
+			 * large, and we get very small font.
+			 * Or, we can get too large font - requested
+			 * terminal size is small and this will cause large
+			 * font to be selected.
+			 * Now, the setup_font() is updated to consider
+			 * display density and this should give us mostly
+			 * acceptable font. However, the catch is, not all
+			 * display devices will give us display density.
+			 * Still, we do hope, external monitors do - this is
+			 * where the display size will matter the most.
+			 * And for laptop screens, we should still get good
+			 * results by requesting 80x25 terminal.
 			 */
-			gfx_state.tg_tp.tp_row = rows;
-			gfx_state.tg_tp.tp_col = cols;
+			gfx_state.tg_tp.tp_row = 25;
+			gfx_state.tg_tp.tp_col = 80;
 			setup_font(&gfx_state, fb_height, fb_width);
 			rows = gfx_state.tg_tp.tp_row;
 			cols = gfx_state.tg_tp.tp_col;
diff --git a/stand/efi/loader/framebuffer.c b/stand/efi/loader/framebuffer.c
index 509c41844dcb..adb9dfb62cee 100644
--- a/stand/efi/loader/framebuffer.c
+++ b/stand/efi/loader/framebuffer.c
@@ -38,6 +38,8 @@ __FBSDID("$FreeBSD$");
 #include <efilib.h>
 #include <efiuga.h>
 #include <efipciio.h>
+#include <Protocol/EdidActive.h>
+#include <Protocol/EdidDiscovered.h>
 #include <machine/metadata.h>
 
 #include "bootstrap.h"
@@ -47,6 +49,12 @@ static EFI_GUID conout_guid = EFI_CONSOLE_OUT_DEVICE_GUID;
 EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
 static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID;
 static 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;
+static EFI_HANDLE gop_handle;
+
+/* Cached EDID. */
+struct vesa_edid_info *edid_info = NULL;
 
 static EFI_GRAPHICS_OUTPUT *gop;
 static EFI_UGA_DRAW_PROTOCOL *uga;
@@ -467,10 +475,71 @@ efifb_from_uga(struct efi_fb *efifb)
 	return (0);
 }
 
+/*
+ * Fetch EDID info. Caller must free the buffer.
+ */
+static struct vesa_edid_info *
+efifb_gop_get_edid(EFI_HANDLE h)
+{
+	const uint8_t magic[] = EDID_MAGIC;
+	EFI_EDID_ACTIVE_PROTOCOL *edid;
+	struct vesa_edid_info *edid_infop;
+	EFI_GUID *guid;
+	EFI_STATUS status;
+	size_t size;
+
+	guid = &active_edid_guid;
+	status = BS->OpenProtocol(h, guid, (void **)&edid, IH, NULL,
+	    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+	if (status != EFI_SUCCESS ||
+	    edid->SizeOfEdid == 0) {
+		guid = &discovered_edid_guid;
+		status = BS->OpenProtocol(h, guid, (void **)&edid, IH, NULL,
+		    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+		if (status != EFI_SUCCESS ||
+		    edid->SizeOfEdid == 0)
+			return (NULL);
+	}
+
+	size = MAX(sizeof(*edid_infop), edid->SizeOfEdid);
+
+	edid_infop = calloc(1, size);
+	if (edid_infop == NULL)
+		return (NULL);
+
+	memcpy(edid_infop, edid->Edid, edid->SizeOfEdid);
+
+	/* Validate EDID */
+	if (memcmp(edid_infop, magic, sizeof (magic)) != 0)
+		goto error;
+
+	if (edid_infop->header.version != 1)
+		goto error;
+
+	return (edid_infop);
+error:
+	free(edid_infop);
+	return (NULL);
+}
+
+static bool
+efifb_get_edid(edid_res_list_t *res)
+{
+	bool rv = false;
+
+	if (edid_info == NULL)
+		edid_info = efifb_gop_get_edid(gop_handle);
+
+	if (edid_info != NULL)
+		rv = gfx_get_edid_resolution(edid_info, res);
+
+	return (rv);
+}
+
 int
 efi_find_framebuffer(teken_gfx_t *gfx_state)
 {
-	EFI_HANDLE h, *hlist;
+	EFI_HANDLE *hlist;
 	UINTN nhandles, i, hsize;
 	struct efi_fb efifb;
 	EFI_STATUS status;
@@ -498,23 +567,25 @@ efi_find_framebuffer(teken_gfx_t *gfx_state)
 	/*
 	 * Search for ConOut protocol, if not found, use first handle.
 	 */
-	h = *hlist;
+	gop_handle = *hlist;
 	for (i = 0; i < nhandles; i++) {
 		void *dummy = NULL;
 
 		status = OpenProtocolByHandle(hlist[i], &conout_guid, &dummy);
 		if (status == EFI_SUCCESS) {
-			h = hlist[i];
+			gop_handle = hlist[i];
 			break;
 		}
 	}
 
-	status = OpenProtocolByHandle(h, &gop_guid, (void **)&gop);
+	status = OpenProtocolByHandle(gop_handle, &gop_guid, (void **)&gop);
 	free(hlist);
 
 	if (status == EFI_SUCCESS) {
 		gfx_state->tg_fb_type = FB_GOP;
 		gfx_state->tg_private = gop;
+		if (edid_info == NULL)
+			edid_info = efifb_gop_get_edid(gop_handle);
 	} else {
 		status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
 		if (status == EFI_SUCCESS) {
@@ -767,9 +838,25 @@ command_gop(int argc, char *argv[])
 	} else if (strcmp(argv[1], "off") == 0) {
 		(void) cons_update_mode(false);
 	} else if (strcmp(argv[1], "get") == 0) {
+		edid_res_list_t res;
+
 		if (argc != 2)
 			goto usage;
+		TAILQ_INIT(&res);
 		efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
+		if (efifb_get_edid(&res)) {
+			struct resolution *rp;
+
+			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");
+		}
 		print_efifb(gop->Mode->Mode, &efifb, 1);
 		printf("\n");
 	} else if (!strcmp(argv[1], "list")) {
@@ -778,6 +865,7 @@ command_gop(int argc, char *argv[])
 
 		if (argc != 2)
 			goto usage;
+
 		pager_open();
 		for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
 			status = gop->QueryMode(gop, mode, &infosz, &info);
diff --git a/stand/i386/libi386/vbe.c b/stand/i386/libi386/vbe.c
index 0e9f6929ac05..ef4daffa8380 100644
--- a/stand/i386/libi386/vbe.c
+++ b/stand/i386/libi386/vbe.c
@@ -51,6 +51,7 @@ static struct modeinfoblock *vbe_mode;
 
 static uint16_t *vbe_mode_list;
 static size_t vbe_mode_list_size;
+struct vesa_edid_info *edid_info = NULL;
 
 /* The default VGA color palette format is 6 bits per primary color. */
 int palette_format = 6;
@@ -836,34 +837,40 @@ vbe_dump_mode(int modenum, struct modeinfoblock *mi)
 static bool
 vbe_get_edid(edid_res_list_t *res)
 {
-	struct vesa_edid_info *edid_info;
+	struct vesa_edid_info *edidp;
 	const uint8_t magic[] = EDID_MAGIC;
 	int ddc_caps;
 	bool ret = false;
 
+	if (edid_info != NULL)
+		return (gfx_get_edid_resolution(edid_info, res));
+
 	ddc_caps = biosvbe_ddc_caps();
 	if (ddc_caps == 0) {
 		return (ret);
 	}
 
-	edid_info = bio_alloc(sizeof (*edid_info));
-	if (edid_info == NULL)
+	edidp = bio_alloc(sizeof(*edidp));
+	if (edidp == NULL)
 		return (ret);
-	memset(edid_info, 0, sizeof (*edid_info));
+	memset(edidp, 0, sizeof(*edidp));
 
-	if (VBE_ERROR(biosvbe_ddc_read_edid(0, edid_info)))
+	if (VBE_ERROR(biosvbe_ddc_read_edid(0, edidp)))
 		goto done;
 
-	if (memcmp(edid_info, magic, sizeof (magic)) != 0)
+	if (memcmp(edidp, magic, sizeof(magic)) != 0)
 		goto done;
 
 	/* Unknown EDID version. */
-	if (edid_info->header.version != 1)
+	if (edidp->header.version != 1)
 		goto done;
 
-	ret = gfx_get_edid_resolution(edid_info, res);
+	ret = gfx_get_edid_resolution(edidp, res);
+	edid_info = malloc(sizeof(*edid_info));
+	if (edid_info != NULL)
+		memcpy(edid_info, edidp, sizeof (*edid_info));
 done:
-	bio_free(edid_info, sizeof (*edid_info));
+	bio_free(edidp, sizeof(*edidp));
 	return (ret);
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202103042100.124L0NJ2017649>