diff --git a/share/man/man4/vt.4 b/share/man/man4/vt.4 index 3d930ab4f710..6759164fe54a 100644 --- a/share/man/man4/vt.4 +++ b/share/man/man4/vt.4 @@ -358,6 +358,24 @@ To set black and white colors of console palette .Pp .Dl kern.vt.color.0.rgb="10,10,10" .Dl kern.vt.color.15.rgb="#f0f0f0" +.Pp +The mode option +.Sy rotate +is also supported, with values 0, 90, 180, and 270 representing the degrees +counterclockwise to rotate the display. Follow an optional connector name +and mode with a comma and the mode option. For example, +to set the default rotation for all output connectors 90 degrees clockwise: +.Pp +.Dl kern.vt.fb.default_mode=",rotate=270" +.Pp +This option is only supported by +.Cm vt_fb +when it is paired with a KMS video driver, and in most cases the +.Pp +.Dl screen.rotate=90 +.Pp +.Xr loader.conf 5 +setting, which will work with and without KMS, is more appropriate. .Sh SEE ALSO .Xr kbdcontrol 1 , .Xr login 1 , diff --git a/stand/defaults/loader.conf.5 b/stand/defaults/loader.conf.5 index 23a65935c95a..47c21f99a917 100644 --- a/stand/defaults/loader.conf.5 +++ b/stand/defaults/loader.conf.5 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" .\" $FreeBSD$ -.Dd July 31, 2021 +.Dd August 3, 2022 .Dt LOADER.CONF 5 .Os .Sh NAME @@ -308,6 +308,16 @@ If EDID is not available, the default resolution is 800x600 (if available). .Va screen.width , .Va screen.depth are set by loader when loader is using framebuffer mode to draw the screen. +.It Va screen.rotate +Sets the rotation of the framebuffer console to 0, 90, 180, or 270 degrees clockwise. +If KMS is present, the rotation may be performed in hardware, +and an additional value, "AUTO", is supported which will often detect the +correct rotation. +.Pp +This option will also rotate the screen of the EFI boot loader; because the rotation +setting for the EFI loader is stored in the EFI itself during shutdown based upon the +value present in the current kernel environment, +it will take a second reboot to pass a change to the loader. .It Va efi_max_resolution .It Va vbe_max_resolution Specify the maximum desired resolution for the EFI or VBE framebuffer console. diff --git a/sys/dev/vt/hw/fb/vt_fb.c b/sys/dev/vt/hw/fb/vt_fb.c index 2f6c4c3939d5..33e76f903b8c 100644 --- a/sys/dev/vt/hw/fb/vt_fb.c +++ b/sys/dev/vt/hw/fb/vt_fb.c @@ -162,11 +162,22 @@ vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) { struct fb_info *info; uint32_t c; - u_int o; + u_int o, row, col; info = vd->vd_softc; c = info->fb_cmap[color]; - o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info); + + /* Rotations */ + if (info->fb_flags & FB_FLAG_ROTATE_90) + row = x, col = y; + else + row = y, col = x; + if (info->fb_flags & FB_FLAG_ROTATE_180) + row = (info->fb_height - 1) - row; + if (!(info->fb_flags & FB_FLAG_ROTATE_90) != + !(info->fb_flags & FB_FLAG_ROTATE_180)) + col = (info->fb_width - 1) - col; + o = info->fb_stride * row + col * FBTYPE_GET_BYTESPP(info); if (info->fb_flags & FB_FLAG_NOWRITE) return; @@ -266,7 +277,7 @@ vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) { struct fb_info *info; - uint32_t fgc, bgc, cc, o; + uint32_t fgc, bgc, cc, o, row, col; int bpp, bpl, xi, yi; int bit, byte; @@ -299,7 +310,18 @@ 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; + + /* Rotations */ + if (info->fb_flags & FB_FLAG_ROTATE_90) + row = x + xi, col = y + yi; + else + row = y + yi, col = x + xi; + if (info->fb_flags & FB_FLAG_ROTATE_180) + row = (info->fb_height - 1) - row; + if (!(info->fb_flags & FB_FLAG_ROTATE_90) != + !(info->fb_flags & FB_FLAG_ROTATE_180)) + col = (info->fb_width - 1) - col; + o = row * info->fb_stride + col * bpp; o += vd->vd_transpose; cc = pattern[byte] & bit ? fgc : bgc; @@ -459,15 +481,64 @@ vt_fb_init(struct vt_device *vd) { struct fb_info *info; u_int margin; - int bg, err; + int angle = 0, bg, err; term_color_t c; + char *rotation, strbuf[4]; 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; + + /* + * If we have been passed a rotation, and screen.rotate="AUTO", change + * screen.rotate to the autodetected rotation for use during the next + * reboot. + */ + if (info->fb_flags & FB_FLAG_ROTATE_NODEFAULT) { + if ((rotation = kern_getenv("screen.rotate")) != NULL && + strncasecmp(rotation, "auto", 5) == 0) { + if (info->fb_flags & FB_FLAG_ROTATE_90) + angle += 90; + if (info->fb_flags & FB_FLAG_ROTATE_180) + angle += 180; + snprintf(strbuf, sizeof(strbuf), "%d", angle); + kern_setenv("screen.rotate", strbuf); + } + freeenv(rotation); + } + + /* Default angle to rotate console clockwise, in 90 degree increments. */ + if (!(info->fb_flags & FB_FLAG_ROTATE_NODEFAULT)) { + if ((TUNABLE_INT_FETCH("screen.autorotate", &angle) == 0)) + TUNABLE_INT_FETCH("screen.rotate", &angle); + info->fb_flags &= ~FB_FLAG_ROTATE_MASK; + switch (angle) { + case 270: + info->fb_flags |= FB_FLAG_ROTATE_270; + break; + case 180: + info->fb_flags |= FB_FLAG_ROTATE_180; + break; + case 90: + info->fb_flags |= FB_FLAG_ROTATE_90; + break; + default: + info->fb_flags |= FB_FLAG_ROTATE_0; + break; + } + } + + if (info->fb_flags & FB_FLAG_ROTATE_90) { /* +/-90 degrees rotation */ + 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; + } else { + vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height); + vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width); + margin = (info->fb_height - vd->vd_height) >> 1; + vd->vd_transpose = margin * info->fb_stride; + margin = (info->fb_width - vd->vd_width) >> 1; + } vd->vd_transpose += margin * (info->fb_bpp / NBBY); vd->vd_video_dev = info->fb_video_dev; diff --git a/sys/sys/fbio.h b/sys/sys/fbio.h index 931ef689fc1f..d573811044bc 100644 --- a/sys/sys/fbio.h +++ b/sys/sys/fbio.h @@ -143,9 +143,15 @@ struct fb_info { void *fb_priv; /* First argument for read/write. */ const char *fb_name; uint32_t fb_flags; -#define FB_FLAG_NOMMAP 1 /* mmap unsupported. */ -#define FB_FLAG_NOWRITE 2 /* disable writes for the time being */ -#define FB_FLAG_MEMATTR 4 /* override memattr for mmap */ +#define FB_FLAG_NOMMAP 0x01 /* mmap unsupported. */ +#define FB_FLAG_NOWRITE 0x02 /* disable writes for the time being */ +#define FB_FLAG_MEMATTR 0x04 /* override memattr for mmap */ +#define FB_FLAG_ROTATE_0 0x00 /* rotate fb 0 degrees clockwise */ +#define FB_FLAG_ROTATE_90 0x08 /* rotate fb 90 degrees clockwise */ +#define FB_FLAG_ROTATE_180 0x10 /* rotate fb 180 degrees clockwise */ +#define FB_FLAG_ROTATE_270 (FB_FLAG_ROTATE_90 | FB_FLAG_ROTATE_180) +#define FB_FLAG_ROTATE_MASK 0x18 +#define FB_FLAG_ROTATE_NODEFAULT 0x20 /* do not load console defaults */ vm_memattr_t fb_memattr; int fb_stride; int fb_bpp; /* bits per pixel */