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