Rotating (efi) framebuffer console

From: Stefan Grundmann <sg2342_at_googlemail.com>
Date: Sat, 05 Feb 2022 20:50:41 UTC
Hi,                                                                                       
                                                                                          
the screen of my brand new GPD Pocket 3 (tiger lake) laptop is in portrait mode          
and reports a resolution of 1200x1920. I have another older device with the same          
issue (a GPD Pocket 1).                                                                   
In 2019 johalun@FreeBSD.org wrote on the freebsd-current about an Lenovo Ideapad
with a portrait mode screen and raised the question if it would be good to support
rotation of the vt_fb console.                         
                                                                                          
Since the new GPD Pocket 3 is a nice device and rather well supported in FreeBSD,         
i thought "how hard can it be" (OpenBSD has fb console rotation and Linux has fbcon=rotate..)


The patch (against stable/13, at the end of this mail) works for me(tm):
Whenever the width of a frambuffer device is smaller than it's height; a portrait mode
screen is assumed and the screen is rotated by 90 degrees clockwise.

I write this here for two reasons:

1. give others with similar hardware a chance to avoid the neck-craning issue

   and

2. offer to work on something that can be reviewed and merged e.g.
   - implement the rest of the transformations (180 degrees, 270 degrees)
   - boot-time variable to select behavior
   - vt(4) man page update

For 2. i would like to know if vt_fb.c is even the right place to do this.
The framebuffer code in the loader could also get this feature.


best regards

Stefan Grundmann



diff --git a/sys/dev/vt/hw/fb/vt_fb.c b/sys/dev/vt/hw/fb/vt_fb.c
index c535d1b753c..19ab5999d89 100644
--- a/sys/dev/vt/hw/fb/vt_fb.c
+++ b/sys/dev/vt/hw/fb/vt_fb.c
@@ -45,6 +45,8 @@ __FBSDID("$FreeBSD$");
 #include <vm/vm.h>
 #include <vm/pmap.h>
 
+#define FB_FLAG_ROTATE 2147483648
+
 static struct vt_driver vt_fb_driver = {
 	.vd_name = "fb",
 	.vd_init = vt_fb_init,
@@ -167,7 +169,13 @@ vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
 
 	info = vd->vd_softc;
 	c = info->fb_cmap[color];
-	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
+
+	if (info->fb_flags & FB_FLAG_ROTATE) {
+		o = info->fb_stride * x +
+		    (info->fb_width - (y + 1)) * FBTYPE_GET_BYTESPP(info);
+	} else {
+		o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
+	}
 
 	if (info->fb_flags & FB_FLAG_NOWRITE)
 		return;
@@ -300,7 +308,13 @@ vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw,
 			/* Skip pixel write, if mask bit not set. */
 			if (mask != NULL && (mask[byte] & bit) == 0)
 				continue;
-			o = (y + yi) * info->fb_stride + (x + xi) * bpp;
+			if (info->fb_flags & FB_FLAG_ROTATE) {
+				o = (x + xi) * info->fb_stride +
+				    (info->fb_width - (y + yi + 1)) * bpp;
+			} else {
+				o = (y + yi) * info->fb_stride + (x + xi) * bpp;
+			}
+
 			o += vd->vd_transpose;
 			cc = pattern[byte] & bit ? fgc : bgc;
 
@@ -464,12 +478,22 @@ vt_fb_init(struct vt_device *vd)
 	term_color_t c;
 
 	info = vd->vd_softc;
-	vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height);
-	margin = (info->fb_height - vd->vd_height) >> 1;
-	vd->vd_transpose = margin * info->fb_stride;
-	vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width);
-	margin = (info->fb_width - vd->vd_width) >> 1;
-	vd->vd_transpose += margin * (info->fb_bpp / NBBY);
+	if (info->fb_height > info->fb_width) { /*assume 90 degrees clockwise rotation*/
+		info->fb_flags |= FB_FLAG_ROTATE;
+		vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_width);
+		vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_height);
+		margin = (info->fb_height - vd->vd_width) >> 1;
+		vd->vd_transpose = margin * info->fb_stride;
+		margin = (info->fb_width - vd->vd_height) >> 1;
+		vd->vd_transpose += margin * (info->fb_bpp / NBBY);
+	} else {
+		vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height);
+		margin = (info->fb_height - vd->vd_height) >> 1;
+		vd->vd_transpose = margin * info->fb_stride;
+		vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width);
+		margin = (info->fb_width - vd->vd_width) >> 1;
+		vd->vd_transpose += margin * (info->fb_bpp / NBBY);
+	}
 	vd->vd_video_dev = info->fb_video_dev;
 
 	if (info->fb_size == 0)