From chrimek@tpki.toppoint.de Mon May 16 03:10:05 EDT 1994
Article: 9135 of comp.os.linux.development
Path: bigblue.oit.unc.edu!concert!gatech!howland.reston.ans.net!EU.net!Germany.EU.net!netmbx.de!zrz.TU-Berlin.DE!math.fu-berlin.de!tpki.toppoint.de!rimki.toppoint.de!chrimek
Date: 15 May 1994 23:02:00 +0100
From: chrimek@rimki.toppoint.de (Christoph Rimek)
Newsgroups: comp.os.linux.development
Message-ID: <5OupqxxZo_B@rimki.toppoint.de>
References: <2orh33$t5s@aggedor.rmit.EDU.AU>
	<5NqI7RSoo_B@rimki.toppoint.de>
Subject: Programming for DPMS monitors - working!!
X-Newsreader: XP v3.0 R/C6513
Organization: Toppoint Mailbox e.V.
Reply-To: chrimek@tpki.toppoint.de
Lines: 300

Hello

In my last posting (of 30-apr-94) I published the raw code to support the
new generation of monitors that know the VESA Power Saving Protocol (PSP).
Recently I found the time to code and can now present:

Patch (based on Linux Kernel revision 1.0) for handling the Power Saving
feature of the new monitor generation. The code works on all these monitors
(mine is a Smile 1506) and should run on *all* video adapter cards (change
some i/o-adresses), although tested only on two different VGA-cards: a  
cheap Cirrus Logic (5428) and a miro Crystal 8S (S3-805).

The only affected module is driver/char/console.c and within this the
two functions blank_screen and unblank_screen and a few new variable  
definitions. If activated, the function memsetw is no longer needed.

The file config.in in the main kernel source directory has got two new
variables to define: CONFIG_VESA_PSPM (Power Saving Protocol Monitor)
and CONFIG_PSPM_FORCE_OFF. I have placed these lines into the character
device section right after the SELECTION part. You can choose from two
options:

(1) CONFIG_VESA_PSPM YES to activate these patches.
    The code will save the current setting of your video adapters'
    register settings and then program the controller to turn off
    the vertical sycnchronisation pulse. For details how this is
    achived refer to the code fragments following.

(2) CONFIG_PSPM_FORCE_OFF
    If your monitor locally has an Off_Mode timer then you should not
    force your video card to send the OFF-signal - your monitor will
    power down by itself.
    If your monitor cannot handle this and needs the Off-signal directly,
    or if you like your monitor to power down immediately when the
    blank_timer times out, then you chose this option.

On the other hand I'd recommend to not chose this second option unless
it is absolutely necessary. Powering down a monitor to the Off_State with
an approx. power consumption of 3-5 Watts is a rather strong action for
the CRT and it should not be done every now and then. If the software only  
sends the signal to enter Standby mode, you have the chance to interfere
before the monitor powers down. Do not set a too short period, if you love
your hardware :-)) .

Another advantage of leaving the power down handling to the monitor is
that your always can use the same kernel and change the OFF timeout
locally at your monitor. My model lets me chose the timeout in steps
of five minutes: 0-5-10-15-...


If requested, in the future it may be possible to install another timer
to provide a configurable delay between the two stages Standby and Off
similar to the "setterm -blank"-feature.

After all blah-blah now the diff-outputs for the patches.


--------------------------------cut here--------------------------------
--- config.in.orig	Mon May  9 17:16:34 1994
+++ config.in	Sun May 15 14:17:41 1994
@@ -116,4 +116,21 @@
 bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
 bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION y
+:
+:  The following item should only be set to YES (y) if your monitor knows
+:  the VESA Power Saving Protocol and if you have an EGA/VGA card (other
+:  video adapters are untested, but should work too).
+:  ATTENTION: setting this to YES when not appropriate can destroy your
+:             monitor and/or video adapter card !
+:
+bool 'VESA Power Saving Protocol Monitor support' CONFIG_VESA_PSPM n
+if [ "$CONFIG_VESA_PSPM" = "y" ]
+:
+: Some VESA PSP Monitors have a local timer and switch to Power_Off state
+: after a defined period (e.g. 5 minutes) in Standby mode. If your monitor
+: does not have such a timer or if you want to force your monitor to directly
+: go to Power_Off_State, set the following variable to YES (y).
+:
+bool 'Force OFF signal to be sent to VESA PSP Monitor' CONFIG_PSPM_FORCE_OFF y

--------------------------------cut here--------------------------------
--- console.c.orig	Wed Feb 23 07:52:24 1994
+++ console.c	Sun May 15 19:32:14 1994
@@ -78,6 +78,49 @@
 static char sel_buffer[SEL_BUFFER_SIZE] = { '\0' };
 #endif /* CONFIG_SELECTION */

