Index: share/man/man4/vt.4 =================================================================== --- share/man/man4/vt.4 +++ share/man/man4/vt.4 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 17, 2022 +.Dd July 31, 2022 .Dt "VT" 4 .Os .Sh NAME @@ -356,6 +356,24 @@ .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 , Index: stand/defaults/loader.conf.5 =================================================================== --- stand/defaults/loader.conf.5 +++ stand/defaults/loader.conf.5 @@ -308,6 +308,10 @@ .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. +This option will also rotate the screen of the EFI boot loader. .It Va efi_max_resolution .It Va vbe_max_resolution Specify the maximum desired resolution for the EFI or VBE framebuffer console. Index: sys/dev/vt/hw/fb/vt_fb.c =================================================================== --- sys/dev/vt/hw/fb/vt_fb.c +++ sys/dev/vt/hw/fb/vt_fb.c @@ -162,11 +162,22 @@ { 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 @@ 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 @@ /* 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,44 @@ { struct fb_info *info; u_int margin; - int bg, err; + int angle = 0, bg, err; 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; + + /* Default angle to rotate console clockwise, in 90 degree increments. */ + if (!(info->fb_flags & FB_FLAG_ROTATE_NODEFAULT)) { + 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; Index: sys/sys/fbio.h =================================================================== --- sys/sys/fbio.h +++ sys/sys/fbio.h @@ -143,9 +143,15 @@ 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 */