1/*
2 *  linux/drivers/video/vgacon.c -- Low level VGA based console driver
3 *
4 *	Created 28 Sep 1997 by Geert Uytterhoeven
5 *
6 *	Rewritten by Martin Mares <mj@ucw.cz>, July 1998
7 *
8 *  This file is based on the old console.c, vga.c and vesa_blank.c drivers.
9 *
10 *	Copyright (C) 1991, 1992  Linus Torvalds
11 *			    1995  Jay Estabrook
12 *
13 *	User definable mapping table and font loading by Eugene G. Crosser,
14 *	<crosser@average.org>
15 *
16 *	Improved loadable font/UTF-8 support by H. Peter Anvin
17 *	Feb-Sep 1995 <peter.anvin@linux.org>
18 *
19 *	Colour palette handling, by Simon Tatham
20 *	17-Jun-95 <sgt20@cam.ac.uk>
21 *
22 *	if 512 char mode is already enabled don't re-enable it,
23 *	because it causes screen to flicker, by Mitja Horvat
24 *	5-May-96 <mitja.horvat@guest.arnes.si>
25 *
26 *	Use 2 outw instead of 4 outb_p to reduce erroneous text
27 *	flashing on RHS of screen during heavy console scrolling .
28 *	Oct 1996, Paul Gortmaker.
29 *
30 *
31 *  This file is subject to the terms and conditions of the GNU General Public
32 *  License.  See the file COPYING in the main directory of this archive for
33 *  more details.
34 */
35
36#include <linux/module.h>
37#include <linux/types.h>
38#include <linux/fs.h>
39#include <linux/kernel.h>
40#include <linux/console.h>
41#include <linux/string.h>
42#include <linux/kd.h>
43#include <linux/slab.h>
44#include <linux/vt_kern.h>
45#include <linux/sched.h>
46#include <linux/selection.h>
47#include <linux/spinlock.h>
48#include <linux/ioport.h>
49#include <linux/init.h>
50#include <linux/screen_info.h>
51#include <video/vga.h>
52#include <asm/io.h>
53
54static DEFINE_RAW_SPINLOCK(vga_lock);
55static int cursor_size_lastfrom;
56static int cursor_size_lastto;
57static u32 vgacon_xres;
58static u32 vgacon_yres;
59static struct vgastate vgastate;
60
61#define BLANK 0x0020
62
63#define VGA_FONTWIDTH       8   /* VGA does not support fontwidths != 8 */
64/*
65 *  Interface used by the world
66 */
67
68static int vgacon_set_origin(struct vc_data *c);
69
70static struct uni_pagedict *vgacon_uni_pagedir;
71static int vgacon_refcount;
72
73/* Description of the hardware situation */
74static unsigned long	vga_vram_base		__read_mostly;	/* Base of video memory */
75static unsigned long	vga_vram_end		__read_mostly;	/* End of video memory */
76static unsigned int	vga_vram_size		__read_mostly;	/* Size of video memory */
77static u16		vga_video_port_reg	__read_mostly;	/* Video register select port */
78static u16		vga_video_port_val	__read_mostly;	/* Video register value port */
79static unsigned int	vga_video_num_columns;			/* Number of text columns */
80static unsigned int	vga_video_num_lines;			/* Number of text lines */
81static bool		vga_can_do_color;			/* Do we support colors? */
82static unsigned int	vga_default_font_height __read_mostly;	/* Height of default screen font */
83static unsigned char	vga_video_type		__read_mostly;	/* Card type */
84static int		vga_vesa_blanked;
85static bool 		vga_palette_blanked;
86static bool 		vga_is_gfx;
87static bool 		vga_512_chars;
88static int 		vga_video_font_height;
89static int 		vga_scan_lines		__read_mostly;
90static unsigned int 	vga_rolled_over; /* last vc_origin offset before wrap */
91
92static bool vga_hardscroll_enabled;
93static bool vga_hardscroll_user_enable = true;
94
95static int __init no_scroll(char *str)
96{
97	/*
98	 * Disabling scrollback is required for the Braillex ib80-piezo
99	 * Braille reader made by F.H. Papenmeier (Germany).
100	 * Use the "no-scroll" bootflag.
101	 */
102	vga_hardscroll_user_enable = vga_hardscroll_enabled = false;
103	return 1;
104}
105
106__setup("no-scroll", no_scroll);
107
108/*
109 * By replacing the four outb_p with two back to back outw, we can reduce
110 * the window of opportunity to see text mislocated to the RHS of the
111 * console during heavy scrolling activity. However there is the remote
112 * possibility that some pre-dinosaur hardware won't like the back to back
113 * I/O. Since the Xservers get away with it, we should be able to as well.
114 */
115static inline void write_vga(unsigned char reg, unsigned int val)
116{
117	unsigned int v1, v2;
118	unsigned long flags;
119
120	/*
121	 * ddprintk might set the console position from interrupt
122	 * handlers, thus the write has to be IRQ-atomic.
123	 */
124	raw_spin_lock_irqsave(&vga_lock, flags);
125	v1 = reg + (val & 0xff00);
126	v2 = reg + 1 + ((val << 8) & 0xff00);
127	outw(v1, vga_video_port_reg);
128	outw(v2, vga_video_port_reg);
129	raw_spin_unlock_irqrestore(&vga_lock, flags);
130}
131
132static inline void vga_set_mem_top(struct vc_data *c)
133{
134	write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2);
135}
136
137static void vgacon_scrolldelta(struct vc_data *c, int lines)
138{
139	vc_scrolldelta_helper(c, lines, vga_rolled_over, (void *)vga_vram_base,
140			vga_vram_size);
141	vga_set_mem_top(c);
142}
143
144static void vgacon_restore_screen(struct vc_data *c)
145{
146	if (c->vc_origin != c->vc_visible_origin)
147		vgacon_scrolldelta(c, 0);
148}
149
150static const char *vgacon_startup(void)
151{
152	const char *display_desc = NULL;
153	u16 saved1, saved2;
154	volatile u16 *p;
155
156	if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB ||
157	    screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) {
158	      no_vga:
159#ifdef CONFIG_DUMMY_CONSOLE
160		conswitchp = &dummy_con;
161		return conswitchp->con_startup();
162#else
163		return NULL;
164#endif
165	}
166
167	/* boot_params.screen_info reasonably initialized? */
168	if ((screen_info.orig_video_lines == 0) ||
169	    (screen_info.orig_video_cols  == 0))
170		goto no_vga;
171
172	/* VGA16 modes are not handled by VGACON */
173	if ((screen_info.orig_video_mode == 0x0D) ||	/* 320x200/4 */
174	    (screen_info.orig_video_mode == 0x0E) ||	/* 640x200/4 */
175	    (screen_info.orig_video_mode == 0x10) ||	/* 640x350/4 */
176	    (screen_info.orig_video_mode == 0x12) ||	/* 640x480/4 */
177	    (screen_info.orig_video_mode == 0x6A))	/* 800x600/4 (VESA) */
178		goto no_vga;
179
180	vga_video_num_lines = screen_info.orig_video_lines;
181	vga_video_num_columns = screen_info.orig_video_cols;
182	vgastate.vgabase = NULL;
183
184	if (screen_info.orig_video_mode == 7) {
185		/* Monochrome display */
186		vga_vram_base = 0xb0000;
187		vga_video_port_reg = VGA_CRT_IM;
188		vga_video_port_val = VGA_CRT_DM;
189		if ((screen_info.orig_video_ega_bx & 0xff) != 0x10) {
190			static struct resource ega_console_resource =
191			    { .name	= "ega",
192			      .flags	= IORESOURCE_IO,
193			      .start	= 0x3B0,
194			      .end	= 0x3BF };
195			vga_video_type = VIDEO_TYPE_EGAM;
196			vga_vram_size = 0x8000;
197			display_desc = "EGA+";
198			request_resource(&ioport_resource,
199					 &ega_console_resource);
200		} else {
201			static struct resource mda1_console_resource =
202			    { .name	= "mda",
203			      .flags	= IORESOURCE_IO,
204			      .start	= 0x3B0,
205			      .end	= 0x3BB };
206			static struct resource mda2_console_resource =
207			    { .name	= "mda",
208			      .flags	= IORESOURCE_IO,
209			      .start	= 0x3BF,
210			      .end	= 0x3BF };
211			vga_video_type = VIDEO_TYPE_MDA;
212			vga_vram_size = 0x2000;
213			display_desc = "*MDA";
214			request_resource(&ioport_resource,
215					 &mda1_console_resource);
216			request_resource(&ioport_resource,
217					 &mda2_console_resource);
218			vga_video_font_height = 14;
219		}
220	} else {
221		/* If not, it is color. */
222		vga_can_do_color = true;
223		vga_vram_base = 0xb8000;
224		vga_video_port_reg = VGA_CRT_IC;
225		vga_video_port_val = VGA_CRT_DC;
226		if ((screen_info.orig_video_ega_bx & 0xff) != 0x10) {
227			int i;
228
229			vga_vram_size = 0x8000;
230
231			if (!screen_info.orig_video_isVGA) {
232				static struct resource ega_console_resource =
233				    { .name	= "ega",
234				      .flags	= IORESOURCE_IO,
235				      .start	= 0x3C0,
236				      .end	= 0x3DF };
237				vga_video_type = VIDEO_TYPE_EGAC;
238				display_desc = "EGA";
239				request_resource(&ioport_resource,
240						 &ega_console_resource);
241			} else {
242				static struct resource vga_console_resource =
243				    { .name	= "vga+",
244				      .flags	= IORESOURCE_IO,
245				      .start	= 0x3C0,
246				      .end	= 0x3DF };
247				vga_video_type = VIDEO_TYPE_VGAC;
248				display_desc = "VGA+";
249				request_resource(&ioport_resource,
250						 &vga_console_resource);
251
252				/*
253				 * Normalise the palette registers, to point
254				 * the 16 screen colours to the first 16
255				 * DAC entries.
256				 */
257
258				for (i = 0; i < 16; i++) {
259					inb_p(VGA_IS1_RC);
260					outb_p(i, VGA_ATT_W);
261					outb_p(i, VGA_ATT_W);
262				}
263				outb_p(0x20, VGA_ATT_W);
264
265				/*
266				 * Now set the DAC registers back to their
267				 * default values
268				 */
269				for (i = 0; i < 16; i++) {
270					outb_p(color_table[i], VGA_PEL_IW);
271					outb_p(default_red[i], VGA_PEL_D);
272					outb_p(default_grn[i], VGA_PEL_D);
273					outb_p(default_blu[i], VGA_PEL_D);
274				}
275			}
276		} else {
277			static struct resource cga_console_resource =
278			    { .name	= "cga",
279			      .flags	= IORESOURCE_IO,
280			      .start	= 0x3D4,
281			      .end	= 0x3D5 };
282			vga_video_type = VIDEO_TYPE_CGA;
283			vga_vram_size = 0x2000;
284			display_desc = "*CGA";
285			request_resource(&ioport_resource,
286					 &cga_console_resource);
287			vga_video_font_height = 8;
288		}
289	}
290
291	vga_vram_base = VGA_MAP_MEM(vga_vram_base, vga_vram_size);
292	vga_vram_end = vga_vram_base + vga_vram_size;
293
294	/*
295	 *      Find out if there is a graphics card present.
296	 *      Are there smarter methods around?
297	 */
298	p = (volatile u16 *) vga_vram_base;
299	saved1 = scr_readw(p);
300	saved2 = scr_readw(p + 1);
301	scr_writew(0xAA55, p);
302	scr_writew(0x55AA, p + 1);
303	if (scr_readw(p) != 0xAA55 || scr_readw(p + 1) != 0x55AA) {
304		scr_writew(saved1, p);
305		scr_writew(saved2, p + 1);
306		goto no_vga;
307	}
308	scr_writew(0x55AA, p);
309	scr_writew(0xAA55, p + 1);
310	if (scr_readw(p) != 0x55AA || scr_readw(p + 1) != 0xAA55) {
311		scr_writew(saved1, p);
312		scr_writew(saved2, p + 1);
313		goto no_vga;
314	}
315	scr_writew(saved1, p);
316	scr_writew(saved2, p + 1);
317
318	if (vga_video_type == VIDEO_TYPE_EGAC
319	    || vga_video_type == VIDEO_TYPE_VGAC
320	    || vga_video_type == VIDEO_TYPE_EGAM) {
321		vga_hardscroll_enabled = vga_hardscroll_user_enable;
322		vga_default_font_height = screen_info.orig_video_points;
323		vga_video_font_height = screen_info.orig_video_points;
324		/* This may be suboptimal but is a safe bet - go with it */
325		vga_scan_lines =
326		    vga_video_font_height * vga_video_num_lines;
327	}
328
329	vgacon_xres = screen_info.orig_video_cols * VGA_FONTWIDTH;
330	vgacon_yres = vga_scan_lines;
331
332	return display_desc;
333}
334
335static void vgacon_init(struct vc_data *c, int init)
336{
337	struct uni_pagedict *p;
338
339	/*
340	 * We cannot be loaded as a module, therefore init will be 1
341	 * if we are the default console, however if we are a fallback
342	 * console, for example if fbcon has failed registration, then
343	 * init will be 0, so we need to make sure our boot parameters
344	 * have been copied to the console structure for vgacon_resize
345	 * ultimately called by vc_resize.  Any subsequent calls to
346	 * vgacon_init init will have init set to 0 too.
347	 */
348	c->vc_can_do_color = vga_can_do_color;
349	c->vc_scan_lines = vga_scan_lines;
350	c->vc_font.height = c->vc_cell_height = vga_video_font_height;
351
352	/* set dimensions manually if init != 0 since vc_resize() will fail */
353	if (init) {
354		c->vc_cols = vga_video_num_columns;
355		c->vc_rows = vga_video_num_lines;
356	} else
357		vc_resize(c, vga_video_num_columns, vga_video_num_lines);
358
359	c->vc_complement_mask = 0x7700;
360	if (vga_512_chars)
361		c->vc_hi_font_mask = 0x0800;
362	p = *c->uni_pagedict_loc;
363	if (c->uni_pagedict_loc != &vgacon_uni_pagedir) {
364		con_free_unimap(c);
365		c->uni_pagedict_loc = &vgacon_uni_pagedir;
366		vgacon_refcount++;
367	}
368	if (!vgacon_uni_pagedir && p)
369		con_set_default_unimap(c);
370
371	/* Only set the default if the user didn't deliberately override it */
372	if (global_cursor_default == -1)
373		global_cursor_default =
374			!(screen_info.flags & VIDEO_FLAGS_NOCURSOR);
375}
376
377static void vgacon_deinit(struct vc_data *c)
378{
379	/* When closing the active console, reset video origin */
380	if (con_is_visible(c)) {
381		c->vc_visible_origin = vga_vram_base;
382		vga_set_mem_top(c);
383	}
384
385	if (!--vgacon_refcount)
386		con_free_unimap(c);
387	c->uni_pagedict_loc = &c->uni_pagedict;
388	con_set_default_unimap(c);
389}
390
391static u8 vgacon_build_attr(struct vc_data *c, u8 color,
392			    enum vc_intensity intensity,
393			    bool blink, bool underline, bool reverse,
394			    bool italic)
395{
396	u8 attr = color;
397
398	if (vga_can_do_color) {
399		if (italic)
400			attr = (attr & 0xF0) | c->vc_itcolor;
401		else if (underline)
402			attr = (attr & 0xf0) | c->vc_ulcolor;
403		else if (intensity == VCI_HALF_BRIGHT)
404			attr = (attr & 0xf0) | c->vc_halfcolor;
405	}
406	if (reverse)
407		attr =
408		    ((attr) & 0x88) | ((((attr) >> 4) | ((attr) << 4)) &
409				       0x77);
410	if (blink)
411		attr ^= 0x80;
412	if (intensity == VCI_BOLD)
413		attr ^= 0x08;
414	if (!vga_can_do_color) {
415		if (italic)
416			attr = (attr & 0xF8) | 0x02;
417		else if (underline)
418			attr = (attr & 0xf8) | 0x01;
419		else if (intensity == VCI_HALF_BRIGHT)
420			attr = (attr & 0xf0) | 0x08;
421	}
422	return attr;
423}
424
425static void vgacon_invert_region(struct vc_data *c, u16 * p, int count)
426{
427	const bool col = vga_can_do_color;
428
429	while (count--) {
430		u16 a = scr_readw(p);
431		if (col)
432			a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
433			    (((a) & 0x0700) << 4);
434		else
435			a ^= ((a & 0x0700) == 0x0100) ? 0x7000 : 0x7700;
436		scr_writew(a, p++);
437	}
438}
439
440static void vgacon_set_cursor_size(int from, int to)
441{
442	unsigned long flags;
443	int curs, cure;
444
445	if ((from == cursor_size_lastfrom) && (to == cursor_size_lastto))
446		return;
447	cursor_size_lastfrom = from;
448	cursor_size_lastto = to;
449
450	raw_spin_lock_irqsave(&vga_lock, flags);
451	if (vga_video_type >= VIDEO_TYPE_VGAC) {
452		outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg);
453		curs = inb_p(vga_video_port_val);
454		outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg);
455		cure = inb_p(vga_video_port_val);
456	} else {
457		curs = 0;
458		cure = 0;
459	}
460
461	curs = (curs & 0xc0) | from;
462	cure = (cure & 0xe0) | to;
463
464	outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg);
465	outb_p(curs, vga_video_port_val);
466	outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg);
467	outb_p(cure, vga_video_port_val);
468	raw_spin_unlock_irqrestore(&vga_lock, flags);
469}
470
471static void vgacon_cursor(struct vc_data *c, int mode)
472{
473	unsigned int c_height;
474
475	if (c->vc_mode != KD_TEXT)
476		return;
477
478	vgacon_restore_screen(c);
479
480	c_height = c->vc_cell_height;
481
482	switch (mode) {
483	case CM_ERASE:
484		write_vga(14, (c->vc_pos - vga_vram_base) / 2);
485	        if (vga_video_type >= VIDEO_TYPE_VGAC)
486			vgacon_set_cursor_size(31, 30);
487		else
488			vgacon_set_cursor_size(31, 31);
489		break;
490
491	case CM_MOVE:
492	case CM_DRAW:
493		write_vga(14, (c->vc_pos - vga_vram_base) / 2);
494		switch (CUR_SIZE(c->vc_cursor_type)) {
495		case CUR_UNDERLINE:
496			vgacon_set_cursor_size(c_height -
497					       (c_height < 10 ? 2 : 3),
498					       c_height -
499					       (c_height < 10 ? 1 : 2));
500			break;
501		case CUR_TWO_THIRDS:
502			vgacon_set_cursor_size(c_height / 3, c_height -
503					       (c_height < 10 ? 1 : 2));
504			break;
505		case CUR_LOWER_THIRD:
506			vgacon_set_cursor_size(c_height * 2 / 3, c_height -
507					       (c_height < 10 ? 1 : 2));
508			break;
509		case CUR_LOWER_HALF:
510			vgacon_set_cursor_size(c_height / 2, c_height -
511					       (c_height < 10 ? 1 : 2));
512			break;
513		case CUR_NONE:
514			if (vga_video_type >= VIDEO_TYPE_VGAC)
515				vgacon_set_cursor_size(31, 30);
516			else
517				vgacon_set_cursor_size(31, 31);
518			break;
519		default:
520			vgacon_set_cursor_size(1, c_height);
521			break;
522		}
523		break;
524	}
525}
526
527static void vgacon_doresize(struct vc_data *c,
528		unsigned int width, unsigned int height)
529{
530	unsigned long flags;
531	unsigned int scanlines = height * c->vc_cell_height;
532	u8 scanlines_lo = 0, r7 = 0, vsync_end = 0, mode, max_scan;
533
534	raw_spin_lock_irqsave(&vga_lock, flags);
535
536	vgacon_xres = width * VGA_FONTWIDTH;
537	vgacon_yres = height * c->vc_cell_height;
538	if (vga_video_type >= VIDEO_TYPE_VGAC) {
539		outb_p(VGA_CRTC_MAX_SCAN, vga_video_port_reg);
540		max_scan = inb_p(vga_video_port_val);
541
542		if (max_scan & 0x80)
543			scanlines <<= 1;
544
545		outb_p(VGA_CRTC_MODE, vga_video_port_reg);
546		mode = inb_p(vga_video_port_val);
547
548		if (mode & 0x04)
549			scanlines >>= 1;
550
551		scanlines -= 1;
552		scanlines_lo = scanlines & 0xff;
553
554		outb_p(VGA_CRTC_OVERFLOW, vga_video_port_reg);
555		r7 = inb_p(vga_video_port_val) & ~0x42;
556
557		if (scanlines & 0x100)
558			r7 |= 0x02;
559		if (scanlines & 0x200)
560			r7 |= 0x40;
561
562		/* deprotect registers */
563		outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
564		vsync_end = inb_p(vga_video_port_val);
565		outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
566		outb_p(vsync_end & ~0x80, vga_video_port_val);
567	}
568
569	outb_p(VGA_CRTC_H_DISP, vga_video_port_reg);
570	outb_p(width - 1, vga_video_port_val);
571	outb_p(VGA_CRTC_OFFSET, vga_video_port_reg);
572	outb_p(width >> 1, vga_video_port_val);
573
574	if (vga_video_type >= VIDEO_TYPE_VGAC) {
575		outb_p(VGA_CRTC_V_DISP_END, vga_video_port_reg);
576		outb_p(scanlines_lo, vga_video_port_val);
577		outb_p(VGA_CRTC_OVERFLOW, vga_video_port_reg);
578		outb_p(r7,vga_video_port_val);
579
580		/* reprotect registers */
581		outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
582		outb_p(vsync_end, vga_video_port_val);
583	}
584
585	raw_spin_unlock_irqrestore(&vga_lock, flags);
586}
587
588static int vgacon_switch(struct vc_data *c)
589{
590	int x = c->vc_cols * VGA_FONTWIDTH;
591	int y = c->vc_rows * c->vc_cell_height;
592	int rows = screen_info.orig_video_lines * vga_default_font_height/
593		c->vc_cell_height;
594	/*
595	 * We need to save screen size here as it's the only way
596	 * we can spot the screen has been resized and we need to
597	 * set size of freshly allocated screens ourselves.
598	 */
599	vga_video_num_columns = c->vc_cols;
600	vga_video_num_lines = c->vc_rows;
601
602	/* We can only copy out the size of the video buffer here,
603	 * otherwise we get into VGA BIOS */
604
605	if (!vga_is_gfx) {
606		scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf,
607			    c->vc_screenbuf_size > vga_vram_size ?
608				vga_vram_size : c->vc_screenbuf_size);
609
610		if ((vgacon_xres != x || vgacon_yres != y) &&
611		    (!(vga_video_num_columns % 2) &&
612		     vga_video_num_columns <= screen_info.orig_video_cols &&
613		     vga_video_num_lines <= rows))
614			vgacon_doresize(c, c->vc_cols, c->vc_rows);
615	}
616
617	return 0;		/* Redrawing not needed */
618}
619
620static void vga_set_palette(struct vc_data *vc, const unsigned char *table)
621{
622	int i, j;
623
624	vga_w(vgastate.vgabase, VGA_PEL_MSK, 0xff);
625	for (i = j = 0; i < 16; i++) {
626		vga_w(vgastate.vgabase, VGA_PEL_IW, table[i]);
627		vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
628		vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
629		vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
630	}
631}
632
633static void vgacon_set_palette(struct vc_data *vc, const unsigned char *table)
634{
635	if (vga_video_type != VIDEO_TYPE_VGAC || vga_palette_blanked
636	    || !con_is_visible(vc))
637		return;
638	vga_set_palette(vc, table);
639}
640
641/* structure holding original VGA register settings */
642static struct {
643	unsigned char SeqCtrlIndex;	/* Sequencer Index reg.   */
644	unsigned char CrtCtrlIndex;	/* CRT-Contr. Index reg.  */
645	unsigned char CrtMiscIO;	/* Miscellaneous register */
646	unsigned char HorizontalTotal;	/* CRT-Controller:00h */
647	unsigned char HorizDisplayEnd;	/* CRT-Controller:01h */
648	unsigned char StartHorizRetrace;	/* CRT-Controller:04h */
649	unsigned char EndHorizRetrace;	/* CRT-Controller:05h */
650	unsigned char Overflow;	/* CRT-Controller:07h */
651	unsigned char StartVertRetrace;	/* CRT-Controller:10h */
652	unsigned char EndVertRetrace;	/* CRT-Controller:11h */
653	unsigned char ModeControl;	/* CRT-Controller:17h */
654	unsigned char ClockingMode;	/* Seq-Controller:01h */
655} vga_state;
656
657static void vga_vesa_blank(struct vgastate *state, int mode)
658{
659	/* save original values of VGA controller registers */
660	if (!vga_vesa_blanked) {
661		raw_spin_lock_irq(&vga_lock);
662		vga_state.SeqCtrlIndex = vga_r(state->vgabase, VGA_SEQ_I);
663		vga_state.CrtCtrlIndex = inb_p(vga_video_port_reg);
664		vga_state.CrtMiscIO = vga_r(state->vgabase, VGA_MIS_R);
665		raw_spin_unlock_irq(&vga_lock);
666
667		outb_p(0x00, vga_video_port_reg);	/* HorizontalTotal */
668		vga_state.HorizontalTotal = inb_p(vga_video_port_val);
669		outb_p(0x01, vga_video_port_reg);	/* HorizDisplayEnd */
670		vga_state.HorizDisplayEnd = inb_p(vga_video_port_val);
671		outb_p(0x04, vga_video_port_reg);	/* StartHorizRetrace */
672		vga_state.StartHorizRetrace = inb_p(vga_video_port_val);
673		outb_p(0x05, vga_video_port_reg);	/* EndHorizRetrace */
674		vga_state.EndHorizRetrace = inb_p(vga_video_port_val);
675		outb_p(0x07, vga_video_port_reg);	/* Overflow */
676		vga_state.Overflow = inb_p(vga_video_port_val);
677		outb_p(0x10, vga_video_port_reg);	/* StartVertRetrace */
678		vga_state.StartVertRetrace = inb_p(vga_video_port_val);
679		outb_p(0x11, vga_video_port_reg);	/* EndVertRetrace */
680		vga_state.EndVertRetrace = inb_p(vga_video_port_val);
681		outb_p(0x17, vga_video_port_reg);	/* ModeControl */
682		vga_state.ModeControl = inb_p(vga_video_port_val);
683		vga_state.ClockingMode = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE);
684	}
685
686	/* assure that video is enabled */
687	/* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
688	raw_spin_lock_irq(&vga_lock);
689	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, vga_state.ClockingMode | 0x20);
690
691	/* test for vertical retrace in process.... */
692	if ((vga_state.CrtMiscIO & 0x80) == 0x80)
693		vga_w(state->vgabase, VGA_MIS_W, vga_state.CrtMiscIO & 0xEF);
694
695	/*
696	 * Set <End of vertical retrace> to minimum (0) and
697	 * <Start of vertical Retrace> to maximum (incl. overflow)
698	 * Result: turn off vertical sync (VSync) pulse.
699	 */
700	if (mode & VESA_VSYNC_SUSPEND) {
701		outb_p(0x10, vga_video_port_reg);	/* StartVertRetrace */
702		outb_p(0xff, vga_video_port_val);	/* maximum value */
703		outb_p(0x11, vga_video_port_reg);	/* EndVertRetrace */
704		outb_p(0x40, vga_video_port_val);	/* minimum (bits 0..3)  */
705		outb_p(0x07, vga_video_port_reg);	/* Overflow */
706		outb_p(vga_state.Overflow | 0x84, vga_video_port_val);	/* bits 9,10 of vert. retrace */
707	}
708
709	if (mode & VESA_HSYNC_SUSPEND) {
710		/*
711		 * Set <End of horizontal retrace> to minimum (0) and
712		 *  <Start of horizontal Retrace> to maximum
713		 * Result: turn off horizontal sync (HSync) pulse.
714		 */
715		outb_p(0x04, vga_video_port_reg);	/* StartHorizRetrace */
716		outb_p(0xff, vga_video_port_val);	/* maximum */
717		outb_p(0x05, vga_video_port_reg);	/* EndHorizRetrace */
718		outb_p(0x00, vga_video_port_val);	/* minimum (0) */
719	}
720
721	/* restore both index registers */
722	vga_w(state->vgabase, VGA_SEQ_I, vga_state.SeqCtrlIndex);
723	outb_p(vga_state.CrtCtrlIndex, vga_video_port_reg);
724	raw_spin_unlock_irq(&vga_lock);
725}
726
727static void vga_vesa_unblank(struct vgastate *state)
728{
729	/* restore original values of VGA controller registers */
730	raw_spin_lock_irq(&vga_lock);
731	vga_w(state->vgabase, VGA_MIS_W, vga_state.CrtMiscIO);
732
733	outb_p(0x00, vga_video_port_reg);	/* HorizontalTotal */
734	outb_p(vga_state.HorizontalTotal, vga_video_port_val);
735	outb_p(0x01, vga_video_port_reg);	/* HorizDisplayEnd */
736	outb_p(vga_state.HorizDisplayEnd, vga_video_port_val);
737	outb_p(0x04, vga_video_port_reg);	/* StartHorizRetrace */
738	outb_p(vga_state.StartHorizRetrace, vga_video_port_val);
739	outb_p(0x05, vga_video_port_reg);	/* EndHorizRetrace */
740	outb_p(vga_state.EndHorizRetrace, vga_video_port_val);
741	outb_p(0x07, vga_video_port_reg);	/* Overflow */
742	outb_p(vga_state.Overflow, vga_video_port_val);
743	outb_p(0x10, vga_video_port_reg);	/* StartVertRetrace */
744	outb_p(vga_state.StartVertRetrace, vga_video_port_val);
745	outb_p(0x11, vga_video_port_reg);	/* EndVertRetrace */
746	outb_p(vga_state.EndVertRetrace, vga_video_port_val);
747	outb_p(0x17, vga_video_port_reg);	/* ModeControl */
748	outb_p(vga_state.ModeControl, vga_video_port_val);
749	/* ClockingMode */
750	vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, vga_state.ClockingMode);
751
752	/* restore index/control registers */
753	vga_w(state->vgabase, VGA_SEQ_I, vga_state.SeqCtrlIndex);
754	outb_p(vga_state.CrtCtrlIndex, vga_video_port_reg);
755	raw_spin_unlock_irq(&vga_lock);
756}
757
758static void vga_pal_blank(struct vgastate *state)
759{
760	int i;
761
762	vga_w(state->vgabase, VGA_PEL_MSK, 0xff);
763	for (i = 0; i < 16; i++) {
764		vga_w(state->vgabase, VGA_PEL_IW, i);
765		vga_w(state->vgabase, VGA_PEL_D, 0);
766		vga_w(state->vgabase, VGA_PEL_D, 0);
767		vga_w(state->vgabase, VGA_PEL_D, 0);
768	}
769}
770
771static int vgacon_blank(struct vc_data *c, int blank, int mode_switch)
772{
773	switch (blank) {
774	case 0:		/* Unblank */
775		if (vga_vesa_blanked) {
776			vga_vesa_unblank(&vgastate);
777			vga_vesa_blanked = 0;
778		}
779		if (vga_palette_blanked) {
780			vga_set_palette(c, color_table);
781			vga_palette_blanked = false;
782			return 0;
783		}
784		vga_is_gfx = false;
785		/* Tell console.c that it has to restore the screen itself */
786		return 1;
787	case 1:		/* Normal blanking */
788	case -1:	/* Obsolete */
789		if (!mode_switch && vga_video_type == VIDEO_TYPE_VGAC) {
790			vga_pal_blank(&vgastate);
791			vga_palette_blanked = true;
792			return 0;
793		}
794		vgacon_set_origin(c);
795		scr_memsetw((void *) vga_vram_base, BLANK,
796			    c->vc_screenbuf_size);
797		if (mode_switch)
798			vga_is_gfx = true;
799		return 1;
800	default:		/* VESA blanking */
801		if (vga_video_type == VIDEO_TYPE_VGAC) {
802			vga_vesa_blank(&vgastate, blank - 1);
803			vga_vesa_blanked = blank;
804		}
805		return 0;
806	}
807}
808
809/*
810 * PIO_FONT support.
811 *
812 * The font loading code goes back to the codepage package by
813 * Joel Hoffman (joel@wam.umd.edu). (He reports that the original
814 * reference is: "From: p. 307 of _Programmer's Guide to PC & PS/2
815 * Video Systems_ by Richard Wilton. 1987.  Microsoft Press".)
816 *
817 * Change for certain monochrome monitors by Yury Shevchuck
818 * (sizif@botik.yaroslavl.su).
819 */
820
821#define colourmap 0xa0000
822/* Pauline Middelink <middelin@polyware.iaf.nl> reports that we
823   should use 0xA0000 for the bwmap as well.. */
824#define blackwmap 0xa0000
825#define cmapsz 8192
826
827static int vgacon_do_font_op(struct vgastate *state, char *arg, int set,
828		bool ch512)
829{
830	unsigned short video_port_status = vga_video_port_reg + 6;
831	int font_select = 0x00, beg, i;
832	char *charmap;
833	bool clear_attribs = false;
834	if (vga_video_type != VIDEO_TYPE_EGAM) {
835		charmap = (char *) VGA_MAP_MEM(colourmap, 0);
836		beg = 0x0e;
837	} else {
838		charmap = (char *) VGA_MAP_MEM(blackwmap, 0);
839		beg = 0x0a;
840	}
841
842	/*
843	 * All fonts are loaded in slot 0 (0:1 for 512 ch)
844	 */
845
846	if (!arg)
847		return -EINVAL;	/* Return to default font not supported */
848
849	font_select = ch512 ? 0x04 : 0x00;
850
851	raw_spin_lock_irq(&vga_lock);
852	/* First, the Sequencer */
853	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
854	/* CPU writes only to map 2 */
855	vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x04);
856	/* Sequential addressing */
857	vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x07);
858	/* Clear synchronous reset */
859	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03);
860
861	/* Now, the graphics controller, select map 2 */
862	vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x02);
863	/* disable odd-even addressing */
864	vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x00);
865	/* map start at A000:0000 */
866	vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x00);
867	raw_spin_unlock_irq(&vga_lock);
868
869	if (arg) {
870		if (set)
871			for (i = 0; i < cmapsz; i++) {
872				vga_writeb(arg[i], charmap + i);
873				cond_resched();
874			}
875		else
876			for (i = 0; i < cmapsz; i++) {
877				arg[i] = vga_readb(charmap + i);
878				cond_resched();
879			}
880
881		/*
882		 * In 512-character mode, the character map is not contiguous if
883		 * we want to remain EGA compatible -- which we do
884		 */
885
886		if (ch512) {
887			charmap += 2 * cmapsz;
888			arg += cmapsz;
889			if (set)
890				for (i = 0; i < cmapsz; i++) {
891					vga_writeb(arg[i], charmap + i);
892					cond_resched();
893				}
894			else
895				for (i = 0; i < cmapsz; i++) {
896					arg[i] = vga_readb(charmap + i);
897					cond_resched();
898				}
899		}
900	}
901
902	raw_spin_lock_irq(&vga_lock);
903	/* First, the sequencer, Synchronous reset */
904	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x01);
905	/* CPU writes to maps 0 and 1 */
906	vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x03);
907	/* odd-even addressing */
908	vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x03);
909	/* Character Map Select */
910	if (set)
911		vga_wseq(state->vgabase, VGA_SEQ_CHARACTER_MAP, font_select);
912	/* clear synchronous reset */
913	vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03);
914
915	/* Now, the graphics controller, select map 0 for CPU */
916	vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x00);
917	/* enable even-odd addressing */
918	vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x10);
919	/* map starts at b800:0 or b000:0 */
920	vga_wgfx(state->vgabase, VGA_GFX_MISC, beg);
921
922	/* if 512 char mode is already enabled don't re-enable it. */
923	if ((set) && (ch512 != vga_512_chars)) {
924		vga_512_chars = ch512;
925		/* 256-char: enable intensity bit
926		   512-char: disable intensity bit */
927		inb_p(video_port_status);	/* clear address flip-flop */
928		/* color plane enable register */
929		vga_wattr(state->vgabase, VGA_ATC_PLANE_ENABLE, ch512 ? 0x07 : 0x0f);
930		/* Wilton (1987) mentions the following; I don't know what
931		   it means, but it works, and it appears necessary */
932		inb_p(video_port_status);
933		vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);
934		clear_attribs = true;
935	}
936	raw_spin_unlock_irq(&vga_lock);
937
938	if (clear_attribs) {
939		for (i = 0; i < MAX_NR_CONSOLES; i++) {
940			struct vc_data *c = vc_cons[i].d;
941			if (c && c->vc_sw == &vga_con) {
942				/* force hi font mask to 0, so we always clear
943				   the bit on either transition */
944				c->vc_hi_font_mask = 0x00;
945				clear_buffer_attributes(c);
946				c->vc_hi_font_mask = ch512 ? 0x0800 : 0;
947			}
948		}
949	}
950	return 0;
951}
952
953/*
954 * Adjust the screen to fit a font of a certain height
955 */
956static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
957{
958	unsigned char ovr, vde, fsr;
959	int rows, maxscan, i;
960
961	rows = vc->vc_scan_lines / fontheight;	/* Number of video rows we end up with */
962	maxscan = rows * fontheight - 1;	/* Scan lines to actually display-1 */
963
964	/* Reprogram the CRTC for the new font size
965	   Note: the attempt to read the overflow register will fail
966	   on an EGA, but using 0xff for the previous value appears to
967	   be OK for EGA text modes in the range 257-512 scan lines, so I
968	   guess we don't need to worry about it.
969
970	   The same applies for the spill bits in the font size and cursor
971	   registers; they are write-only on EGA, but it appears that they
972	   are all don't care bits on EGA, so I guess it doesn't matter. */
973
974	raw_spin_lock_irq(&vga_lock);
975	outb_p(0x07, vga_video_port_reg);	/* CRTC overflow register */
976	ovr = inb_p(vga_video_port_val);
977	outb_p(0x09, vga_video_port_reg);	/* Font size register */
978	fsr = inb_p(vga_video_port_val);
979	raw_spin_unlock_irq(&vga_lock);
980
981	vde = maxscan & 0xff;	/* Vertical display end reg */
982	ovr = (ovr & 0xbd) +	/* Overflow register */
983	    ((maxscan & 0x100) >> 7) + ((maxscan & 0x200) >> 3);
984	fsr = (fsr & 0xe0) + (fontheight - 1);	/*  Font size register */
985
986	raw_spin_lock_irq(&vga_lock);
987	outb_p(0x07, vga_video_port_reg);	/* CRTC overflow register */
988	outb_p(ovr, vga_video_port_val);
989	outb_p(0x09, vga_video_port_reg);	/* Font size */
990	outb_p(fsr, vga_video_port_val);
991	outb_p(0x12, vga_video_port_reg);	/* Vertical display limit */
992	outb_p(vde, vga_video_port_val);
993	raw_spin_unlock_irq(&vga_lock);
994	vga_video_font_height = fontheight;
995
996	for (i = 0; i < MAX_NR_CONSOLES; i++) {
997		struct vc_data *c = vc_cons[i].d;
998
999		if (c && c->vc_sw == &vga_con) {
1000			if (con_is_visible(c)) {
1001			        /* void size to cause regs to be rewritten */
1002				cursor_size_lastfrom = 0;
1003				cursor_size_lastto = 0;
1004				c->vc_sw->con_cursor(c, CM_DRAW);
1005			}
1006			c->vc_font.height = c->vc_cell_height = fontheight;
1007			vc_resize(c, 0, rows);	/* Adjust console size */
1008		}
1009	}
1010	return 0;
1011}
1012
1013static int vgacon_font_set(struct vc_data *c, struct console_font *font,
1014			   unsigned int vpitch, unsigned int flags)
1015{
1016	unsigned charcount = font->charcount;
1017	int rc;
1018
1019	if (vga_video_type < VIDEO_TYPE_EGAM)
1020		return -EINVAL;
1021
1022	if (font->width != VGA_FONTWIDTH || font->height > 32 || vpitch != 32 ||
1023	    (charcount != 256 && charcount != 512))
1024		return -EINVAL;
1025
1026	rc = vgacon_do_font_op(&vgastate, font->data, 1, charcount == 512);
1027	if (rc)
1028		return rc;
1029
1030	if (!(flags & KD_FONT_FLAG_DONT_RECALC))
1031		rc = vgacon_adjust_height(c, font->height);
1032	return rc;
1033}
1034
1035static int vgacon_font_get(struct vc_data *c, struct console_font *font, unsigned int vpitch)
1036{
1037	if (vga_video_type < VIDEO_TYPE_EGAM || vpitch != 32)
1038		return -EINVAL;
1039
1040	font->width = VGA_FONTWIDTH;
1041	font->height = c->vc_font.height;
1042	font->charcount = vga_512_chars ? 512 : 256;
1043	if (!font->data)
1044		return 0;
1045	return vgacon_do_font_op(&vgastate, font->data, 0, vga_512_chars);
1046}
1047
1048static int vgacon_resize(struct vc_data *c, unsigned int width,
1049			 unsigned int height, unsigned int user)
1050{
1051	if ((width << 1) * height > vga_vram_size)
1052		return -EINVAL;
1053
1054	if (user) {
1055		/*
1056		 * Ho ho!  Someone (svgatextmode, eh?) may have reprogrammed
1057		 * the video mode!  Set the new defaults then and go away.
1058		 */
1059		screen_info.orig_video_cols = width;
1060		screen_info.orig_video_lines = height;
1061		vga_default_font_height = c->vc_cell_height;
1062		return 0;
1063	}
1064	if (width % 2 || width > screen_info.orig_video_cols ||
1065	    height > (screen_info.orig_video_lines * vga_default_font_height)/
1066	    c->vc_cell_height)
1067		return -EINVAL;
1068
1069	if (con_is_visible(c) && !vga_is_gfx) /* who knows */
1070		vgacon_doresize(c, width, height);
1071	return 0;
1072}
1073
1074static int vgacon_set_origin(struct vc_data *c)
1075{
1076	if (vga_is_gfx ||	/* We don't play origin tricks in graphic modes */
1077	    (console_blanked && !vga_palette_blanked))	/* Nor we write to blanked screens */
1078		return 0;
1079	c->vc_origin = c->vc_visible_origin = vga_vram_base;
1080	vga_set_mem_top(c);
1081	vga_rolled_over = 0;
1082	return 1;
1083}
1084
1085static void vgacon_save_screen(struct vc_data *c)
1086{
1087	static int vga_bootup_console = 0;
1088
1089	if (!vga_bootup_console) {
1090		/* This is a gross hack, but here is the only place we can
1091		 * set bootup console parameters without messing up generic
1092		 * console initialization routines.
1093		 */
1094		vga_bootup_console = 1;
1095		c->state.x = screen_info.orig_x;
1096		c->state.y = screen_info.orig_y;
1097	}
1098
1099	/* We can't copy in more than the size of the video buffer,
1100	 * or we'll be copying in VGA BIOS */
1101
1102	if (!vga_is_gfx)
1103		scr_memcpyw((u16 *) c->vc_screenbuf, (u16 *) c->vc_origin,
1104			    c->vc_screenbuf_size > vga_vram_size ? vga_vram_size : c->vc_screenbuf_size);
1105}
1106
1107static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
1108		enum con_scroll dir, unsigned int lines)
1109{
1110	unsigned long oldo;
1111	unsigned int delta;
1112
1113	if (t || b != c->vc_rows || vga_is_gfx || c->vc_mode != KD_TEXT)
1114		return false;
1115
1116	if (!vga_hardscroll_enabled || lines >= c->vc_rows / 2)
1117		return false;
1118
1119	vgacon_restore_screen(c);
1120	oldo = c->vc_origin;
1121	delta = lines * c->vc_size_row;
1122	if (dir == SM_UP) {
1123		if (c->vc_scr_end + delta >= vga_vram_end) {
1124			scr_memcpyw((u16 *) vga_vram_base,
1125				    (u16 *) (oldo + delta),
1126				    c->vc_screenbuf_size - delta);
1127			c->vc_origin = vga_vram_base;
1128			vga_rolled_over = oldo - vga_vram_base;
1129		} else
1130			c->vc_origin += delta;
1131		scr_memsetw((u16 *) (c->vc_origin + c->vc_screenbuf_size -
1132				     delta), c->vc_video_erase_char,
1133			    delta);
1134	} else {
1135		if (oldo - delta < vga_vram_base) {
1136			scr_memmovew((u16 *) (vga_vram_end -
1137					      c->vc_screenbuf_size +
1138					      delta), (u16 *) oldo,
1139				     c->vc_screenbuf_size - delta);
1140			c->vc_origin = vga_vram_end - c->vc_screenbuf_size;
1141			vga_rolled_over = 0;
1142		} else
1143			c->vc_origin -= delta;
1144		c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
1145		scr_memsetw((u16 *) (c->vc_origin), c->vc_video_erase_char,
1146			    delta);
1147	}
1148	c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
1149	c->vc_visible_origin = c->vc_origin;
1150	vga_set_mem_top(c);
1151	c->vc_pos = (c->vc_pos - oldo) + c->vc_origin;
1152	return true;
1153}
1154
1155/*
1156 *  The console `switch' structure for the VGA based console
1157 */
1158
1159static void vgacon_clear(struct vc_data *vc, int sy, int sx, int height,
1160			 int width) { }
1161static void vgacon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
1162static void vgacon_putcs(struct vc_data *vc, const unsigned short *s,
1163			 int count, int ypos, int xpos) { }
1164
1165const struct consw vga_con = {
1166	.owner = THIS_MODULE,
1167	.con_startup = vgacon_startup,
1168	.con_init = vgacon_init,
1169	.con_deinit = vgacon_deinit,
1170	.con_clear = vgacon_clear,
1171	.con_putc = vgacon_putc,
1172	.con_putcs = vgacon_putcs,
1173	.con_cursor = vgacon_cursor,
1174	.con_scroll = vgacon_scroll,
1175	.con_switch = vgacon_switch,
1176	.con_blank = vgacon_blank,
1177	.con_font_set = vgacon_font_set,
1178	.con_font_get = vgacon_font_get,
1179	.con_resize = vgacon_resize,
1180	.con_set_palette = vgacon_set_palette,
1181	.con_scrolldelta = vgacon_scrolldelta,
1182	.con_set_origin = vgacon_set_origin,
1183	.con_save_screen = vgacon_save_screen,
1184	.con_build_attr = vgacon_build_attr,
1185	.con_invert_region = vgacon_invert_region,
1186};
1187EXPORT_SYMBOL(vga_con);
1188
1189MODULE_LICENSE("GPL");
1190