+#ifdef CONFIG_VESA_PSPM
+/*
+ * This section(s) handles the VESA Power Saving Protocol that let a	*
+ * monitor be powered down whenever not needed for a longer time.	*
+ * VESA protocol defines:						*
+ *									*
+ *  Mode/Status		HSync	VSync	Video				*
+ *  ----------------------------------------------			*
+ *  "On"		on	on	active				*
+ *  "Suspend" {either}	on	off	blank				*
+ *            {  or  }	off	on	blank				*
+ *  "Off"               off	off	blank	<<  PSPM_FORCE_OFF	*
+ *									*
+ * Original code taken from the Power Management Utility (PMU) of	*
+ * Huang shi chao, delivered together with many new monitor models	*
+ * capable of the VESA Power Saving Protocol.				*
+ * Adapted to Linux by Christoph Rimek (chrimek@toppoint.de)  15-may-94	*
+ */
+
+#define seq_port_reg	(0x3c4)		/* Sequencer register select port	*/
+#define seq_port_val	(0x3c5)		/* Sequencer register value port	*/
+#define video_misc_rd	(0x3cc)		/* Video misc. read port	*/
+#define video_misc_wr	(0x3c2)		/* Video misc. write port	*/
+
+/* structure holding original VGA register settings */
+static struct {
+	unsigned char	SeqCtrlIndex;		/* Sequencer Index reg.   */
+	unsigned char	CrtCtrlIndex;		/* CRT-Contr. Index reg.  */
+	unsigned char	CrtMiscIO;		/* Miscellaneous register */
+#ifdef CONFIG_PSPM_FORCE_OFF
+	unsigned char	HorizontalTotal;	/* CRT-Controller:00h */
+	unsigned char	HorizDisplayEnd;	/* CRT-Controller:01h */
+	unsigned char	StartHorizRetrace;	/* CRT-Controller:04h */
+	unsigned char	EndHorizRetrace;	/* CRT-Controller:05h */
+#endif
+	unsigned char	Overflow;		/* CRT-Controller:07h */
+	unsigned char	StartVertRetrace;	/* CRT-Controller:10h */
+	unsigned char	EndVertRetrace;		/* CRT-Controller:11h */
+	unsigned char	ModeControl;		/* CRT-Controller:17h */
+	unsigned char	ClockingMode;		/* Seq-Controller:01h */
+} vga;
+#endif /* CONFIG_VESA_PSPM */
+
 #define NPAR 16

 extern void vt_init(void);
@@ -1330,6 +1373,9 @@
 	}
 }

+#ifndef CONFIG_VESA_PSPM
+/* This function only needed for fast screen buffer filling in	*/
+/* blank_screen, may be spared if hardware blanks screen.  /CR	*/
 void * memsetw(void * s,unsigned short c,int count)
 {
 __asm__("cld\n\t"
@@ -1340,6 +1386,7 @@
 	:"cx","di");
 return s;
 }
+#endif

 void console_print(const char * b)
 {
@@ -1551,10 +1598,80 @@
 	if (console_blanked)
 		return;
 	timer_table[BLANK_TIMER].fn = unblank_screen;
+
+#ifdef CONFIG_VESA_PSPM
+	/* save original values of VGA controller registers */
+	cli();
+	vga.SeqCtrlIndex = inb_p(seq_port_reg);
+	vga.CrtCtrlIndex = inb_p(video_port_reg);
+	vga.CrtMiscIO = inb_p(video_misc_rd);
+	sti();
+#ifdef CONFIG_PSPM_FORCE_OFF
+	outb_p(0x00,video_port_reg);			/* HorizontalTotal */
+	vga.HorizontalTotal = inb_p(video_port_val);
+	outb_p(0x01,video_port_reg);			/* HorizDisplayEnd */
+	vga.HorizDisplayEnd = inb_p(video_port_val);
+	outb_p(0x04,video_port_reg);			/* StartHorizRetrace */
+	vga.StartHorizRetrace = inb_p(video_port_val);
+	outb_p(0x05,video_port_reg);			/* EndHorizRetrace */
+	vga.EndHorizRetrace = inb_p(video_port_val);
+ #endif
+	outb_p(0x07,video_port_reg);			/* Overflow */
+	vga.Overflow = inb_p(video_port_val);
+	outb_p(0x10,video_port_reg);			/* StartVertRetrace */
+	vga.StartVertRetrace = inb_p(video_port_val);
+	outb_p(0x11,video_port_reg);			/* EndVertRetrace */
+	vga.EndVertRetrace = inb_p(video_port_val);
+	outb_p(0x17,video_port_reg);			/* ModeControl */
+	vga.ModeControl = inb_p(video_port_val);
+	outb_p(0x01,seq_port_reg);			/* ClockingMode */
+	vga.ClockingMode = inb_p(seq_port_val);
+
+	/* assure that video is enabled */
+	/* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
+	cli();
+	outb_p(0x01,seq_port_reg);
+	outb_p(vga.ClockingMode | 0x20,seq_port_val);
+	sti();
+#else
 	get_scrmem(fg_console);
 	hide_cursor();
+#endif /* CONFIG_VESA_PSPM */
+
 	console_blanked = 1;
+
+#ifdef CONFIG_VESA_PSPM
+	cli();
+	/* test for vertical retrace in process.... */
+	if ((vga.CrtMiscIO & 0x80) == 0x80)
+		outb_p(vga.CrtMiscIO & 0xef,video_misc_wr);
+
+	/*  Set <End of vertical retrace> to minimum (0) and		*
+	 *  <Start of vertical Retrace> to maximum (incl. overflow)	*
+	 *  Result: turn off vertical sync (VSync) pulse		*/
+	outb_p(0x10,video_port_reg);		/* StartVertRetrace */
+	outb_p(0xff,video_port_val);		/* maximum value */
+	outb_p(0x11,video_port_reg);		/* EndVertRetrace */
+	outb_p(0x40,video_port_val);            /* minimum (bits 0..3)  */
+	outb_p(0x07,video_port_reg);		/* Overflow */
+	outb_p(vga.Overflow | 0x84,video_port_val);	/* bits 9,10 of  */
+							/* vert. retrace */
+#ifdef CONFIG_PSPM_FORCE_OFF
+	/*  Set <End of horizontal retrace> to minimum (0) and	*
+	 *  <Start of horizontal Retrace> to maximum		*
+	 *  Result: turn off horizontal sync (HSync) pulse	*/
+	outb_p(0x04,video_port_reg);		/* StartHorizRetrace */
+	outb_p(0xff,video_port_val);		/* maximum */
+	outb_p(0x05,video_port_reg);		/* EndHorizRetrace */
+	outb_p(0x00,video_port_val);		/* minimum (0) */
+#endif
+	/* restore both index registers */
+	outb_p(vga.SeqCtrlIndex,seq_port_reg);
+	outb_p(vga.CrtCtrlIndex,video_port_reg);
+	sti();
+#else
 	memsetw((void *)video_mem_base, 0x0020, video_mem_term-video_mem_base );
+#endif /* CONFIG_VESA_PSPM */
 }

 void unblank_screen(void)
