1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36#include <linux/module.h>
37#include <linux/types.h>
38#include <linux/fs.h>
39#include <linux/kernel.h>
40#include <linux/console.h>
41#include <linux/string.h>
42#include <linux/kd.h>
43#include <linux/slab.h>
44#include <linux/vt_kern.h>
45#include <linux/sched.h>
46#include <linux/selection.h>
47#include <linux/spinlock.h>
48#include <linux/ioport.h>
49#include <linux/init.h>
50#include <linux/screen_info.h>
51#include <video/vga.h>
52#include <asm/io.h>
53
54static DEFINE_RAW_SPINLOCK(vga_lock);
55static int cursor_size_lastfrom;
56static int cursor_size_lastto;
57static u32 vgacon_xres;
58static u32 vgacon_yres;
59static struct vgastate vgastate;
60
61#define BLANK 0x0020
62
63#define VGA_FONTWIDTH 8
64
65
66
67
68static const char *vgacon_startup(void);
69static void vgacon_init(struct vc_data *c, int init);
70static void vgacon_deinit(struct vc_data *c);
71static void vgacon_cursor(struct vc_data *c, int mode);
72static int vgacon_switch(struct vc_data *c);
73static int vgacon_blank(struct vc_data *c, int blank, int mode_switch);
74static void vgacon_scrolldelta(struct vc_data *c, int lines);
75static int vgacon_set_origin(struct vc_data *c);
76static void vgacon_save_screen(struct vc_data *c);
77static void vgacon_invert_region(struct vc_data *c, u16 * p, int count);
78static struct uni_pagedir *vgacon_uni_pagedir;
79static int vgacon_refcount;
80
81
82static bool vga_init_done;
83static unsigned long vga_vram_base __read_mostly;
84static unsigned long vga_vram_end __read_mostly;
85static unsigned int vga_vram_size __read_mostly;
86static u16 vga_video_port_reg __read_mostly;
87static u16 vga_video_port_val __read_mostly;
88static unsigned int vga_video_num_columns;
89static unsigned int vga_video_num_lines;
90static bool vga_can_do_color;
91static unsigned int vga_default_font_height __read_mostly;
92static unsigned char vga_video_type __read_mostly;
93static bool vga_font_is_default = true;
94static int vga_vesa_blanked;
95static bool vga_palette_blanked;
96static bool vga_is_gfx;
97static bool vga_512_chars;
98static int vga_video_font_height;
99static int vga_scan_lines __read_mostly;
100static unsigned int vga_rolled_over;
101
102static bool vgacon_text_mode_force;
103static bool vga_hardscroll_enabled;
104static bool vga_hardscroll_user_enable = true;
105
106bool vgacon_text_force(void)
107{
108 return vgacon_text_mode_force;
109}
110EXPORT_SYMBOL(vgacon_text_force);
111
112static int __init text_mode(char *str)
113{
114 vgacon_text_mode_force = true;
115 return 1;
116}
117
118
119__setup("nomodeset", text_mode);
120
121static int __init no_scroll(char *str)
122{
123
124
125
126
127
128 vga_hardscroll_user_enable = vga_hardscroll_enabled = false;
129 return 1;
130}
131
132__setup("no-scroll", no_scroll);
133
134
135
136
137
138
139
140
141static inline void write_vga(unsigned char reg, unsigned int val)
142{
143 unsigned int v1, v2;
144 unsigned long flags;
145
146
147
148
149
150 raw_spin_lock_irqsave(&vga_lock, flags);
151 v1 = reg + (val & 0xff00);
152 v2 = reg + 1 + ((val << 8) & 0xff00);
153 outw(v1, vga_video_port_reg);
154 outw(v2, vga_video_port_reg);
155 raw_spin_unlock_irqrestore(&vga_lock, flags);
156}
157
158static inline void vga_set_mem_top(struct vc_data *c)
159{
160 write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2);
161}
162
163#ifdef CONFIG_VGACON_SOFT_SCROLLBACK
164
165struct vgacon_scrollback_info {
166 void *data;
167 int tail;
168 int size;
169 int rows;
170 int cnt;
171 int cur;
172 int save;
173 int restore;
174};
175
176static struct vgacon_scrollback_info *vgacon_scrollback_cur;
177static struct vgacon_scrollback_info vgacon_scrollbacks[MAX_NR_CONSOLES];
178static bool scrollback_persistent = \
179 IS_ENABLED(CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT);
180module_param_named(scrollback_persistent, scrollback_persistent, bool, 0000);
181MODULE_PARM_DESC(scrollback_persistent, "Enable persistent scrollback for all vga consoles");
182
183static void vgacon_scrollback_reset(int vc_num, size_t reset_size)
184{
185 struct vgacon_scrollback_info *scrollback = &vgacon_scrollbacks[vc_num];
186
187 if (scrollback->data && reset_size > 0)
188 memset(scrollback->data, 0, reset_size);
189
190 scrollback->cnt = 0;
191 scrollback->tail = 0;
192 scrollback->cur = 0;
193}
194
195static void vgacon_scrollback_init(int vc_num)
196{
197 int pitch = vga_video_num_columns * 2;
198 size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024;
199 int rows = size / pitch;
200 void *data;
201
202 data = kmalloc_array(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE, 1024,
203 GFP_NOWAIT);
204
205 vgacon_scrollbacks[vc_num].data = data;
206 vgacon_scrollback_cur = &vgacon_scrollbacks[vc_num];
207
208 vgacon_scrollback_cur->rows = rows - 1;
209 vgacon_scrollback_cur->size = rows * pitch;
210
211 vgacon_scrollback_reset(vc_num, size);
212}
213
214static void vgacon_scrollback_switch(int vc_num)
215{
216 if (!scrollback_persistent)
217 vc_num = 0;
218
219 if (!vgacon_scrollbacks[vc_num].data) {
220 vgacon_scrollback_init(vc_num);
221 } else {
222 if (scrollback_persistent) {
223 vgacon_scrollback_cur = &vgacon_scrollbacks[vc_num];
224 } else {
225 size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024;
226
227 vgacon_scrollback_reset(vc_num, size);
228 }
229 }
230}
231
232static void vgacon_scrollback_startup(void)
233{
234 vgacon_scrollback_cur = &vgacon_scrollbacks[0];
235 vgacon_scrollback_init(0);
236}
237
238static void vgacon_scrollback_update(struct vc_data *c, int t, int count)
239{
240 void *p;
241
242 if (!vgacon_scrollback_cur->data || !vgacon_scrollback_cur->size ||
243 c->vc_num != fg_console)
244 return;
245
246 p = (void *) (c->vc_origin + t * c->vc_size_row);
247
248 while (count--) {
249 scr_memcpyw(vgacon_scrollback_cur->data +
250 vgacon_scrollback_cur->tail,
251 p, c->vc_size_row);
252
253 vgacon_scrollback_cur->cnt++;
254 p += c->vc_size_row;
255 vgacon_scrollback_cur->tail += c->vc_size_row;
256
257 if (vgacon_scrollback_cur->tail >= vgacon_scrollback_cur->size)
258 vgacon_scrollback_cur->tail = 0;
259
260 if (vgacon_scrollback_cur->cnt > vgacon_scrollback_cur->rows)
261 vgacon_scrollback_cur->cnt = vgacon_scrollback_cur->rows;
262
263 vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt;
264 }
265}
266
267static void vgacon_restore_screen(struct vc_data *c)
268{
269 vgacon_scrollback_cur->save = 0;
270
271 if (!vga_is_gfx && !vgacon_scrollback_cur->restore) {
272 scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf,
273 c->vc_screenbuf_size > vga_vram_size ?
274 vga_vram_size : c->vc_screenbuf_size);
275 vgacon_scrollback_cur->restore = 1;
276 vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt;
277 }
278}
279
280static void vgacon_scrolldelta(struct vc_data *c, int lines)
281{
282 int start, end, count, soff;
283
284 if (!lines) {
285 c->vc_visible_origin = c->vc_origin;
286 vga_set_mem_top(c);
287 return;
288 }
289
290 if (!vgacon_scrollback_cur->data)
291 return;
292
293 if (!vgacon_scrollback_cur->save) {
294 vgacon_cursor(c, CM_ERASE);
295 vgacon_save_screen(c);
296 vgacon_scrollback_cur->save = 1;
297 }
298
299 vgacon_scrollback_cur->restore = 0;
300 start = vgacon_scrollback_cur->cur + lines;
301 end = start + abs(lines);
302
303 if (start < 0)
304 start = 0;
305
306 if (start > vgacon_scrollback_cur->cnt)
307 start = vgacon_scrollback_cur->cnt;
308
309 if (end < 0)
310 end = 0;
311
312 if (end > vgacon_scrollback_cur->cnt)
313 end = vgacon_scrollback_cur->cnt;
314
315 vgacon_scrollback_cur->cur = start;
316 count = end - start;
317 soff = vgacon_scrollback_cur->tail -
318 ((vgacon_scrollback_cur->cnt - end) * c->vc_size_row);
319 soff -= count * c->vc_size_row;
320
321 if (soff < 0)
322 soff += vgacon_scrollback_cur->size;
323
324 count = vgacon_scrollback_cur->cnt - start;
325
326 if (count > c->vc_rows)
327 count = c->vc_rows;
328
329 if (count) {
330 int copysize;
331
332 int diff = c->vc_rows - count;
333 void *d = (void *) c->vc_origin;
334 void *s = (void *) c->vc_screenbuf;
335
336 count *= c->vc_size_row;
337
338 copysize = min(count, vgacon_scrollback_cur->size - soff);
339 scr_memcpyw(d, vgacon_scrollback_cur->data + soff, copysize);
340 d += copysize;
341 count -= copysize;
342
343 if (count) {
344 scr_memcpyw(d, vgacon_scrollback_cur->data, count);
345 d += count;
346 }
347
348 if (diff)
349 scr_memcpyw(d, s, diff * c->vc_size_row);
350 } else
351 vgacon_cursor(c, CM_MOVE);
352}
353
354static void vgacon_flush_scrollback(struct vc_data *c)
355{
356 size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024;
357
358 vgacon_scrollback_reset(c->vc_num, size);
359}
360#else
361#define vgacon_scrollback_startup(...) do { } while (0)
362#define vgacon_scrollback_init(...) do { } while (0)
363#define vgacon_scrollback_update(...) do { } while (0)
364#define vgacon_scrollback_switch(...) do { } while (0)
365
366static void vgacon_restore_screen(struct vc_data *c)
367{
368 if (c->vc_origin != c->vc_visible_origin)
369 vgacon_scrolldelta(c, 0);
370}
371
372static void vgacon_scrolldelta(struct vc_data *c, int lines)
373{
374 vc_scrolldelta_helper(c, lines, vga_rolled_over, (void *)vga_vram_base,
375 vga_vram_size);
376 vga_set_mem_top(c);
377}
378
379static void vgacon_flush_scrollback(struct vc_data *c)
380{
381}
382#endif
383
384static const char *vgacon_startup(void)
385{
386 const char *display_desc = NULL;
387 u16 saved1, saved2;
388 volatile u16 *p;
389
390 if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB ||
391 screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) {
392 no_vga:
393#ifdef CONFIG_DUMMY_CONSOLE
394 conswitchp = &dummy_con;
395 return conswitchp->con_startup();
396#else
397 return NULL;
398#endif
399 }
400
401
402 if ((screen_info.orig_video_lines == 0) ||
403 (screen_info.orig_video_cols == 0))
404 goto no_vga;
405
406
407 if ((screen_info.orig_video_mode == 0x0D) ||
408 (screen_info.orig_video_mode == 0x0E) ||
409 (screen_info.orig_video_mode == 0x10) ||
410 (screen_info.orig_video_mode == 0x12) ||
411 (screen_info.orig_video_mode == 0x6A))
412 goto no_vga;
413
414 vga_video_num_lines = screen_info.orig_video_lines;
415 vga_video_num_columns = screen_info.orig_video_cols;
416 vgastate.vgabase = NULL;
417
418 if (screen_info.orig_video_mode == 7) {
419
420 vga_vram_base = 0xb0000;
421 vga_video_port_reg = VGA_CRT_IM;
422 vga_video_port_val = VGA_CRT_DM;
423 if ((screen_info.orig_video_ega_bx & 0xff) != 0x10) {
424 static struct resource ega_console_resource =
425 { .name = "ega",
426 .flags = IORESOURCE_IO,
427 .start = 0x3B0,
428 .end = 0x3BF };
429 vga_video_type = VIDEO_TYPE_EGAM;
430 vga_vram_size = 0x8000;
431 display_desc = "EGA+";
432 request_resource(&ioport_resource,
433 &ega_console_resource);
434 } else {
435 static struct resource mda1_console_resource =
436 { .name = "mda",
437 .flags = IORESOURCE_IO,
438 .start = 0x3B0,
439 .end = 0x3BB };
440 static struct resource mda2_console_resource =
441 { .name = "mda",
442 .flags = IORESOURCE_IO,
443 .start = 0x3BF,
444 .end = 0x3BF };
445 vga_video_type = VIDEO_TYPE_MDA;
446 vga_vram_size = 0x2000;
447 display_desc = "*MDA";
448 request_resource(&ioport_resource,
449 &mda1_console_resource);
450 request_resource(&ioport_resource,
451 &mda2_console_resource);
452 vga_video_font_height = 14;
453 }
454 } else {
455
456 vga_can_do_color = true;
457 vga_vram_base = 0xb8000;
458 vga_video_port_reg = VGA_CRT_IC;
459 vga_video_port_val = VGA_CRT_DC;
460 if ((screen_info.orig_video_ega_bx & 0xff) != 0x10) {
461 int i;
462
463 vga_vram_size = 0x8000;
464
465 if (!screen_info.orig_video_isVGA) {
466 static struct resource ega_console_resource =
467 { .name = "ega",
468 .flags = IORESOURCE_IO,
469 .start = 0x3C0,
470 .end = 0x3DF };
471 vga_video_type = VIDEO_TYPE_EGAC;
472 display_desc = "EGA";
473 request_resource(&ioport_resource,
474 &ega_console_resource);
475 } else {
476 static struct resource vga_console_resource =
477 { .name = "vga+",
478 .flags = IORESOURCE_IO,
479 .start = 0x3C0,
480 .end = 0x3DF };
481 vga_video_type = VIDEO_TYPE_VGAC;
482 display_desc = "VGA+";
483 request_resource(&ioport_resource,
484 &vga_console_resource);
485
486
487
488
489
490
491
492 for (i = 0; i < 16; i++) {
493 inb_p(VGA_IS1_RC);
494 outb_p(i, VGA_ATT_W);
495 outb_p(i, VGA_ATT_W);
496 }
497 outb_p(0x20, VGA_ATT_W);
498
499
500
501
502
503 for (i = 0; i < 16; i++) {
504 outb_p(color_table[i], VGA_PEL_IW);
505 outb_p(default_red[i], VGA_PEL_D);
506 outb_p(default_grn[i], VGA_PEL_D);
507 outb_p(default_blu[i], VGA_PEL_D);
508 }
509 }
510 } else {
511 static struct resource cga_console_resource =
512 { .name = "cga",
513 .flags = IORESOURCE_IO,
514 .start = 0x3D4,
515 .end = 0x3D5 };
516 vga_video_type = VIDEO_TYPE_CGA;
517 vga_vram_size = 0x2000;
518 display_desc = "*CGA";
519 request_resource(&ioport_resource,
520 &cga_console_resource);
521 vga_video_font_height = 8;
522 }
523 }
524
525 vga_vram_base = VGA_MAP_MEM(vga_vram_base, vga_vram_size);
526 vga_vram_end = vga_vram_base + vga_vram_size;
527
528
529
530
531
532 p = (volatile u16 *) vga_vram_base;
533 saved1 = scr_readw(p);
534 saved2 = scr_readw(p + 1);
535 scr_writew(0xAA55, p);
536 scr_writew(0x55AA, p + 1);
537 if (scr_readw(p) != 0xAA55 || scr_readw(p + 1) != 0x55AA) {
538 scr_writew(saved1, p);
539 scr_writew(saved2, p + 1);
540 goto no_vga;
541 }
542 scr_writew(0x55AA, p);
543 scr_writew(0xAA55, p + 1);
544 if (scr_readw(p) != 0x55AA || scr_readw(p + 1) != 0xAA55) {
545 scr_writew(saved1, p);
546 scr_writew(saved2, p + 1);
547 goto no_vga;
548 }
549 scr_writew(saved1, p);
550 scr_writew(saved2, p + 1);
551
552 if (vga_video_type == VIDEO_TYPE_EGAC
553 || vga_video_type == VIDEO_TYPE_VGAC
554 || vga_video_type == VIDEO_TYPE_EGAM) {
555 vga_hardscroll_enabled = vga_hardscroll_user_enable;
556 vga_default_font_height = screen_info.orig_video_points;
557 vga_video_font_height = screen_info.orig_video_points;
558
559 vga_scan_lines =
560 vga_video_font_height * vga_video_num_lines;
561 }
562
563 vgacon_xres = screen_info.orig_video_cols * VGA_FONTWIDTH;
564 vgacon_yres = vga_scan_lines;
565
566 if (!vga_init_done) {
567 vgacon_scrollback_startup();
568 vga_init_done = true;
569 }
570
571 return display_desc;
572}
573
574static void vgacon_init(struct vc_data *c, int init)
575{
576 struct uni_pagedir *p;
577
578
579
580
581
582
583 c->vc_can_do_color = vga_can_do_color;
584
585
586 if (init) {
587 c->vc_cols = vga_video_num_columns;
588 c->vc_rows = vga_video_num_lines;
589 } else
590 vc_resize(c, vga_video_num_columns, vga_video_num_lines);
591
592 c->vc_scan_lines = vga_scan_lines;
593 c->vc_font.height = vga_video_font_height;
594 c->vc_complement_mask = 0x7700;
595 if (vga_512_chars)
596 c->vc_hi_font_mask = 0x0800;
597 p = *c->vc_uni_pagedir_loc;
598 if (c->vc_uni_pagedir_loc != &vgacon_uni_pagedir) {
599 con_free_unimap(c);
600 c->vc_uni_pagedir_loc = &vgacon_uni_pagedir;
601 vgacon_refcount++;
602 }
603 if (!vgacon_uni_pagedir && p)
604 con_set_default_unimap(c);
605
606
607 if (global_cursor_default == -1)
608 global_cursor_default =
609 !(screen_info.flags & VIDEO_FLAGS_NOCURSOR);
610}
611
612static void vgacon_deinit(struct vc_data *c)
613{
614
615 if (con_is_visible(c)) {
616 c->vc_visible_origin = vga_vram_base;
617 vga_set_mem_top(c);
618 }
619
620 if (!--vgacon_refcount)
621 con_free_unimap(c);
622 c->vc_uni_pagedir_loc = &c->vc_uni_pagedir;
623 con_set_default_unimap(c);
624}
625
626static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity,
627 u8 blink, u8 underline, u8 reverse, u8 italic)
628{
629 u8 attr = color;
630
631 if (vga_can_do_color) {
632 if (italic)
633 attr = (attr & 0xF0) | c->vc_itcolor;
634 else if (underline)
635 attr = (attr & 0xf0) | c->vc_ulcolor;
636 else if (intensity == 0)
637 attr = (attr & 0xf0) | c->vc_halfcolor;
638 }
639 if (reverse)
640 attr =
641 ((attr) & 0x88) | ((((attr) >> 4) | ((attr) << 4)) &
642 0x77);
643 if (blink)
644 attr ^= 0x80;
645 if (intensity == 2)
646 attr ^= 0x08;
647 if (!vga_can_do_color) {
648 if (italic)
649 attr = (attr & 0xF8) | 0x02;
650 else if (underline)
651 attr = (attr & 0xf8) | 0x01;
652 else if (intensity == 0)
653 attr = (attr & 0xf0) | 0x08;
654 }
655 return attr;
656}
657
658static void vgacon_invert_region(struct vc_data *c, u16 * p, int count)
659{
660 const bool col = vga_can_do_color;
661
662 while (count--) {
663 u16 a = scr_readw(p);
664 if (col)
665 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
666 (((a) & 0x0700) << 4);
667 else
668 a ^= ((a & 0x0700) == 0x0100) ? 0x7000 : 0x7700;
669 scr_writew(a, p++);
670 }
671}
672
673static void vgacon_set_cursor_size(int xpos, int from, int to)
674{
675 unsigned long flags;
676 int curs, cure;
677
678 if ((from == cursor_size_lastfrom) && (to == cursor_size_lastto))
679 return;
680 cursor_size_lastfrom = from;
681 cursor_size_lastto = to;
682
683 raw_spin_lock_irqsave(&vga_lock, flags);
684 if (vga_video_type >= VIDEO_TYPE_VGAC) {
685 outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg);
686 curs = inb_p(vga_video_port_val);
687 outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg);
688 cure = inb_p(vga_video_port_val);
689 } else {
690 curs = 0;
691 cure = 0;
692 }
693
694 curs = (curs & 0xc0) | from;
695 cure = (cure & 0xe0) | to;
696
697 outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg);
698 outb_p(curs, vga_video_port_val);
699 outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg);
700 outb_p(cure, vga_video_port_val);
701 raw_spin_unlock_irqrestore(&vga_lock, flags);
702}
703
704static void vgacon_cursor(struct vc_data *c, int mode)
705{
706 if (c->vc_mode != KD_TEXT)
707 return;
708
709 vgacon_restore_screen(c);
710
711 switch (mode) {
712 case CM_ERASE:
713 write_vga(14, (c->vc_pos - vga_vram_base) / 2);
714 if (vga_video_type >= VIDEO_TYPE_VGAC)
715 vgacon_set_cursor_size(c->vc_x, 31, 30);
716 else
717 vgacon_set_cursor_size(c->vc_x, 31, 31);
718 break;
719
720 case CM_MOVE:
721 case CM_DRAW:
722 write_vga(14, (c->vc_pos - vga_vram_base) / 2);
723 switch (c->vc_cursor_type & 0x0f) {
724 case CUR_UNDERLINE:
725 vgacon_set_cursor_size(c->vc_x,
726 c->vc_font.height -
727 (c->vc_font.height <
728 10 ? 2 : 3),
729 c->vc_font.height -
730 (c->vc_font.height <
731 10 ? 1 : 2));
732 break;
733 case CUR_TWO_THIRDS:
734 vgacon_set_cursor_size(c->vc_x,
735 c->vc_font.height / 3,
736 c->vc_font.height -
737 (c->vc_font.height <
738 10 ? 1 : 2));
739 break;
740 case CUR_LOWER_THIRD:
741 vgacon_set_cursor_size(c->vc_x,
742 (c->vc_font.height * 2) / 3,
743 c->vc_font.height -
744 (c->vc_font.height <
745 10 ? 1 : 2));
746 break;
747 case CUR_LOWER_HALF:
748 vgacon_set_cursor_size(c->vc_x,
749 c->vc_font.height / 2,
750 c->vc_font.height -
751 (c->vc_font.height <
752 10 ? 1 : 2));
753 break;
754 case CUR_NONE:
755 if (vga_video_type >= VIDEO_TYPE_VGAC)
756 vgacon_set_cursor_size(c->vc_x, 31, 30);
757 else
758 vgacon_set_cursor_size(c->vc_x, 31, 31);
759 break;
760 default:
761 vgacon_set_cursor_size(c->vc_x, 1,
762 c->vc_font.height);
763 break;
764 }
765 break;
766 }
767}
768
769static int vgacon_doresize(struct vc_data *c,
770 unsigned int width, unsigned int height)
771{
772 unsigned long flags;
773 unsigned int scanlines = height * c->vc_font.height;
774 u8 scanlines_lo = 0, r7 = 0, vsync_end = 0, mode, max_scan;
775
776 raw_spin_lock_irqsave(&vga_lock, flags);
777
778 vgacon_xres = width * VGA_FONTWIDTH;
779 vgacon_yres = height * c->vc_font.height;
780 if (vga_video_type >= VIDEO_TYPE_VGAC) {
781 outb_p(VGA_CRTC_MAX_SCAN, vga_video_port_reg);
782 max_scan = inb_p(vga_video_port_val);
783
784 if (max_scan & 0x80)
785 scanlines <<= 1;
786
787 outb_p(VGA_CRTC_MODE, vga_video_port_reg);
788 mode = inb_p(vga_video_port_val);
789
790 if (mode & 0x04)
791 scanlines >>= 1;
792
793 scanlines -= 1;
794 scanlines_lo = scanlines & 0xff;
795
796 outb_p(VGA_CRTC_OVERFLOW, vga_video_port_reg);
797 r7 = inb_p(vga_video_port_val) & ~0x42;
798
799 if (scanlines & 0x100)
800 r7 |= 0x02;
801 if (scanlines & 0x200)
802 r7 |= 0x40;
803
804
805 outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
806 vsync_end = inb_p(vga_video_port_val);
807 outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
808 outb_p(vsync_end & ~0x80, vga_video_port_val);
809 }
810
811 outb_p(VGA_CRTC_H_DISP, vga_video_port_reg);
812 outb_p(width - 1, vga_video_port_val);
813 outb_p(VGA_CRTC_OFFSET, vga_video_port_reg);
814 outb_p(width >> 1, vga_video_port_val);
815
816 if (vga_video_type >= VIDEO_TYPE_VGAC) {
817 outb_p(VGA_CRTC_V_DISP_END, vga_video_port_reg);
818 outb_p(scanlines_lo, vga_video_port_val);
819 outb_p(VGA_CRTC_OVERFLOW, vga_video_port_reg);
820 outb_p(r7,vga_video_port_val);
821
822
823 outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
824 outb_p(vsync_end, vga_video_port_val);
825 }
826
827 raw_spin_unlock_irqrestore(&vga_lock, flags);
828 return 0;
829}
830
831static int vgacon_switch(struct vc_data *c)
832{
833 int x = c->vc_cols * VGA_FONTWIDTH;
834 int y = c->vc_rows * c->vc_font.height;
835 int rows = screen_info.orig_video_lines * vga_default_font_height/
836 c->vc_font.height;
837
838
839
840
841
842 vga_video_num_columns = c->vc_cols;
843 vga_video_num_lines = c->vc_rows;
844
845
846
847
848 if (!vga_is_gfx) {
849 scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf,
850 c->vc_screenbuf_size > vga_vram_size ?
851 vga_vram_size : c->vc_screenbuf_size);
852
853 if ((vgacon_xres != x || vgacon_yres != y) &&
854 (!(vga_video_num_columns % 2) &&
855 vga_video_num_columns <= screen_info.orig_video_cols &&
856 vga_video_num_lines <= rows))
857 vgacon_doresize(c, c->vc_cols, c->vc_rows);
858 }
859
860 vgacon_scrollback_switch(c->vc_num);
861 return 0;
862}
863
864static void vga_set_palette(struct vc_data *vc, const unsigned char *table)
865{
866 int i, j;
867
868 vga_w(vgastate.vgabase, VGA_PEL_MSK, 0xff);
869 for (i = j = 0; i < 16; i++) {
870 vga_w(vgastate.vgabase, VGA_PEL_IW, table[i]);
871 vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
872 vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
873 vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
874 }
875}
876
877static void vgacon_set_palette(struct vc_data *vc, const unsigned char *table)
878{
879 if (vga_video_type != VIDEO_TYPE_VGAC || vga_palette_blanked
880 || !con_is_visible(vc))
881 return;
882 vga_set_palette(vc, table);
883}
884
885
886static struct {
887 unsigned char SeqCtrlIndex;
888 unsigned char CrtCtrlIndex;
889 unsigned char CrtMiscIO;
890 unsigned char HorizontalTotal;
891 unsigned char HorizDisplayEnd;
892 unsigned char StartHorizRetrace;
893 unsigned char EndHorizRetrace;
894 unsigned char Overflow;
895 unsigned char StartVertRetrace;
896 unsigned char EndVertRetrace;
897 unsigned char ModeControl;
898 unsigned char ClockingMode;
899} vga_state;
900
901static void vga_vesa_blank(struct vgastate *state, int mode)
902{
903
904 if (!vga_vesa_blanked) {
905 raw_spin_lock_irq(&vga_lock);
906 vga_state.SeqCtrlIndex = vga_r(state->vgabase, VGA_SEQ_I);
907 vga_state.CrtCtrlIndex = inb_p(vga_video_port_reg);
908 vga_state.CrtMiscIO = vga_r(state->vgabase, VGA_MIS_R);
909 raw_spin_unlock_irq(&vga_lock);
910
911 outb_p(0x00, vga_video_port_reg);
912 vga_state.HorizontalTotal = inb_p(vga_video_port_val);
913 outb_p(0x01, vga_video_port_reg);
914 vga_state.HorizDisplayEnd = inb_p(vga_video_port_val);
915 outb_p(0x04, vga_video_port_reg);
916 vga_state.StartHorizRetrace = inb_p(vga_video_port_val);
917 outb_p(0x05, vga_video_port_reg);
918 vga_state.EndHorizRetrace = inb_p(vga_video_port_val);
919 outb_p(0x07, vga_video_port_reg);
920 vga_state.Overflow = inb_p(vga_video_port_val);
921 outb_p(0x10, vga_video_port_reg);
922 vga_state.StartVertRetrace = inb_p(vga_video_port_val);
923 outb_p(0x11, vga_video_port_reg);
924 vga_state.EndVertRetrace = inb_p(vga_video_port_val);
925 outb_p(0x17, vga_video_port_reg);
926 vga_state.ModeControl = inb_p(vga_video_port_val);
927 vga_state.ClockingMode = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE);
928 }
929
930
931
932 raw_spin_lock_irq(&vga_lock);
933 vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, vga_state.ClockingMode | 0x20);
934
935
936 if ((vga_state.CrtMiscIO & 0x80) == 0x80)
937 vga_w(state->vgabase, VGA_MIS_W, vga_state.CrtMiscIO & 0xEF);
938
939
940
941
942
943
944 if (mode & VESA_VSYNC_SUSPEND) {
945 outb_p(0x10, vga_video_port_reg);
946 outb_p(0xff, vga_video_port_val);
947 outb_p(0x11, vga_video_port_reg);
948 outb_p(0x40, vga_video_port_val);
949 outb_p(0x07, vga_video_port_reg);
950 outb_p(vga_state.Overflow | 0x84, vga_video_port_val);
951 }
952
953 if (mode & VESA_HSYNC_SUSPEND) {
954
955
956
957
958
959 outb_p(0x04, vga_video_port_reg);
960 outb_p(0xff, vga_video_port_val);
961 outb_p(0x05, vga_video_port_reg);
962 outb_p(0x00, vga_video_port_val);
963 }
964
965
966 vga_w(state->vgabase, VGA_SEQ_I, vga_state.SeqCtrlIndex);
967 outb_p(vga_state.CrtCtrlIndex, vga_video_port_reg);
968 raw_spin_unlock_irq(&vga_lock);
969}
970
971static void vga_vesa_unblank(struct vgastate *state)
972{
973
974 raw_spin_lock_irq(&vga_lock);
975 vga_w(state->vgabase, VGA_MIS_W, vga_state.CrtMiscIO);
976
977 outb_p(0x00, vga_video_port_reg);
978 outb_p(vga_state.HorizontalTotal, vga_video_port_val);
979 outb_p(0x01, vga_video_port_reg);
980 outb_p(vga_state.HorizDisplayEnd, vga_video_port_val);
981 outb_p(0x04, vga_video_port_reg);
982 outb_p(vga_state.StartHorizRetrace, vga_video_port_val);
983 outb_p(0x05, vga_video_port_reg);
984 outb_p(vga_state.EndHorizRetrace, vga_video_port_val);
985 outb_p(0x07, vga_video_port_reg);
986 outb_p(vga_state.Overflow, vga_video_port_val);
987 outb_p(0x10, vga_video_port_reg);
988 outb_p(vga_state.StartVertRetrace, vga_video_port_val);
989 outb_p(0x11, vga_video_port_reg);
990 outb_p(vga_state.EndVertRetrace, vga_video_port_val);
991 outb_p(0x17, vga_video_port_reg);
992 outb_p(vga_state.ModeControl, vga_video_port_val);
993
994 vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, vga_state.ClockingMode);
995
996
997 vga_w(state->vgabase, VGA_SEQ_I, vga_state.SeqCtrlIndex);
998 outb_p(vga_state.CrtCtrlIndex, vga_video_port_reg);
999 raw_spin_unlock_irq(&vga_lock);
1000}
1001
1002static void vga_pal_blank(struct vgastate *state)
1003{
1004 int i;
1005
1006 vga_w(state->vgabase, VGA_PEL_MSK, 0xff);
1007 for (i = 0; i < 16; i++) {
1008 vga_w(state->vgabase, VGA_PEL_IW, i);
1009 vga_w(state->vgabase, VGA_PEL_D, 0);
1010 vga_w(state->vgabase, VGA_PEL_D, 0);
1011 vga_w(state->vgabase, VGA_PEL_D, 0);
1012 }
1013}
1014
1015static int vgacon_blank(struct vc_data *c, int blank, int mode_switch)
1016{
1017 switch (blank) {
1018 case 0:
1019 if (vga_vesa_blanked) {
1020 vga_vesa_unblank(&vgastate);
1021 vga_vesa_blanked = 0;
1022 }
1023 if (vga_palette_blanked) {
1024 vga_set_palette(c, color_table);
1025 vga_palette_blanked = false;
1026 return 0;
1027 }
1028 vga_is_gfx = false;
1029
1030 return 1;
1031 case 1:
1032 case -1:
1033 if (!mode_switch && vga_video_type == VIDEO_TYPE_VGAC) {
1034 vga_pal_blank(&vgastate);
1035 vga_palette_blanked = true;
1036 return 0;
1037 }
1038 vgacon_set_origin(c);
1039 scr_memsetw((void *) vga_vram_base, BLANK,
1040 c->vc_screenbuf_size);
1041 if (mode_switch)
1042 vga_is_gfx = true;
1043 return 1;
1044 default:
1045 if (vga_video_type == VIDEO_TYPE_VGAC) {
1046 vga_vesa_blank(&vgastate, blank - 1);
1047 vga_vesa_blanked = blank;
1048 }
1049 return 0;
1050 }
1051}
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065#define colourmap 0xa0000
1066
1067
1068#define blackwmap 0xa0000
1069#define cmapsz 8192
1070
1071static int vgacon_do_font_op(struct vgastate *state, char *arg, int set,
1072 bool ch512)
1073{
1074 unsigned short video_port_status = vga_video_port_reg + 6;
1075 int font_select = 0x00, beg, i;
1076 char *charmap;
1077 bool clear_attribs = false;
1078 if (vga_video_type != VIDEO_TYPE_EGAM) {
1079 charmap = (char *) VGA_MAP_MEM(colourmap, 0);
1080 beg = 0x0e;
1081 } else {
1082 charmap = (char *) VGA_MAP_MEM(blackwmap, 0);
1083 beg = 0x0a;
1084 }
1085
1086#ifdef BROKEN_GRAPHICS_PROGRAMS
1087
1088
1089
1090
1091 if (!arg)
1092 return -EINVAL;
1093
1094 vga_font_is_default = false;
1095 font_select = ch512 ? 0x04 : 0x00;
1096#else
1097
1098
1099
1100
1101
1102 if (set) {
1103 vga_font_is_default = !arg;
1104 if (!arg)
1105 ch512 = false;
1106 font_select = arg ? (ch512 ? 0x0e : 0x0a) : 0x00;
1107 }
1108
1109 if (!vga_font_is_default)
1110 charmap += 4 * cmapsz;
1111#endif
1112
1113 raw_spin_lock_irq(&vga_lock);
1114
1115 vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
1116
1117 vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x04);
1118
1119 vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x07);
1120
1121 vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03);
1122
1123
1124 vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x02);
1125
1126 vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x00);
1127
1128 vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x00);
1129 raw_spin_unlock_irq(&vga_lock);
1130
1131 if (arg) {
1132 if (set)
1133 for (i = 0; i < cmapsz; i++) {
1134 vga_writeb(arg[i], charmap + i);
1135 cond_resched();
1136 }
1137 else
1138 for (i = 0; i < cmapsz; i++) {
1139 arg[i] = vga_readb(charmap + i);
1140 cond_resched();
1141 }
1142
1143
1144
1145
1146
1147
1148 if (ch512) {
1149 charmap += 2 * cmapsz;
1150 arg += cmapsz;
1151 if (set)
1152 for (i = 0; i < cmapsz; i++) {
1153 vga_writeb(arg[i], charmap + i);
1154 cond_resched();
1155 }
1156 else
1157 for (i = 0; i < cmapsz; i++) {
1158 arg[i] = vga_readb(charmap + i);
1159 cond_resched();
1160 }
1161 }
1162 }
1163
1164 raw_spin_lock_irq(&vga_lock);
1165
1166 vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x01);
1167
1168 vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x03);
1169
1170 vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x03);
1171
1172 if (set)
1173 vga_wseq(state->vgabase, VGA_SEQ_CHARACTER_MAP, font_select);
1174
1175 vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03);
1176
1177
1178 vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x00);
1179
1180 vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x10);
1181
1182 vga_wgfx(state->vgabase, VGA_GFX_MISC, beg);
1183
1184
1185 if ((set) && (ch512 != vga_512_chars)) {
1186 vga_512_chars = ch512;
1187
1188
1189 inb_p(video_port_status);
1190
1191 vga_wattr(state->vgabase, VGA_ATC_PLANE_ENABLE, ch512 ? 0x07 : 0x0f);
1192
1193
1194 inb_p(video_port_status);
1195 vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);
1196 clear_attribs = true;
1197 }
1198 raw_spin_unlock_irq(&vga_lock);
1199
1200 if (clear_attribs) {
1201 for (i = 0; i < MAX_NR_CONSOLES; i++) {
1202 struct vc_data *c = vc_cons[i].d;
1203 if (c && c->vc_sw == &vga_con) {
1204
1205
1206 c->vc_hi_font_mask = 0x00;
1207 clear_buffer_attributes(c);
1208 c->vc_hi_font_mask = ch512 ? 0x0800 : 0;
1209 }
1210 }
1211 }
1212 return 0;
1213}
1214
1215
1216
1217
1218static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
1219{
1220 unsigned char ovr, vde, fsr;
1221 int rows, maxscan, i;
1222
1223 rows = vc->vc_scan_lines / fontheight;
1224 maxscan = rows * fontheight - 1;
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236 raw_spin_lock_irq(&vga_lock);
1237 outb_p(0x07, vga_video_port_reg);
1238 ovr = inb_p(vga_video_port_val);
1239 outb_p(0x09, vga_video_port_reg);
1240 fsr = inb_p(vga_video_port_val);
1241 raw_spin_unlock_irq(&vga_lock);
1242
1243 vde = maxscan & 0xff;
1244 ovr = (ovr & 0xbd) +
1245 ((maxscan & 0x100) >> 7) + ((maxscan & 0x200) >> 3);
1246 fsr = (fsr & 0xe0) + (fontheight - 1);
1247
1248 raw_spin_lock_irq(&vga_lock);
1249 outb_p(0x07, vga_video_port_reg);
1250 outb_p(ovr, vga_video_port_val);
1251 outb_p(0x09, vga_video_port_reg);
1252 outb_p(fsr, vga_video_port_val);
1253 outb_p(0x12, vga_video_port_reg);
1254 outb_p(vde, vga_video_port_val);
1255 raw_spin_unlock_irq(&vga_lock);
1256 vga_video_font_height = fontheight;
1257
1258 for (i = 0; i < MAX_NR_CONSOLES; i++) {
1259 struct vc_data *c = vc_cons[i].d;
1260
1261 if (c && c->vc_sw == &vga_con) {
1262 if (con_is_visible(c)) {
1263
1264 cursor_size_lastfrom = 0;
1265 cursor_size_lastto = 0;
1266 c->vc_sw->con_cursor(c, CM_DRAW);
1267 }
1268 c->vc_font.height = fontheight;
1269 vc_resize(c, 0, rows);
1270 }
1271 }
1272 return 0;
1273}
1274
1275static int vgacon_font_set(struct vc_data *c, struct console_font *font,
1276 unsigned int flags)
1277{
1278 unsigned charcount = font->charcount;
1279 int rc;
1280
1281 if (vga_video_type < VIDEO_TYPE_EGAM)
1282 return -EINVAL;
1283
1284 if (font->width != VGA_FONTWIDTH ||
1285 (charcount != 256 && charcount != 512))
1286 return -EINVAL;
1287
1288 rc = vgacon_do_font_op(&vgastate, font->data, 1, charcount == 512);
1289 if (rc)
1290 return rc;
1291
1292 if (!(flags & KD_FONT_FLAG_DONT_RECALC))
1293 rc = vgacon_adjust_height(c, font->height);
1294 return rc;
1295}
1296
1297static int vgacon_font_get(struct vc_data *c, struct console_font *font)
1298{
1299 if (vga_video_type < VIDEO_TYPE_EGAM)
1300 return -EINVAL;
1301
1302 font->width = VGA_FONTWIDTH;
1303 font->height = c->vc_font.height;
1304 font->charcount = vga_512_chars ? 512 : 256;
1305 if (!font->data)
1306 return 0;
1307 return vgacon_do_font_op(&vgastate, font->data, 0, vga_512_chars);
1308}
1309
1310static int vgacon_resize(struct vc_data *c, unsigned int width,
1311 unsigned int height, unsigned int user)
1312{
1313 if (width % 2 || width > screen_info.orig_video_cols ||
1314 height > (screen_info.orig_video_lines * vga_default_font_height)/
1315 c->vc_font.height)
1316
1317
1318 return (user) ? 0 : -EINVAL;
1319
1320 if (con_is_visible(c) && !vga_is_gfx)
1321 vgacon_doresize(c, width, height);
1322 return 0;
1323}
1324
1325static int vgacon_set_origin(struct vc_data *c)
1326{
1327 if (vga_is_gfx ||
1328 (console_blanked && !vga_palette_blanked))
1329 return 0;
1330 c->vc_origin = c->vc_visible_origin = vga_vram_base;
1331 vga_set_mem_top(c);
1332 vga_rolled_over = 0;
1333 return 1;
1334}
1335
1336static void vgacon_save_screen(struct vc_data *c)
1337{
1338 static int vga_bootup_console = 0;
1339
1340 if (!vga_bootup_console) {
1341
1342
1343
1344
1345 vga_bootup_console = 1;
1346 c->vc_x = screen_info.orig_x;
1347 c->vc_y = screen_info.orig_y;
1348 }
1349
1350
1351
1352
1353 if (!vga_is_gfx)
1354 scr_memcpyw((u16 *) c->vc_screenbuf, (u16 *) c->vc_origin,
1355 c->vc_screenbuf_size > vga_vram_size ? vga_vram_size : c->vc_screenbuf_size);
1356}
1357
1358static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
1359 enum con_scroll dir, unsigned int lines)
1360{
1361 unsigned long oldo;
1362 unsigned int delta;
1363
1364 if (t || b != c->vc_rows || vga_is_gfx || c->vc_mode != KD_TEXT)
1365 return false;
1366
1367 if (!vga_hardscroll_enabled || lines >= c->vc_rows / 2)
1368 return false;
1369
1370 vgacon_restore_screen(c);
1371 oldo = c->vc_origin;
1372 delta = lines * c->vc_size_row;
1373 if (dir == SM_UP) {
1374 vgacon_scrollback_update(c, t, lines);
1375 if (c->vc_scr_end + delta >= vga_vram_end) {
1376 scr_memcpyw((u16 *) vga_vram_base,
1377 (u16 *) (oldo + delta),
1378 c->vc_screenbuf_size - delta);
1379 c->vc_origin = vga_vram_base;
1380 vga_rolled_over = oldo - vga_vram_base;
1381 } else
1382 c->vc_origin += delta;
1383 scr_memsetw((u16 *) (c->vc_origin + c->vc_screenbuf_size -
1384 delta), c->vc_video_erase_char,
1385 delta);
1386 } else {
1387 if (oldo - delta < vga_vram_base) {
1388 scr_memmovew((u16 *) (vga_vram_end -
1389 c->vc_screenbuf_size +
1390 delta), (u16 *) oldo,
1391 c->vc_screenbuf_size - delta);
1392 c->vc_origin = vga_vram_end - c->vc_screenbuf_size;
1393 vga_rolled_over = 0;
1394 } else
1395 c->vc_origin -= delta;
1396 c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
1397 scr_memsetw((u16 *) (c->vc_origin), c->vc_video_erase_char,
1398 delta);
1399 }
1400 c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
1401 c->vc_visible_origin = c->vc_origin;
1402 vga_set_mem_top(c);
1403 c->vc_pos = (c->vc_pos - oldo) + c->vc_origin;
1404 return true;
1405}
1406
1407
1408
1409
1410
1411static void vgacon_clear(struct vc_data *vc, int sy, int sx, int height,
1412 int width) { }
1413static void vgacon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
1414static void vgacon_putcs(struct vc_data *vc, const unsigned short *s,
1415 int count, int ypos, int xpos) { }
1416
1417const struct consw vga_con = {
1418 .owner = THIS_MODULE,
1419 .con_startup = vgacon_startup,
1420 .con_init = vgacon_init,
1421 .con_deinit = vgacon_deinit,
1422 .con_clear = vgacon_clear,
1423 .con_putc = vgacon_putc,
1424 .con_putcs = vgacon_putcs,
1425 .con_cursor = vgacon_cursor,
1426 .con_scroll = vgacon_scroll,
1427 .con_switch = vgacon_switch,
1428 .con_blank = vgacon_blank,
1429 .con_font_set = vgacon_font_set,
1430 .con_font_get = vgacon_font_get,
1431 .con_resize = vgacon_resize,
1432 .con_set_palette = vgacon_set_palette,
1433 .con_scrolldelta = vgacon_scrolldelta,
1434 .con_set_origin = vgacon_set_origin,
1435 .con_save_screen = vgacon_save_screen,
1436 .con_build_attr = vgacon_build_attr,
1437 .con_invert_region = vgacon_invert_region,
1438 .con_flush_scrollback = vgacon_flush_scrollback,
1439};
1440EXPORT_SYMBOL(vga_con);
1441
1442MODULE_LICENSE("GPL");
1443