Linux-2.6.12-rc2
[cascardo/linux.git] / drivers / video / console / vgacon.c
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/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
56 static DEFINE_SPINLOCK(vga_lock);
57 static int cursor_size_lastfrom;
58 static int cursor_size_lastto;
59 static 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
77 static const char *vgacon_startup(void);
78 static void vgacon_init(struct vc_data *c, int init);
79 static void vgacon_deinit(struct vc_data *c);
80 static void vgacon_cursor(struct vc_data *c, int mode);
81 static int vgacon_switch(struct vc_data *c);
82 static int vgacon_blank(struct vc_data *c, int blank, int mode_switch);
83 static int vgacon_set_palette(struct vc_data *vc, unsigned char *table);
84 static int vgacon_scrolldelta(struct vc_data *c, int lines);
85 static int vgacon_set_origin(struct vc_data *c);
86 static void vgacon_save_screen(struct vc_data *c);
87 static int vgacon_scroll(struct vc_data *c, int t, int b, int dir,
88                          int lines);
89 static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity,
90                             u8 blink, u8 underline, u8 reverse);
91 static void vgacon_invert_region(struct vc_data *c, u16 * p, int count);
92 static unsigned long vgacon_uni_pagedir[2];
93
94
95 /* Description of the hardware situation */
96 static unsigned long    vga_vram_base;          /* Base of video memory */
97 static unsigned long    vga_vram_end;           /* End of video memory */
98 static u16              vga_video_port_reg;     /* Video register select port */
99 static u16              vga_video_port_val;     /* Video register value port */
100 static unsigned int     vga_video_num_columns;  /* Number of text columns */
101 static unsigned int     vga_video_num_lines;    /* Number of text lines */
102 static int              vga_can_do_color = 0;   /* Do we support colors? */
103 static unsigned int     vga_default_font_height;/* Height of default screen font */
104 static unsigned char    vga_video_type;         /* Card type */
105 static unsigned char    vga_hardscroll_enabled;
106 static unsigned char    vga_hardscroll_user_enable = 1;
107 static unsigned char    vga_font_is_default = 1;
108 static int              vga_vesa_blanked;
109 static int              vga_palette_blanked;
110 static int              vga_is_gfx;
111 static int              vga_512_chars;
112 static int              vga_video_font_height;
113 static int              vga_scan_lines;
114 static unsigned int     vga_rolled_over = 0;
115
116 static 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  */
136 static 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
161 static 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
329 static 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;
340         p = *c->vc_uni_pagedir_loc;
341         if (c->vc_uni_pagedir_loc == &c->vc_uni_pagedir ||
342             !--c->vc_uni_pagedir_loc[1])
343                 con_free_unimap(c);
344         c->vc_uni_pagedir_loc = vgacon_uni_pagedir;
345         vgacon_uni_pagedir[1]++;
346         if (!vgacon_uni_pagedir[0] && p)
347                 con_set_default_unimap(c);
348 }
349
350 static inline void vga_set_mem_top(struct vc_data *c)
351 {
352         write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2);
353 }
354
355 static void vgacon_deinit(struct vc_data *c)
356 {
357         /* When closing the last console, reset video origin */
358         if (!--vgacon_uni_pagedir[1]) {
359                 c->vc_visible_origin = vga_vram_base;
360                 vga_set_mem_top(c);
361                 con_free_unimap(c);
362         }
363         c->vc_uni_pagedir_loc = &c->vc_uni_pagedir;
364         con_set_default_unimap(c);
365 }
366
367 static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity,
368                             u8 blink, u8 underline, u8 reverse)
369 {
370         u8 attr = color;
371
372         if (vga_can_do_color) {
373                 if (underline)
374                         attr = (attr & 0xf0) | c->vc_ulcolor;
375                 else if (intensity == 0)
376                         attr = (attr & 0xf0) | c->vc_halfcolor;
377         }
378         if (reverse)
379                 attr =
380                     ((attr) & 0x88) | ((((attr) >> 4) | ((attr) << 4)) &
381                                        0x77);
382         if (blink)
383                 attr ^= 0x80;
384         if (intensity == 2)
385                 attr ^= 0x08;
386         if (!vga_can_do_color) {
387                 if (underline)
388                         attr = (attr & 0xf8) | 0x01;
389                 else if (intensity == 0)
390                         attr = (attr & 0xf0) | 0x08;
391         }
392         return attr;
393 }
394
395 static void vgacon_invert_region(struct vc_data *c, u16 * p, int count)
396 {
397         int col = vga_can_do_color;
398
399         while (count--) {
400                 u16 a = scr_readw(p);
401                 if (col)
402                         a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
403                             (((a) & 0x0700) << 4);
404                 else
405                         a ^= ((a & 0x0700) == 0x0100) ? 0x7000 : 0x7700;
406                 scr_writew(a, p++);
407         }
408 }
409
410 static void vgacon_set_cursor_size(int xpos, int from, int to)
411 {
412         unsigned long flags;
413         int curs, cure;
414
415 #ifdef TRIDENT_GLITCH
416         if (xpos < 16)
417                 from--, to--;
418 #endif
419
420         if ((from == cursor_size_lastfrom) && (to == cursor_size_lastto))
421                 return;
422         cursor_size_lastfrom = from;
423         cursor_size_lastto = to;
424
425         spin_lock_irqsave(&vga_lock, flags);
426         outb_p(0x0a, vga_video_port_reg);       /* Cursor start */
427         curs = inb_p(vga_video_port_val);
428         outb_p(0x0b, vga_video_port_reg);       /* Cursor end */
429         cure = inb_p(vga_video_port_val);
430
431         curs = (curs & 0xc0) | from;
432         cure = (cure & 0xe0) | to;
433
434         outb_p(0x0a, vga_video_port_reg);       /* Cursor start */
435         outb_p(curs, vga_video_port_val);
436         outb_p(0x0b, vga_video_port_reg);       /* Cursor end */
437         outb_p(cure, vga_video_port_val);
438         spin_unlock_irqrestore(&vga_lock, flags);
439 }
440
441 static void vgacon_cursor(struct vc_data *c, int mode)
442 {
443         if (c->vc_origin != c->vc_visible_origin)
444                 vgacon_scrolldelta(c, 0);
445         switch (mode) {
446         case CM_ERASE:
447                 write_vga(14, (vga_vram_end - vga_vram_base - 1) / 2);
448                 break;
449
450         case CM_MOVE:
451         case CM_DRAW:
452                 write_vga(14, (c->vc_pos - vga_vram_base) / 2);
453                 switch (c->vc_cursor_type & 0x0f) {
454                 case CUR_UNDERLINE:
455                         vgacon_set_cursor_size(c->vc_x,
456                                                c->vc_font.height -
457                                                (c->vc_font.height <
458                                                 10 ? 2 : 3),
459                                                c->vc_font.height -
460                                                (c->vc_font.height <
461                                                 10 ? 1 : 2));
462                         break;
463                 case CUR_TWO_THIRDS:
464                         vgacon_set_cursor_size(c->vc_x,
465                                                c->vc_font.height / 3,
466                                                c->vc_font.height -
467                                                (c->vc_font.height <
468                                                 10 ? 1 : 2));
469                         break;
470                 case CUR_LOWER_THIRD:
471                         vgacon_set_cursor_size(c->vc_x,
472                                                (c->vc_font.height * 2) / 3,
473                                                c->vc_font.height -
474                                                (c->vc_font.height <
475                                                 10 ? 1 : 2));
476                         break;
477                 case CUR_LOWER_HALF:
478                         vgacon_set_cursor_size(c->vc_x,
479                                                c->vc_font.height / 2,
480                                                c->vc_font.height -
481                                                (c->vc_font.height <
482                                                 10 ? 1 : 2));
483                         break;
484                 case CUR_NONE:
485                         vgacon_set_cursor_size(c->vc_x, 31, 30);
486                         break;
487                 default:
488                         vgacon_set_cursor_size(c->vc_x, 1,
489                                                c->vc_font.height);
490                         break;
491                 }
492                 break;
493         }
494 }
495
496 static int vgacon_switch(struct vc_data *c)
497 {
498         /*
499          * We need to save screen size here as it's the only way
500          * we can spot the screen has been resized and we need to
501          * set size of freshly allocated screens ourselves.
502          */
503         vga_video_num_columns = c->vc_cols;
504         vga_video_num_lines = c->vc_rows;
505         if (!vga_is_gfx)
506                 scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf,
507                             c->vc_screenbuf_size);
508         return 0;               /* Redrawing not needed */
509 }
510
511 static void vga_set_palette(struct vc_data *vc, unsigned char *table)
512 {
513         int i, j;
514
515         for (i = j = 0; i < 16; i++) {
516                 vga_w(state.vgabase, VGA_PEL_IW, table[i]);
517                 vga_w(state.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
518                 vga_w(state.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
519                 vga_w(state.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
520         }
521 }
522
523 static int vgacon_set_palette(struct vc_data *vc, unsigned char *table)
524 {
525 #ifdef CAN_LOAD_PALETTE
526         if (vga_video_type != VIDEO_TYPE_VGAC || vga_palette_blanked
527             || !CON_IS_VISIBLE(vc))
528                 return -EINVAL;
529         vga_set_palette(vc, table);
530         return 0;
531 #else
532         return -EINVAL;
533 #endif
534 }
535
536 /* structure holding original VGA register settings */
537 static struct {
538         unsigned char SeqCtrlIndex;     /* Sequencer Index reg.   */
539         unsigned char CrtCtrlIndex;     /* CRT-Contr. Index reg.  */
540         unsigned char CrtMiscIO;        /* Miscellaneous register */
541         unsigned char HorizontalTotal;  /* CRT-Controller:00h */
542         unsigned char HorizDisplayEnd;  /* CRT-Controller:01h */
543         unsigned char StartHorizRetrace;        /* CRT-Controller:04h */
544         unsigned char EndHorizRetrace;  /* CRT-Controller:05h */
545         unsigned char Overflow; /* CRT-Controller:07h */
546         unsigned char StartVertRetrace; /* CRT-Controller:10h */
547         unsigned char EndVertRetrace;   /* CRT-Controller:11h */
548         unsigned char ModeControl;      /* CRT-Controller:17h */
549         unsigned char ClockingMode;     /* Seq-Controller:01h */
550 } vga_state;
551
552 static void vga_vesa_blank(struct vgastate *state, int mode)
553 {
554         /* save original values of VGA controller registers */
555         if (!vga_vesa_blanked) {
556                 spin_lock_irq(&vga_lock);
557                 vga_state.SeqCtrlIndex = vga_r(state->vgabase, VGA_SEQ_I);
558                 vga_state.CrtCtrlIndex = inb_p(vga_video_port_reg);
559                 vga_state.CrtMiscIO = vga_r(state->vgabase, VGA_MIS_R);
560                 spin_unlock_irq(&vga_lock);
561
562                 outb_p(0x00, vga_video_port_reg);       /* HorizontalTotal */
563                 vga_state.HorizontalTotal = inb_p(vga_video_port_val);
564                 outb_p(0x01, vga_video_port_reg);       /* HorizDisplayEnd */
565                 vga_state.HorizDisplayEnd = inb_p(vga_video_port_val);
566                 outb_p(0x04, vga_video_port_reg);       /* StartHorizRetrace */
567                 vga_state.StartHorizRetrace = inb_p(vga_video_port_val);
568                 outb_p(0x05, vga_video_port_reg);       /* EndHorizRetrace */
569                 vga_state.EndHorizRetrace = inb_p(vga_video_port_val);
570                 outb_p(0x07, vga_video_port_reg);       /* Overflow */
571                 vga_state.Overflow = inb_p(vga_video_port_val);
572                 outb_p(0x10, vga_video_port_reg);       /* StartVertRetrace */
573                 vga_state.StartVertRetrace = inb_p(vga_video_port_val);
574                 outb_p(0x11, vga_video_port_reg);       /* EndVertRetrace */
575                 vga_state.EndVertRetrace = inb_p(vga_video_port_val);
576                 outb_p(0x17, vga_video_port_reg);       /* ModeControl */
577                 vga_state.ModeControl = inb_p(vga_video_port_val);
578                 vga_state.ClockingMode = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE);
579         }
580
581         /* assure that video is enabled */
582         /* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
583         spin_lock_irq(&vga_lock);
584         vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, vga_state.ClockingMode | 0x20);
585
586         /* test for vertical retrace in process.... */
587         if ((vga_state.CrtMiscIO & 0x80) == 0x80)
588                 vga_w(state->vgabase, VGA_MIS_W, vga_state.CrtMiscIO & 0xEF);
589
590         /*
591          * Set <End of vertical retrace> to minimum (0) and
592          * <Start of vertical Retrace> to maximum (incl. overflow)
593          * Result: turn off vertical sync (VSync) pulse.
594          */
595         if (mode & VESA_VSYNC_SUSPEND) {
596                 outb_p(0x10, vga_video_port_reg);       /* StartVertRetrace */
597                 outb_p(0xff, vga_video_port_val);       /* maximum value */
598                 outb_p(0x11, vga_video_port_reg);       /* EndVertRetrace */
599                 outb_p(0x40, vga_video_port_val);       /* minimum (bits 0..3)  */
600                 outb_p(0x07, vga_video_port_reg);       /* Overflow */
601                 outb_p(vga_state.Overflow | 0x84, vga_video_port_val);  /* bits 9,10 of vert. retrace */
602         }
603
604         if (mode & VESA_HSYNC_SUSPEND) {
605                 /*
606                  * Set <End of horizontal retrace> to minimum (0) and
607                  *  <Start of horizontal Retrace> to maximum
608                  * Result: turn off horizontal sync (HSync) pulse.
609                  */
610                 outb_p(0x04, vga_video_port_reg);       /* StartHorizRetrace */
611                 outb_p(0xff, vga_video_port_val);       /* maximum */
612                 outb_p(0x05, vga_video_port_reg);       /* EndHorizRetrace */
613                 outb_p(0x00, vga_video_port_val);       /* minimum (0) */
614         }
615
616         /* restore both index registers */
617         vga_w(state->vgabase, VGA_SEQ_I, vga_state.SeqCtrlIndex);
618         outb_p(vga_state.CrtCtrlIndex, vga_video_port_reg);
619         spin_unlock_irq(&vga_lock);
620 }
621
622 static void vga_vesa_unblank(struct vgastate *state)
623 {
624         /* restore original values of VGA controller registers */
625         spin_lock_irq(&vga_lock);
626         vga_w(state->vgabase, VGA_MIS_W, vga_state.CrtMiscIO);
627
628         outb_p(0x00, vga_video_port_reg);       /* HorizontalTotal */
629         outb_p(vga_state.HorizontalTotal, vga_video_port_val);
630         outb_p(0x01, vga_video_port_reg);       /* HorizDisplayEnd */
631         outb_p(vga_state.HorizDisplayEnd, vga_video_port_val);
632         outb_p(0x04, vga_video_port_reg);       /* StartHorizRetrace */
633         outb_p(vga_state.StartHorizRetrace, vga_video_port_val);
634         outb_p(0x05, vga_video_port_reg);       /* EndHorizRetrace */
635         outb_p(vga_state.EndHorizRetrace, vga_video_port_val);
636         outb_p(0x07, vga_video_port_reg);       /* Overflow */
637         outb_p(vga_state.Overflow, vga_video_port_val);
638         outb_p(0x10, vga_video_port_reg);       /* StartVertRetrace */
639         outb_p(vga_state.StartVertRetrace, vga_video_port_val);
640         outb_p(0x11, vga_video_port_reg);       /* EndVertRetrace */
641         outb_p(vga_state.EndVertRetrace, vga_video_port_val);
642         outb_p(0x17, vga_video_port_reg);       /* ModeControl */
643         outb_p(vga_state.ModeControl, vga_video_port_val);
644         /* ClockingMode */
645         vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, vga_state.ClockingMode);
646
647         /* restore index/control registers */
648         vga_w(state->vgabase, VGA_SEQ_I, vga_state.SeqCtrlIndex);
649         outb_p(vga_state.CrtCtrlIndex, vga_video_port_reg);
650         spin_unlock_irq(&vga_lock);
651 }
652
653 static void vga_pal_blank(struct vgastate *state)
654 {
655         int i;
656
657         for (i = 0; i < 16; i++) {
658                 vga_w(state->vgabase, VGA_PEL_IW, i);
659                 vga_w(state->vgabase, VGA_PEL_D, 0);
660                 vga_w(state->vgabase, VGA_PEL_D, 0);
661                 vga_w(state->vgabase, VGA_PEL_D, 0);
662         }
663 }
664
665 static int vgacon_blank(struct vc_data *c, int blank, int mode_switch)
666 {
667         switch (blank) {
668         case 0:         /* Unblank */
669                 if (vga_vesa_blanked) {
670                         vga_vesa_unblank(&state);
671                         vga_vesa_blanked = 0;
672                 }
673                 if (vga_palette_blanked) {
674                         vga_set_palette(c, color_table);
675                         vga_palette_blanked = 0;
676                         return 0;
677                 }
678                 vga_is_gfx = 0;
679                 /* Tell console.c that it has to restore the screen itself */
680                 return 1;
681         case 1:         /* Normal blanking */
682         case -1:        /* Obsolete */
683                 if (!mode_switch && vga_video_type == VIDEO_TYPE_VGAC) {
684                         vga_pal_blank(&state);
685                         vga_palette_blanked = 1;
686                         return 0;
687                 }
688                 vgacon_set_origin(c);
689                 scr_memsetw((void *) vga_vram_base, BLANK,
690                             c->vc_screenbuf_size);
691                 if (mode_switch)
692                         vga_is_gfx = 1;
693                 return 1;
694         default:                /* VESA blanking */
695                 if (vga_video_type == VIDEO_TYPE_VGAC) {
696                         vga_vesa_blank(&state, blank - 1);
697                         vga_vesa_blanked = blank;
698                 }
699                 return 0;
700         }
701 }
702
703 /*
704  * PIO_FONT support.
705  *
706  * The font loading code goes back to the codepage package by
707  * Joel Hoffman (joel@wam.umd.edu). (He reports that the original
708  * reference is: "From: p. 307 of _Programmer's Guide to PC & PS/2
709  * Video Systems_ by Richard Wilton. 1987.  Microsoft Press".)
710  *
711  * Change for certain monochrome monitors by Yury Shevchuck
712  * (sizif@botik.yaroslavl.su).
713  */
714
715 #ifdef CAN_LOAD_EGA_FONTS
716
717 #define colourmap 0xa0000
718 /* Pauline Middelink <middelin@polyware.iaf.nl> reports that we
719    should use 0xA0000 for the bwmap as well.. */
720 #define blackwmap 0xa0000
721 #define cmapsz 8192
722
723 static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
724 {
725         unsigned short video_port_status = vga_video_port_reg + 6;
726         int font_select = 0x00, beg, i;
727         char *charmap;
728         
729         if (vga_video_type != VIDEO_TYPE_EGAM) {
730                 charmap = (char *) VGA_MAP_MEM(colourmap);
731                 beg = 0x0e;
732 #ifdef VGA_CAN_DO_64KB
733                 if (vga_video_type == VIDEO_TYPE_VGAC)
734                         beg = 0x06;
735 #endif
736         } else {
737                 charmap = (char *) VGA_MAP_MEM(blackwmap);
738                 beg = 0x0a;
739         }
740
741 #ifdef BROKEN_GRAPHICS_PROGRAMS
742         /*
743          * All fonts are loaded in slot 0 (0:1 for 512 ch)
744          */
745
746         if (!arg)
747                 return -EINVAL; /* Return to default font not supported */
748
749         vga_font_is_default = 0;
750         font_select = ch512 ? 0x04 : 0x00;
751 #else
752         /*
753          * The default font is kept in slot 0 and is never touched.
754          * A custom font is loaded in slot 2 (256 ch) or 2:3 (512 ch)
755          */
756
757         if (set) {
758                 vga_font_is_default = !arg;
759                 if (!arg)
760                         ch512 = 0;      /* Default font is always 256 */
761                 font_select = arg ? (ch512 ? 0x0e : 0x0a) : 0x00;
762         }
763
764         if (!vga_font_is_default)
765                 charmap += 4 * cmapsz;
766 #endif
767
768         unlock_kernel();
769         spin_lock_irq(&vga_lock);
770         /* First, the Sequencer */
771         vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
772         /* CPU writes only to map 2 */
773         vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x04);    
774         /* Sequential addressing */
775         vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x07);    
776         /* Clear synchronous reset */
777         vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03);
778
779         /* Now, the graphics controller, select map 2 */
780         vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x02);             
781         /* disable odd-even addressing */
782         vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x00);
783         /* map start at A000:0000 */
784         vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x00);
785         spin_unlock_irq(&vga_lock);
786
787         if (arg) {
788                 if (set)
789                         for (i = 0; i < cmapsz; i++)
790                                 vga_writeb(arg[i], charmap + i);
791                 else
792                         for (i = 0; i < cmapsz; i++)
793                                 arg[i] = vga_readb(charmap + i);
794
795                 /*
796                  * In 512-character mode, the character map is not contiguous if
797                  * we want to remain EGA compatible -- which we do
798                  */
799
800                 if (ch512) {
801                         charmap += 2 * cmapsz;
802                         arg += cmapsz;
803                         if (set)
804                                 for (i = 0; i < cmapsz; i++)
805                                         vga_writeb(arg[i], charmap + i);
806                         else
807                                 for (i = 0; i < cmapsz; i++)
808                                         arg[i] = vga_readb(charmap + i);
809                 }
810         }
811
812         spin_lock_irq(&vga_lock);
813         /* First, the sequencer, Synchronous reset */
814         vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x01);  
815         /* CPU writes to maps 0 and 1 */
816         vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x03);
817         /* odd-even addressing */
818         vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x03);
819         /* Character Map Select */
820         if (set)
821                 vga_wseq(state->vgabase, VGA_SEQ_CHARACTER_MAP, font_select);
822         /* clear synchronous reset */
823         vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03);
824
825         /* Now, the graphics controller, select map 0 for CPU */
826         vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x00);
827         /* enable even-odd addressing */
828         vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x10);
829         /* map starts at b800:0 or b000:0 */
830         vga_wgfx(state->vgabase, VGA_GFX_MISC, beg);
831
832         /* if 512 char mode is already enabled don't re-enable it. */
833         if ((set) && (ch512 != vga_512_chars)) {
834                 int i;  
835                 
836                 /* attribute controller */
837                 for (i = 0; i < MAX_NR_CONSOLES; i++) {
838                         struct vc_data *c = vc_cons[i].d;
839                         if (c && c->vc_sw == &vga_con)
840                                 c->vc_hi_font_mask = ch512 ? 0x0800 : 0;
841                 }
842                 vga_512_chars = ch512;
843                 /* 256-char: enable intensity bit
844                    512-char: disable intensity bit */
845                 inb_p(video_port_status);       /* clear address flip-flop */
846                 /* color plane enable register */
847                 vga_wattr(state->vgabase, VGA_ATC_PLANE_ENABLE, ch512 ? 0x07 : 0x0f);
848                 /* Wilton (1987) mentions the following; I don't know what
849                    it means, but it works, and it appears necessary */
850                 inb_p(video_port_status);
851                 vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);    
852         }
853         spin_unlock_irq(&vga_lock);
854         lock_kernel();
855         return 0;
856 }
857
858 /*
859  * Adjust the screen to fit a font of a certain height
860  */
861 static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
862 {
863         unsigned char ovr, vde, fsr;
864         int rows, maxscan, i;
865
866         rows = vc->vc_scan_lines / fontheight;  /* Number of video rows we end up with */
867         maxscan = rows * fontheight - 1;        /* Scan lines to actually display-1 */
868
869         /* Reprogram the CRTC for the new font size
870            Note: the attempt to read the overflow register will fail
871            on an EGA, but using 0xff for the previous value appears to
872            be OK for EGA text modes in the range 257-512 scan lines, so I
873            guess we don't need to worry about it.
874
875            The same applies for the spill bits in the font size and cursor
876            registers; they are write-only on EGA, but it appears that they
877            are all don't care bits on EGA, so I guess it doesn't matter. */
878
879         spin_lock_irq(&vga_lock);
880         outb_p(0x07, vga_video_port_reg);       /* CRTC overflow register */
881         ovr = inb_p(vga_video_port_val);
882         outb_p(0x09, vga_video_port_reg);       /* Font size register */
883         fsr = inb_p(vga_video_port_val);
884         spin_unlock_irq(&vga_lock);
885
886         vde = maxscan & 0xff;   /* Vertical display end reg */
887         ovr = (ovr & 0xbd) +    /* Overflow register */
888             ((maxscan & 0x100) >> 7) + ((maxscan & 0x200) >> 3);
889         fsr = (fsr & 0xe0) + (fontheight - 1);  /*  Font size register */
890
891         spin_lock_irq(&vga_lock);
892         outb_p(0x07, vga_video_port_reg);       /* CRTC overflow register */
893         outb_p(ovr, vga_video_port_val);
894         outb_p(0x09, vga_video_port_reg);       /* Font size */
895         outb_p(fsr, vga_video_port_val);
896         outb_p(0x12, vga_video_port_reg);       /* Vertical display limit */
897         outb_p(vde, vga_video_port_val);
898         spin_unlock_irq(&vga_lock);
899
900         for (i = 0; i < MAX_NR_CONSOLES; i++) {
901                 struct vc_data *c = vc_cons[i].d;
902
903                 if (c && c->vc_sw == &vga_con) {
904                         if (CON_IS_VISIBLE(c)) {
905                                 /* void size to cause regs to be rewritten */
906                                 cursor_size_lastfrom = 0;
907                                 cursor_size_lastto = 0;
908                                 c->vc_sw->con_cursor(c, CM_DRAW);
909                         }
910                         c->vc_font.height = fontheight;
911                         vc_resize(c, 0, rows);  /* Adjust console size */
912                 }
913         }
914         return 0;
915 }
916
917 static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigned flags)
918 {
919         unsigned charcount = font->charcount;
920         int rc;
921
922         if (vga_video_type < VIDEO_TYPE_EGAM)
923                 return -EINVAL;
924
925         if (font->width != 8 || (charcount != 256 && charcount != 512))
926                 return -EINVAL;
927
928         rc = vgacon_do_font_op(&state, font->data, 1, charcount == 512);
929         if (rc)
930                 return rc;
931
932         if (!(flags & KD_FONT_FLAG_DONT_RECALC))
933                 rc = vgacon_adjust_height(c, font->height);
934         return rc;
935 }
936
937 static int vgacon_font_get(struct vc_data *c, struct console_font *font)
938 {
939         if (vga_video_type < VIDEO_TYPE_EGAM)
940                 return -EINVAL;
941
942         font->width = 8;
943         font->height = c->vc_font.height;
944         font->charcount = vga_512_chars ? 512 : 256;
945         if (!font->data)
946                 return 0;
947         return vgacon_do_font_op(&state, font->data, 0, 0);
948 }
949
950 #else
951
952 #define vgacon_font_set NULL
953 #define vgacon_font_get NULL
954
955 #endif
956
957 static int vgacon_scrolldelta(struct vc_data *c, int lines)
958 {
959         if (!lines)             /* Turn scrollback off */
960                 c->vc_visible_origin = c->vc_origin;
961         else {
962                 int vram_size = vga_vram_end - vga_vram_base;
963                 int margin = c->vc_size_row * 4;
964                 int ul, we, p, st;
965
966                 if (vga_rolled_over >
967                     (c->vc_scr_end - vga_vram_base) + margin) {
968                         ul = c->vc_scr_end - vga_vram_base;
969                         we = vga_rolled_over + c->vc_size_row;
970                 } else {
971                         ul = 0;
972                         we = vram_size;
973                 }
974                 p = (c->vc_visible_origin - vga_vram_base - ul + we) % we +
975                     lines * c->vc_size_row;
976                 st = (c->vc_origin - vga_vram_base - ul + we) % we;
977                 if (st < 2 * margin)
978                         margin = 0;
979                 if (p < margin)
980                         p = 0;
981                 if (p > st - margin)
982                         p = st;
983                 c->vc_visible_origin = vga_vram_base + (p + ul) % we;
984         }
985         vga_set_mem_top(c);
986         return 1;
987 }
988
989 static int vgacon_set_origin(struct vc_data *c)
990 {
991         if (vga_is_gfx ||       /* We don't play origin tricks in graphic modes */
992             (console_blanked && !vga_palette_blanked))  /* Nor we write to blanked screens */
993                 return 0;
994         c->vc_origin = c->vc_visible_origin = vga_vram_base;
995         vga_set_mem_top(c);
996         vga_rolled_over = 0;
997         return 1;
998 }
999
1000 static void vgacon_save_screen(struct vc_data *c)
1001 {
1002         static int vga_bootup_console = 0;
1003
1004         if (!vga_bootup_console) {
1005                 /* This is a gross hack, but here is the only place we can
1006                  * set bootup console parameters without messing up generic
1007                  * console initialization routines.
1008                  */
1009                 vga_bootup_console = 1;
1010                 c->vc_x = ORIG_X;
1011                 c->vc_y = ORIG_Y;
1012         }
1013         if (!vga_is_gfx)
1014                 scr_memcpyw((u16 *) c->vc_screenbuf, (u16 *) c->vc_origin,
1015                             c->vc_screenbuf_size);
1016 }
1017
1018 static int vgacon_scroll(struct vc_data *c, int t, int b, int dir,
1019                          int lines)
1020 {
1021         unsigned long oldo;
1022         unsigned int delta;
1023
1024         if (t || b != c->vc_rows || vga_is_gfx)
1025                 return 0;
1026
1027         if (c->vc_origin != c->vc_visible_origin)
1028                 vgacon_scrolldelta(c, 0);
1029
1030         if (!vga_hardscroll_enabled || lines >= c->vc_rows / 2)
1031                 return 0;
1032
1033         oldo = c->vc_origin;
1034         delta = lines * c->vc_size_row;
1035         if (dir == SM_UP) {
1036                 if (c->vc_scr_end + delta >= vga_vram_end) {
1037                         scr_memcpyw((u16 *) vga_vram_base,
1038                                     (u16 *) (oldo + delta),
1039                                     c->vc_screenbuf_size - delta);
1040                         c->vc_origin = vga_vram_base;
1041                         vga_rolled_over = oldo - vga_vram_base;
1042                 } else
1043                         c->vc_origin += delta;
1044                 scr_memsetw((u16 *) (c->vc_origin + c->vc_screenbuf_size -
1045                                      delta), c->vc_video_erase_char,
1046                             delta);
1047         } else {
1048                 if (oldo - delta < vga_vram_base) {
1049                         scr_memmovew((u16 *) (vga_vram_end -
1050                                               c->vc_screenbuf_size +
1051                                               delta), (u16 *) oldo,
1052                                      c->vc_screenbuf_size - delta);
1053                         c->vc_origin = vga_vram_end - c->vc_screenbuf_size;
1054                         vga_rolled_over = 0;
1055                 } else
1056                         c->vc_origin -= delta;
1057                 c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
1058                 scr_memsetw((u16 *) (c->vc_origin), c->vc_video_erase_char,
1059                             delta);
1060         }
1061         c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
1062         c->vc_visible_origin = c->vc_origin;
1063         vga_set_mem_top(c);
1064         c->vc_pos = (c->vc_pos - oldo) + c->vc_origin;
1065         return 1;
1066 }
1067
1068
1069 /*
1070  *  The console `switch' structure for the VGA based console
1071  */
1072
1073 static int vgacon_dummy(struct vc_data *c)
1074 {
1075         return 0;
1076 }
1077
1078 #define DUMMY (void *) vgacon_dummy
1079
1080 const struct consw vga_con = {
1081         .owner = THIS_MODULE,
1082         .con_startup = vgacon_startup,
1083         .con_init = vgacon_init,
1084         .con_deinit = vgacon_deinit,
1085         .con_clear = DUMMY,
1086         .con_putc = DUMMY,
1087         .con_putcs = DUMMY,
1088         .con_cursor = vgacon_cursor,
1089         .con_scroll = vgacon_scroll,
1090         .con_bmove = DUMMY,
1091         .con_switch = vgacon_switch,
1092         .con_blank = vgacon_blank,
1093         .con_font_set = vgacon_font_set,
1094         .con_font_get = vgacon_font_get,
1095         .con_set_palette = vgacon_set_palette,
1096         .con_scrolldelta = vgacon_scrolldelta,
1097         .con_set_origin = vgacon_set_origin,
1098         .con_save_screen = vgacon_save_screen,
1099         .con_build_attr = vgacon_build_attr,
1100         .con_invert_region = vgacon_invert_region,
1101 };
1102
1103 MODULE_LICENSE("GPL");