@@ -1567,9 +1684,41 @@
 		timer_active |= 1<<BLANK_TIMER;
 	}
 	console_blanked = 0;
+
+#ifdef CONFIG_VESA_PSPM
+	/* restore original values of VGA controller registers */
+	cli();
+	outb_p(vga.CrtMiscIO,video_misc_wr);
+#ifdef CONFIG_PSPM_FORCE_OFF
+	outb_p(0x00,video_port_reg);		/* HorizontalTotal */
+	outb_p(vga.HorizontalTotal,video_port_val);
+	outb_p(0x01,video_port_reg);		/* HorizDisplayEnd */
+	outb_p(vga.HorizDisplayEnd,video_port_val);
+	outb_p(0x04,video_port_reg);		/* StartHorizRetrace */
+	outb_p(vga.StartHorizRetrace,video_port_val);
+	outb_p(0x05,video_port_reg);		/* EndHorizRetrace */
+	outb_p(vga.EndHorizRetrace,video_port_val);
+#endif
+	outb_p(0x07,video_port_reg);		/* Overflow */
+	outb_p(vga.Overflow,video_port_val);
+	outb_p(0x10,video_port_reg);		/* StartVertRetrace */
+	outb_p(vga.StartVertRetrace,video_port_val);
+	outb_p(0x11,video_port_reg);		/* EndVertRetrace */
+	outb_p(vga.EndVertRetrace,video_port_val);
+	outb_p(0x17,video_port_reg);		/* ModeControl */
+	outb_p(vga.ModeControl,video_port_val);
+	outb_p(0x01,seq_port_reg);		/* ClockingMode */
+	outb_p(vga.ClockingMode,seq_port_val);
+
+	/* restore index/control registers */
+	outb_p(vga.SeqCtrlIndex,seq_port_reg);
+	outb_p(vga.CrtCtrlIndex,video_port_reg);
+	sti();
+#else
 	set_scrmem(fg_console);
 	set_origin(fg_console);
 	set_cursor(fg_console);
+#endif /* CONFIG_VESA_PSPM */
 }

 void update_screen(int new_console)
@@ -1841,8 +1990,10 @@
 #define colourmap ((char *)0xa0000)
 #define blackwmap ((char *)0xb0000)
 #define cmapsz 8192
+#ifndef seq_port_reg	/* avoid re-definition */
 #define seq_port_reg (0x3c4)
 #define seq_port_val (0x3c5)
+#endif
 #define gr_port_reg (0x3ce)
 #define gr_port_val (0x3cf)
--------------------------------cut here--------------------------------


That's all.
I'm not sure whether it is necessary to encapsule the saving of the video
adapter registers in a cli/sti-pair, while this code comes to execution
only when the machine (keyboard) is idle. When setting the registers I
think it is better not to allow any interruption of the sequences.

Please let me know about your experiences with this code.

-cr


--
Christoph Rimek, Kiel, Germany  (+49 431 18307)      chrimek@toppoint.de


