Linux Framebuffer Layer development
 help / color / mirror / Atom feed
From: Thomas Zimmermann <tzimmermann@suse.de>
To: gregkh@linuxfoundation.org, jirislaby@kernel.org, deller@gmx.de,
	James.Bottomley@HansenPartnership.com, simona@ffwll.ch
Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org,
	linux-fbdev@vger.kernel.org, linux-parisc@vger.kernel.org,
	sashiko-reviews@lists.linux.dev,
	Thomas Zimmermann <tzimmermann@suse.de>
Subject: [PATCH 2/2] vt: Distinguish between blanking and VESA blank mode in con_blank
Date: Mon, 12 Oct 2026 11:27:39 +0200	[thread overview]
Message-ID: <20261012094114.38549-3-tzimmermann@suse.de> (raw)
In-Reply-To: <20261012094114.38549-1-tzimmermann@suse.de>

The con_blank callback in struct consw encodes blanking and display
blank mode in a single parameter named blank. Its semantics equal
the FB_BLANK constants from the fbdev subsystems. Yet it is of type
enum vesa_blank_mode, which describes VESA DPMS levels of the attached
monitor. Although related, this is something else.

For this reason, calls to con_blank fill the blank parameter with
values that appear non-sensical at first. For example, to blank the
console, but keep the monitor on, vt passes VESA_VSYNC_SUSPEND (eq '1').
This is because consoles interpret '1' as pixels-off-but-monitor-on.
The constant VESA_VSYNC_SUSPEND actually means monitor-vsync-off. And
the argument used for monitor-vsync-off is VESA_VSYNC_SUSPEND+1.

To fix this by splitting blank into two parameters, blank and blank_mode.
The new blank is a boolean to control the display blanking. If cleared,
the console creates display output, if set the console does not. The
new parameter blank_mode specifies the VESA blank mode for the monitor
and equals VESA DPMS levels. If blank is set, blank_mode gives a hint
to the console, on how to set up the signal to the monitor.

The actual interpretation is up to the console. Newportcon and sticon
only handle blank or not-blank. Fbcon and vgacon implement blank plus
the VESA DPMS handling. The implementation of both consoles is again
somewhat confusing because types and values do not match. Clean up
both with the new blank/blank_mode logic.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/tty/vt/vt.c                 | 11 ++++----
 drivers/video/console/dummycon.c    |  4 +--
 drivers/video/console/newport_con.c |  4 +--
 drivers/video/console/sticon.c      |  4 +--
 drivers/video/console/vgacon.c      | 44 +++++++++++++++--------------
 drivers/video/fbdev/core/fbcon.c    | 19 +++++++------
 include/linux/console.h             |  9 ++++--
 7 files changed, 51 insertions(+), 44 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 9e7b4419b422..f762c965351d 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -4671,7 +4671,7 @@ void do_blank_screen(bool entering_gfx)
 	if (console_blanked) {
 		if (blank_state == blank_vesa_wait) {
 			blank_state = blank_off;
-			vc->vc_sw->con_blank(vc, vesa_blank_mode + 1, false);
+			vc->vc_sw->con_blank(vc, true, vesa_blank_mode, false);
 		}
 		return;
 	}
@@ -4680,7 +4680,7 @@ void do_blank_screen(bool entering_gfx)
 	if (entering_gfx) {
 		hide_cursor(vc);
 		save_screen(vc);
-		vc->vc_sw->con_blank(vc, VESA_VSYNC_SUSPEND, true);
+		vc->vc_sw->con_blank(vc, true, VESA_NO_BLANKING, true);
 		console_blanked = fg_console + 1;
 		blank_state = blank_off;
 		set_origin(vc);
@@ -4701,8 +4701,9 @@ void do_blank_screen(bool entering_gfx)
 
 	save_screen(vc);
 	/* In case we need to reset origin, blanking hook returns 1 */
-	i = vc->vc_sw->con_blank(vc, vesa_off_interval ? VESA_VSYNC_SUSPEND :
-				 (vesa_blank_mode + 1), false);
+	i = vc->vc_sw->con_blank(vc, true,
+				 vesa_off_interval ? VESA_NO_BLANKING : vesa_blank_mode,
+				 false);
 	console_blanked = fg_console + 1;
 	if (i)
 		set_origin(vc);
@@ -4753,7 +4754,7 @@ void do_unblank_screen(bool leaving_gfx)
 	}
 
 	console_blanked = 0;
-	if (vc->vc_sw->con_blank(vc, VESA_NO_BLANKING, leaving_gfx))
+	if (vc->vc_sw->con_blank(vc, false, VESA_NO_BLANKING, leaving_gfx))
 		/* Low-level driver cannot restore -> do it ourselves */
 		update_screen(vc);
 	if (console_blank_hook)
diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c
index 7d02470f19b9..50aaccffcccf 100644
--- a/drivers/video/console/dummycon.c
+++ b/drivers/video/console/dummycon.c
@@ -79,7 +79,7 @@ static void dummycon_putcs(struct vc_data *vc, const u16 *s, unsigned int count,
 	raw_notifier_call_chain(&dummycon_output_nh, 0, NULL);
 }
 
-static bool dummycon_blank(struct vc_data *vc, enum vesa_blank_mode blank,
+static bool dummycon_blank(struct vc_data *vc, bool blank, enum vesa_blank_mode blank_mode,
 			   bool mode_switch)
 {
 	/* Redraw, so that we get putc(s) for output done while blanked */
@@ -99,7 +99,7 @@ static void dummycon_putc(struct vc_data *vc, u16 c, unsigned int y,
 			  unsigned int x) { }
 static void dummycon_putcs(struct vc_data *vc, const u16 *s, unsigned int count,
 			   unsigned int ypos, unsigned int xpos) { }
-static bool dummycon_blank(struct vc_data *vc, enum vesa_blank_mode blank,
+static bool dummycon_blank(struct vc_data *vc, bool blank, enum vesa_blank_mode blank_mode,
 			   bool mode_switch)
 {
 	return false;
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index ecbbe2e4e2a6..90e6d70f94bb 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -476,12 +476,12 @@ static bool newport_switch(struct vc_data *vc)
 	return true;
 }
 
-static bool newport_blank(struct vc_data *c, enum vesa_blank_mode blank,
+static bool newport_blank(struct vc_data *c, bool blank, enum vesa_blank_mode blank_mode,
 			  bool mode_switch)
 {
 	unsigned short treg;
 
-	if (blank == VESA_NO_BLANKING) {
+	if (!blank) {
 		/* unblank console */
 		treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
 		newport_vc2_set(npregs, VC2_IREG_CONTROL,
diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c
index 6f58e6a9e76a..00e5f86dd35a 100644
--- a/drivers/video/console/sticon.c
+++ b/drivers/video/console/sticon.c
@@ -299,10 +299,10 @@ static bool sticon_switch(struct vc_data *conp)
     return true;	/* needs refreshing */
 }
 
-static bool sticon_blank(struct vc_data *c, enum vesa_blank_mode blank,
+static bool sticon_blank(struct vc_data *c, bool blank, enum vesa_blank_mode blank_mode,
 			 bool mode_switch)
 {
-    if (blank == VESA_NO_BLANKING) {
+    if (!blank) {
 	if (mode_switch)
 	    vga_is_gfx = 0;
 	return true;
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index 536e7fe4d142..a3a45b6956df 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -797,11 +797,31 @@ static void vga_pal_blank(struct vgastate *state)
 	}
 }
 
-static bool vgacon_blank(struct vc_data *c, enum vesa_blank_mode blank,
+static bool vgacon_blank(struct vc_data *c, bool blank, enum vesa_blank_mode blank_mode,
 			 bool mode_switch)
 {
-	switch (blank) {
-	case VESA_NO_BLANKING:		/* Unblank */
+	if (blank) {
+		switch (blank_mode) {
+		case VESA_NO_BLANKING:	/* Normal blanking */
+			if (!mode_switch && vga_video_type == VIDEO_TYPE_VGAC) {
+				vga_pal_blank(&vgastate);
+				vga_palette_blanked = true;
+				return 0;
+			}
+			vgacon_set_origin(c);
+			scr_memsetw((void *)vga_vram_base, BLANK,
+				    c->vc_screenbuf_size);
+			if (mode_switch)
+				vga_is_gfx = true;
+			return 1;
+		default:		/* VESA blanking */
+			if (vga_video_type == VIDEO_TYPE_VGAC) {
+				vga_vesa_blank(&vgastate, blank_mode);
+				vga_vesa_blanked = blank_mode;
+			}
+			return 0;
+		}
+	} else { /* Unblank */
 		if (vga_vesa_blanked) {
 			vga_vesa_unblank(&vgastate);
 			vga_vesa_blanked = VESA_NO_BLANKING;
@@ -814,24 +834,6 @@ static bool vgacon_blank(struct vc_data *c, enum vesa_blank_mode blank,
 		vga_is_gfx = false;
 		/* Tell console.c that it has to restore the screen itself */
 		return 1;
-	case VESA_VSYNC_SUSPEND:	/* Normal blanking */
-		if (!mode_switch && vga_video_type == VIDEO_TYPE_VGAC) {
-			vga_pal_blank(&vgastate);
-			vga_palette_blanked = true;
-			return 0;
-		}
-		vgacon_set_origin(c);
-		scr_memsetw((void *) vga_vram_base, BLANK,
-			    c->vc_screenbuf_size);
-		if (mode_switch)
-			vga_is_gfx = true;
-		return 1;
-	default:		/* VESA blanking */
-		if (vga_video_type == VIDEO_TYPE_VGAC) {
-			vga_vesa_blank(&vgastate, blank - 1);
-			vga_vesa_blanked = blank;
-		}
-		return 0;
 	}
 }
 
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 274f6bf6e165..7f75b4f5da74 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -2287,18 +2287,19 @@ static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
 	}
 }
 
-static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank,
+static bool fbcon_blank(struct vc_data *vc, bool blank, enum vesa_blank_mode blank_mode,
 			bool mode_switch)
 {
 	struct fb_info *info = fbcon_info_from_console(vc->vc_num);
 	struct fbcon_par *par = info->fbcon_par;
+	int blank_state = !blank ? FB_BLANK_UNBLANK : blank_mode + 1;
 
 	if (mode_switch) {
 		struct fb_var_screeninfo var = info->var;
 
 		par->graphics = 1;
 
-		if (!blank) {
+		if (!blank_state) {
 			var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
 				FB_ACTIVATE_KD_TEXT;
 			fb_set_var(info, &var);
@@ -2308,16 +2309,16 @@ static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank,
 	}
 
 	if (fbcon_is_active(vc, info)) {
-		if (par->blank_state != blank) {
-			par->blank_state = blank;
-			fbcon_cursor(vc, !blank);
-			par->cursor_flash = (!blank);
+		if (par->blank_state != blank_state) {
+			par->blank_state = blank_state;
+			fbcon_cursor(vc, !blank_state);
+			par->cursor_flash = (!blank_state);
 
-			if (fb_blank(info, blank))
-				fbcon_generic_blank(vc, info, blank);
+			if (fb_blank(info, blank_state))
+				fbcon_generic_blank(vc, info, blank_state);
 		}
 
-		if (!blank)
+		if (!blank_state)
 			update_screen(vc);
 	}
 
diff --git a/include/linux/console.h b/include/linux/console.h
index 502d1abe3f50..19f80556e9b2 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -56,8 +56,10 @@ enum vc_intensity;
  * @con_switch: notifier about the console switch; it is supposed to return
  *		true if a redraw is needed.
  * @con_blank:  blank/unblank the console. The target mode is passed in @blank.
- *		@mode_switch is set if changing from/to text/graphics. The hook
- *		is supposed to return true if a redraw is needed.
+ *		@blank_mode contains the requested level of power saving
+ *		if @blank is true. @mode_switch is set if changing from/to
+ *		text/graphics. The hook is supposed to return true if a redraw
+ *		is needed.
  * @con_font_set: set console @vc font to @font with height @vpitch. @flags can
  *		be %KD_FONT_FLAG_DONT_RECALC. (optional)
  * @con_font_get: fetch the current font on @vc of height @vpitch into @font.
@@ -97,7 +99,8 @@ struct consw {
 			unsigned int bottom, enum con_scroll dir,
 			unsigned int lines);
 	bool	(*con_switch)(struct vc_data *vc);
-	bool	(*con_blank)(struct vc_data *vc, enum vesa_blank_mode blank,
+	bool	(*con_blank)(struct vc_data *vc, bool blank,
+			     enum vesa_blank_mode blank_mode,
 			     bool mode_switch);
 	int	(*con_font_set)(struct vc_data *vc,
 				const struct console_font *font,
-- 
2.55.0


  parent reply	other threads:[~2026-10-12  9:41 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-12  9:27 [PATCH 0/2] vt: Simplify console-blanking interface Thomas Zimmermann
2026-10-12  9:27 ` [PATCH 1/2] vt: Use boolean as parameter to do_blank_screen() and do_unblank_screen() Thomas Zimmermann
2026-10-12 11:30   ` Greg KH
2026-10-12 12:28     ` Thomas Zimmermann
2026-10-12 12:41       ` Thomas Zimmermann
2026-10-12 16:13         ` Greg KH
2026-10-12  9:27 ` Thomas Zimmermann [this message]
2026-10-12 11:29   ` [PATCH 2/2] vt: Distinguish between blanking and VESA blank mode in con_blank Greg KH
2026-10-12 12:08     ` Thomas Zimmermann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261012094114.38549-3-tzimmermann@suse.de \
    --to=tzimmermann@suse.de \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=deller@gmx.de \
    --cc=gregkh@linuxfoundation.org \
    --cc=jirislaby@kernel.org \
    --cc=linux-fbdev@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-parisc@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=simona@ffwll.ch \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox