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). MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D28849
This commit is contained in:
parent
c01da939b0
commit
becaac3972
@ -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 *
|
static vt_font_bitmap_data_t *
|
||||||
set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
|
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) {
|
if (font != NULL) {
|
||||||
*rows = height / font->vfbd_height;
|
*rows = height / font->vfbd_height;
|
||||||
*cols = width / font->vfbd_width;
|
*cols = width / font->vfbd_width;
|
||||||
|
@ -109,6 +109,8 @@ struct vesa_edid_info {
|
|||||||
uint8_t checksum;
|
uint8_t checksum;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
extern struct vesa_edid_info *edid_info;
|
||||||
|
|
||||||
#define STD_TIMINGS 8
|
#define STD_TIMINGS 8
|
||||||
#define DET_TIMINGS 4
|
#define DET_TIMINGS 4
|
||||||
|
|
||||||
|
@ -952,13 +952,24 @@ cons_update_mode(bool use_gfx_mode)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* setup_font() can adjust terminal size.
|
* setup_font() can adjust terminal size.
|
||||||
* Note, we do use UEFI terminal dimensions first,
|
* We can see two kind of bad happening.
|
||||||
* this is because the font selection will attempt
|
* We either can get too small console font - requested
|
||||||
* to achieve at least this terminal dimension and
|
* terminal size is large, display resolution is
|
||||||
* we do not end up with too small font.
|
* 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_row = 25;
|
||||||
gfx_state.tg_tp.tp_col = cols;
|
gfx_state.tg_tp.tp_col = 80;
|
||||||
setup_font(&gfx_state, fb_height, fb_width);
|
setup_font(&gfx_state, fb_height, fb_width);
|
||||||
rows = gfx_state.tg_tp.tp_row;
|
rows = gfx_state.tg_tp.tp_row;
|
||||||
cols = gfx_state.tg_tp.tp_col;
|
cols = gfx_state.tg_tp.tp_col;
|
||||||
|
@ -38,6 +38,8 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <efilib.h>
|
#include <efilib.h>
|
||||||
#include <efiuga.h>
|
#include <efiuga.h>
|
||||||
#include <efipciio.h>
|
#include <efipciio.h>
|
||||||
|
#include <Protocol/EdidActive.h>
|
||||||
|
#include <Protocol/EdidDiscovered.h>
|
||||||
#include <machine/metadata.h>
|
#include <machine/metadata.h>
|
||||||
|
|
||||||
#include "bootstrap.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;
|
EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
||||||
static EFI_GUID pciio_guid = EFI_PCI_IO_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 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_GRAPHICS_OUTPUT *gop;
|
||||||
static EFI_UGA_DRAW_PROTOCOL *uga;
|
static EFI_UGA_DRAW_PROTOCOL *uga;
|
||||||
@ -467,10 +475,71 @@ efifb_from_uga(struct efi_fb *efifb)
|
|||||||
return (0);
|
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
|
int
|
||||||
efi_find_framebuffer(teken_gfx_t *gfx_state)
|
efi_find_framebuffer(teken_gfx_t *gfx_state)
|
||||||
{
|
{
|
||||||
EFI_HANDLE h, *hlist;
|
EFI_HANDLE *hlist;
|
||||||
UINTN nhandles, i, hsize;
|
UINTN nhandles, i, hsize;
|
||||||
struct efi_fb efifb;
|
struct efi_fb efifb;
|
||||||
EFI_STATUS status;
|
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.
|
* Search for ConOut protocol, if not found, use first handle.
|
||||||
*/
|
*/
|
||||||
h = *hlist;
|
gop_handle = *hlist;
|
||||||
for (i = 0; i < nhandles; i++) {
|
for (i = 0; i < nhandles; i++) {
|
||||||
void *dummy = NULL;
|
void *dummy = NULL;
|
||||||
|
|
||||||
status = OpenProtocolByHandle(hlist[i], &conout_guid, &dummy);
|
status = OpenProtocolByHandle(hlist[i], &conout_guid, &dummy);
|
||||||
if (status == EFI_SUCCESS) {
|
if (status == EFI_SUCCESS) {
|
||||||
h = hlist[i];
|
gop_handle = hlist[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status = OpenProtocolByHandle(h, &gop_guid, (void **)&gop);
|
status = OpenProtocolByHandle(gop_handle, &gop_guid, (void **)&gop);
|
||||||
free(hlist);
|
free(hlist);
|
||||||
|
|
||||||
if (status == EFI_SUCCESS) {
|
if (status == EFI_SUCCESS) {
|
||||||
gfx_state->tg_fb_type = FB_GOP;
|
gfx_state->tg_fb_type = FB_GOP;
|
||||||
gfx_state->tg_private = gop;
|
gfx_state->tg_private = gop;
|
||||||
|
if (edid_info == NULL)
|
||||||
|
edid_info = efifb_gop_get_edid(gop_handle);
|
||||||
} else {
|
} else {
|
||||||
status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
|
status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
|
||||||
if (status == EFI_SUCCESS) {
|
if (status == EFI_SUCCESS) {
|
||||||
@ -767,9 +838,25 @@ command_gop(int argc, char *argv[])
|
|||||||
} else if (strcmp(argv[1], "off") == 0) {
|
} else if (strcmp(argv[1], "off") == 0) {
|
||||||
(void) cons_update_mode(false);
|
(void) cons_update_mode(false);
|
||||||
} else if (strcmp(argv[1], "get") == 0) {
|
} else if (strcmp(argv[1], "get") == 0) {
|
||||||
|
edid_res_list_t res;
|
||||||
|
|
||||||
if (argc != 2)
|
if (argc != 2)
|
||||||
goto usage;
|
goto usage;
|
||||||
|
TAILQ_INIT(&res);
|
||||||
efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
|
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);
|
print_efifb(gop->Mode->Mode, &efifb, 1);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
} else if (!strcmp(argv[1], "list")) {
|
} else if (!strcmp(argv[1], "list")) {
|
||||||
@ -778,6 +865,7 @@ command_gop(int argc, char *argv[])
|
|||||||
|
|
||||||
if (argc != 2)
|
if (argc != 2)
|
||||||
goto usage;
|
goto usage;
|
||||||
|
|
||||||
pager_open();
|
pager_open();
|
||||||
for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
|
for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
|
||||||
status = gop->QueryMode(gop, mode, &infosz, &info);
|
status = gop->QueryMode(gop, mode, &infosz, &info);
|
||||||
|
@ -51,6 +51,7 @@ static struct modeinfoblock *vbe_mode;
|
|||||||
|
|
||||||
static uint16_t *vbe_mode_list;
|
static uint16_t *vbe_mode_list;
|
||||||
static size_t vbe_mode_list_size;
|
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. */
|
/* The default VGA color palette format is 6 bits per primary color. */
|
||||||
int palette_format = 6;
|
int palette_format = 6;
|
||||||
@ -836,34 +837,40 @@ vbe_dump_mode(int modenum, struct modeinfoblock *mi)
|
|||||||
static bool
|
static bool
|
||||||
vbe_get_edid(edid_res_list_t *res)
|
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;
|
const uint8_t magic[] = EDID_MAGIC;
|
||||||
int ddc_caps;
|
int ddc_caps;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
|
if (edid_info != NULL)
|
||||||
|
return (gfx_get_edid_resolution(edid_info, res));
|
||||||
|
|
||||||
ddc_caps = biosvbe_ddc_caps();
|
ddc_caps = biosvbe_ddc_caps();
|
||||||
if (ddc_caps == 0) {
|
if (ddc_caps == 0) {
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
edid_info = bio_alloc(sizeof (*edid_info));
|
edidp = bio_alloc(sizeof(*edidp));
|
||||||
if (edid_info == NULL)
|
if (edidp == NULL)
|
||||||
return (ret);
|
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;
|
goto done;
|
||||||
|
|
||||||
if (memcmp(edid_info, magic, sizeof (magic)) != 0)
|
if (memcmp(edidp, magic, sizeof(magic)) != 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Unknown EDID version. */
|
/* Unknown EDID version. */
|
||||||
if (edid_info->header.version != 1)
|
if (edidp->header.version != 1)
|
||||||
goto done;
|
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:
|
done:
|
||||||
bio_free(edid_info, sizeof (*edid_info));
|
bio_free(edidp, sizeof(*edidp));
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user