vserver 2.0 rc7
[linux-2.6.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         if (vga_512_chars)
341                 c->vc_hi_font_mask = 0x0800;
342         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
352 static 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
357 static 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
369 static 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
397 static 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
412 static 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
443 static 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
498 static 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
513 static 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
525 static 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 */
539 static 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
554 static 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
624 static 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
655 static 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
667 static 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
725 static 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  */
863 static 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
919 static 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
939 static 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
959 static 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
991 static 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
1002 static 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
1020 static 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
1075 static int vgacon_dummy(struct vc_data *c)
1076 {
1077         return 0;
1078 }
1079
1080 #define DUMMY (void *) vgacon_dummy
1081
1082 const 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
1105 MODULE_LICENSE("GPL");