blob: ca23d222e02992e9a573823d6ed28256221d129a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/video/vgacon.c -- Low level VGA based console driver
3 *
4 * Created 28 Sep 1997 by Geert Uytterhoeven
5 *
6 * Rewritten by Martin Mares <mj@ucw.cz>, July 1998
7 *
8 * This file is based on the old console.c, vga.c and vesa_blank.c drivers.
9 *
10 * Copyright (C) 1991, 1992 Linus Torvalds
11 * 1995 Jay Estabrook
12 *
13 * User definable mapping table and font loading by Eugene G. Crosser,
14 * <crosser@average.org>
15 *
16 * Improved loadable font/UTF-8 support by H. Peter Anvin
17 * Feb-Sep 1995 <peter.anvin@linux.org>
18 *
19 * Colour palette handling, by Simon Tatham
20 * 17-Jun-95 <sgt20@cam.ac.uk>
21 *
22 * if 512 char mode is already enabled don't re-enable it,
23 * because it causes screen to flicker, by Mitja Horvat
24 * 5-May-96 <mitja.horvat@guest.arnes.si>
25 *
26 * Use 2 outw instead of 4 outb_p to reduce erroneous text
27 * flashing on RHS of screen during heavy console scrolling .
28 * Oct 1996, Paul Gortmaker.
29 *
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive for
33 * more details.
34 */
35
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <linux/module.h>
37#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include <linux/fs.h>
39#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#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>
Dave Airlie0ab36912013-06-27 10:07:39 +100045#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <linux/selection.h>
47#include <linux/spinlock.h>
48#include <linux/ioport.h>
49#include <linux/init.h>
Jon Smirl894673e2006-07-10 04:44:13 -070050#include <linux/screen_info.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#include <video/vga.h>
52#include <asm/io.h>
53
Thomas Gleixner6b2c1802009-07-25 16:17:02 +020054static DEFINE_RAW_SPINLOCK(vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070055static int cursor_size_lastfrom;
56static int cursor_size_lastto;
Antonino A. Daplas53dbb262006-01-08 01:02:36 -080057static u32 vgacon_xres;
58static u32 vgacon_yres;
Mark Rustad89f02442014-10-14 04:53:49 -070059static struct vgastate vgastate;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61#define BLANK 0x0020
62
Antonino A. Daplas53dbb262006-01-08 01:02:36 -080063#define VGA_FONTWIDTH 8 /* VGA does not support fontwidths != 8 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070064/*
65 * Interface used by the world
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);
Jiri Slaby97293de2016-06-23 13:34:26 +020074static void vgacon_scrolldelta(struct vc_data *c, int lines);
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static int vgacon_set_origin(struct vc_data *c);
76static void vgacon_save_screen(struct vc_data *c);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077static void vgacon_invert_region(struct vc_data *c, u16 * p, int count);
Takashi Iwaie4bdab72014-05-13 12:09:28 +020078static struct uni_pagedir *vgacon_uni_pagedir;
Takashi Iwai0f2893f2014-05-13 12:09:27 +020079static int vgacon_refcount;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
Linus Torvalds1da177e2005-04-16 15:20:36 -070081/* Description of the hardware situation */
Jiri Slaby96fd9552016-10-03 11:18:38 +020082static bool vga_init_done;
Helge Deller0128bee2006-12-08 02:40:29 -080083static unsigned long vga_vram_base __read_mostly; /* Base of video memory */
84static unsigned long vga_vram_end __read_mostly; /* End of video memory */
85static unsigned int vga_vram_size __read_mostly; /* Size of video memory */
86static u16 vga_video_port_reg __read_mostly; /* Video register select port */
87static u16 vga_video_port_val __read_mostly; /* Video register value port */
88static unsigned int vga_video_num_columns; /* Number of text columns */
89static unsigned int vga_video_num_lines; /* Number of text lines */
Jiri Slaby96fd9552016-10-03 11:18:38 +020090static bool vga_can_do_color; /* Do we support colors? */
Helge Deller0128bee2006-12-08 02:40:29 -080091static unsigned int vga_default_font_height __read_mostly; /* Height of default screen font */
92static unsigned char vga_video_type __read_mostly; /* Card type */
Jiri Slaby96fd9552016-10-03 11:18:38 +020093static bool vga_font_is_default = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094static int vga_vesa_blanked;
Jiri Slaby96fd9552016-10-03 11:18:38 +020095static bool vga_palette_blanked;
96static bool vga_is_gfx;
97static bool vga_512_chars;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098static int vga_video_font_height;
Helge Deller0128bee2006-12-08 02:40:29 -080099static int vga_scan_lines __read_mostly;
100static unsigned int vga_rolled_over;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Jiri Slaby96fd9552016-10-03 11:18:38 +0200102static bool vgacon_text_mode_force;
103static bool vga_hardscroll_enabled;
104static bool vga_hardscroll_user_enable = true;
Dave Airlief453ba02008-11-07 14:05:41 -0800105
106bool vgacon_text_force(void)
107{
Jiri Slaby96fd9552016-10-03 11:18:38 +0200108 return vgacon_text_mode_force;
Dave Airlief453ba02008-11-07 14:05:41 -0800109}
110EXPORT_SYMBOL(vgacon_text_force);
111
112static int __init text_mode(char *str)
113{
Jiri Slaby96fd9552016-10-03 11:18:38 +0200114 vgacon_text_mode_force = true;
Dave Airlief453ba02008-11-07 14:05:41 -0800115 return 1;
116}
117
118/* force text mode - used by kernel modesetting */
119__setup("nomodeset", text_mode);
120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121static int __init no_scroll(char *str)
122{
123 /*
124 * Disabling scrollback is required for the Braillex ib80-piezo
125 * Braille reader made by F.H. Papenmeier (Germany).
126 * Use the "no-scroll" bootflag.
127 */
Jiri Slaby96fd9552016-10-03 11:18:38 +0200128 vga_hardscroll_user_enable = vga_hardscroll_enabled = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 return 1;
130}
131
132__setup("no-scroll", no_scroll);
133
134/*
135 * By replacing the four outb_p with two back to back outw, we can reduce
136 * the window of opportunity to see text mislocated to the RHS of the
137 * console during heavy scrolling activity. However there is the remote
138 * possibility that some pre-dinosaur hardware won't like the back to back
139 * I/O. Since the Xservers get away with it, we should be able to as well.
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 * ddprintk might set the console position from interrupt
148 * handlers, thus the write has to be IRQ-atomic.
149 */
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200150 raw_spin_lock_irqsave(&vga_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 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);
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200155 raw_spin_unlock_irqrestore(&vga_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156}
157
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800158static 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
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800164/* software scrollback */
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100165struct vgacon_scrollback_info {
Manuel Schölling66da39e2017-01-13 21:07:55 +0100166 void *data;
167 int tail;
168 int size;
169 int rows;
170 int cnt;
171 int cur;
172 int save;
173 int restore;
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100174};
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800175
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100176static struct vgacon_scrollback_info *vgacon_scrollback_cur;
177#ifdef CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT
178static struct vgacon_scrollback_info vgacon_scrollbacks[MAX_NR_CONSOLES];
179#else
180static struct vgacon_scrollback_info vgacon_scrollbacks[1];
181#endif
182
183static void vgacon_scrollback_reset(int vc_num, size_t reset_size)
Manuel Schöllingbcd375f2017-01-13 21:07:56 +0100184{
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100185 struct vgacon_scrollback_info *scrollback = &vgacon_scrollbacks[vc_num];
Manuel Schöllingbcd375f2017-01-13 21:07:56 +0100186
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100187 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;
Manuel Schöllingbcd375f2017-01-13 21:07:56 +0100193}
194
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100195static void vgacon_scrollback_init(int vc_num)
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800196{
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100197 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;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800201
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100202 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#ifndef CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT
217 vc_num = 0;
218#endif
219
220 if (!vgacon_scrollbacks[vc_num].data) {
221 vgacon_scrollback_init(vc_num);
222 } else {
223#ifdef CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT
224 vgacon_scrollback_cur = &vgacon_scrollbacks[vc_num];
225#else
226 size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024;
227
228 vgacon_scrollback_reset(vc_num, size);
229#endif
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800230 }
231}
232
Amerigo Wang63b1dd02011-01-19 06:24:02 +0000233static void vgacon_scrollback_startup(void)
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800234{
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100235 vgacon_scrollback_cur = &vgacon_scrollbacks[0];
236 vgacon_scrollback_init(0);
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800237}
238
239static void vgacon_scrollback_update(struct vc_data *c, int t, int count)
240{
241 void *p;
242
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100243 if (!vgacon_scrollback_cur->data || !vgacon_scrollback_cur->size ||
244 c->vc_num != fg_console)
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800245 return;
246
247 p = (void *) (c->vc_origin + t * c->vc_size_row);
248
249 while (count--) {
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100250 scr_memcpyw(vgacon_scrollback_cur->data +
251 vgacon_scrollback_cur->tail,
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800252 p, c->vc_size_row);
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100253
254 vgacon_scrollback_cur->cnt++;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800255 p += c->vc_size_row;
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100256 vgacon_scrollback_cur->tail += c->vc_size_row;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800257
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100258 if (vgacon_scrollback_cur->tail >= vgacon_scrollback_cur->size)
259 vgacon_scrollback_cur->tail = 0;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800260
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100261 if (vgacon_scrollback_cur->cnt > vgacon_scrollback_cur->rows)
262 vgacon_scrollback_cur->cnt = vgacon_scrollback_cur->rows;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800263
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100264 vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800265 }
266}
267
268static void vgacon_restore_screen(struct vc_data *c)
269{
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100270 vgacon_scrollback_cur->save = 0;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800271
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100272 if (!vga_is_gfx && !vgacon_scrollback_cur->restore) {
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800273 scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf,
274 c->vc_screenbuf_size > vga_vram_size ?
275 vga_vram_size : c->vc_screenbuf_size);
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100276 vgacon_scrollback_cur->restore = 1;
277 vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800278 }
279}
280
Jiri Slaby97293de2016-06-23 13:34:26 +0200281static void vgacon_scrolldelta(struct vc_data *c, int lines)
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800282{
Marcin Slusarz5ab48402008-10-15 22:03:50 -0700283 int start, end, count, soff;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800284
285 if (!lines) {
286 c->vc_visible_origin = c->vc_origin;
287 vga_set_mem_top(c);
Jiri Slaby97293de2016-06-23 13:34:26 +0200288 return;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800289 }
290
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100291 if (!vgacon_scrollback_cur->data)
Jiri Slaby97293de2016-06-23 13:34:26 +0200292 return;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800293
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100294 if (!vgacon_scrollback_cur->save) {
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800295 vgacon_cursor(c, CM_ERASE);
296 vgacon_save_screen(c);
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100297 vgacon_scrollback_cur->save = 1;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800298 }
299
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100300 vgacon_scrollback_cur->restore = 0;
301 start = vgacon_scrollback_cur->cur + lines;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800302 end = start + abs(lines);
303
304 if (start < 0)
305 start = 0;
306
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100307 if (start > vgacon_scrollback_cur->cnt)
308 start = vgacon_scrollback_cur->cnt;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800309
310 if (end < 0)
311 end = 0;
312
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100313 if (end > vgacon_scrollback_cur->cnt)
314 end = vgacon_scrollback_cur->cnt;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800315
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100316 vgacon_scrollback_cur->cur = start;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800317 count = end - start;
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100318 soff = vgacon_scrollback_cur->tail -
319 ((vgacon_scrollback_cur->cnt - end) * c->vc_size_row);
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800320 soff -= count * c->vc_size_row;
321
322 if (soff < 0)
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100323 soff += vgacon_scrollback_cur->size;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800324
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100325 count = vgacon_scrollback_cur->cnt - start;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800326
327 if (count > c->vc_rows)
328 count = c->vc_rows;
329
Marcin Slusarzc38182a2008-10-15 22:03:49 -0700330 if (count) {
331 int copysize;
Marcin Slusarz5ab48402008-10-15 22:03:50 -0700332
333 int diff = c->vc_rows - count;
334 void *d = (void *) c->vc_origin;
335 void *s = (void *) c->vc_screenbuf;
336
Marcin Slusarzc38182a2008-10-15 22:03:49 -0700337 count *= c->vc_size_row;
338 /* how much memory to end of buffer left? */
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100339 copysize = min(count, vgacon_scrollback_cur->size - soff);
340 scr_memcpyw(d, vgacon_scrollback_cur->data + soff, copysize);
Marcin Slusarzc38182a2008-10-15 22:03:49 -0700341 d += copysize;
342 count -= copysize;
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800343
Marcin Slusarzc38182a2008-10-15 22:03:49 -0700344 if (count) {
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100345 scr_memcpyw(d, vgacon_scrollback_cur->data, count);
Marcin Slusarzc38182a2008-10-15 22:03:49 -0700346 d += count;
347 }
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800348
Marcin Slusarzc38182a2008-10-15 22:03:49 -0700349 if (diff)
350 scr_memcpyw(d, s, diff * c->vc_size_row);
Marcin Slusarz5ab48402008-10-15 22:03:50 -0700351 } else
352 vgacon_cursor(c, CM_MOVE);
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800353}
Manuel Schöllingbcd375f2017-01-13 21:07:56 +0100354
355static void vgacon_flush_scrollback(struct vc_data *c)
356{
357 size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024;
358
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100359 vgacon_scrollback_reset(c->vc_num, size);
Manuel Schöllingbcd375f2017-01-13 21:07:56 +0100360}
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800361#else
362#define vgacon_scrollback_startup(...) do { } while (0)
363#define vgacon_scrollback_init(...) do { } while (0)
364#define vgacon_scrollback_update(...) do { } while (0)
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100365#define vgacon_scrollback_switch(...) do { } while (0)
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800366
367static void vgacon_restore_screen(struct vc_data *c)
368{
369 if (c->vc_origin != c->vc_visible_origin)
370 vgacon_scrolldelta(c, 0);
371}
372
Jiri Slaby97293de2016-06-23 13:34:26 +0200373static void vgacon_scrolldelta(struct vc_data *c, int lines)
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800374{
Jiri Slaby35cc56f2016-10-03 11:18:35 +0200375 vc_scrolldelta_helper(c, lines, vga_rolled_over, (void *)vga_vram_base,
376 vga_vram_size);
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800377 vga_set_mem_top(c);
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800378}
Manuel Schöllingbcd375f2017-01-13 21:07:56 +0100379
380static void vgacon_flush_scrollback(struct vc_data *c)
381{
382}
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800383#endif /* CONFIG_VGACON_SOFT_SCROLLBACK */
384
Antonino A. Daplas50ec42e2006-06-26 00:27:02 -0700385static const char *vgacon_startup(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386{
387 const char *display_desc = NULL;
388 u16 saved1, saved2;
389 volatile u16 *p;
390
Yannick Heneault554ec372011-01-12 17:00:04 -0800391 if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB ||
392 screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 no_vga:
394#ifdef CONFIG_DUMMY_CONSOLE
395 conswitchp = &dummy_con;
396 return conswitchp->con_startup();
397#else
398 return NULL;
399#endif
400 }
401
H. Peter Anvin30c82642007-10-15 17:13:22 -0700402 /* boot_params.screen_info initialized? */
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700403 if ((screen_info.orig_video_mode == 0) &&
404 (screen_info.orig_video_lines == 0) &&
405 (screen_info.orig_video_cols == 0))
Gerd Hoffmanna1a48492007-05-16 22:11:09 -0700406 goto no_vga;
407
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 /* VGA16 modes are not handled by VGACON */
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700409 if ((screen_info.orig_video_mode == 0x0D) || /* 320x200/4 */
410 (screen_info.orig_video_mode == 0x0E) || /* 640x200/4 */
411 (screen_info.orig_video_mode == 0x10) || /* 640x350/4 */
412 (screen_info.orig_video_mode == 0x12) || /* 640x480/4 */
413 (screen_info.orig_video_mode == 0x6A)) /* 800x600/4 (VESA) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 goto no_vga;
415
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700416 vga_video_num_lines = screen_info.orig_video_lines;
417 vga_video_num_columns = screen_info.orig_video_cols;
Mark Rustad89f02442014-10-14 04:53:49 -0700418 vgastate.vgabase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700420 if (screen_info.orig_video_mode == 7) {
421 /* Monochrome display */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 vga_vram_base = 0xb0000;
423 vga_video_port_reg = VGA_CRT_IM;
424 vga_video_port_val = VGA_CRT_DM;
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700425 if ((screen_info.orig_video_ega_bx & 0xff) != 0x10) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 static struct resource ega_console_resource =
Greg Kroah-Hartman740e5182006-06-12 14:47:06 -0700427 { .name = "ega", .start = 0x3B0, .end = 0x3BF };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 vga_video_type = VIDEO_TYPE_EGAM;
Bjorn Helgaas4f1bcaf2006-06-22 14:47:32 -0700429 vga_vram_size = 0x8000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 display_desc = "EGA+";
431 request_resource(&ioport_resource,
432 &ega_console_resource);
433 } else {
434 static struct resource mda1_console_resource =
Greg Kroah-Hartman740e5182006-06-12 14:47:06 -0700435 { .name = "mda", .start = 0x3B0, .end = 0x3BB };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 static struct resource mda2_console_resource =
Greg Kroah-Hartman740e5182006-06-12 14:47:06 -0700437 { .name = "mda", .start = 0x3BF, .end = 0x3BF };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 vga_video_type = VIDEO_TYPE_MDA;
Bjorn Helgaas4f1bcaf2006-06-22 14:47:32 -0700439 vga_vram_size = 0x2000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 display_desc = "*MDA";
441 request_resource(&ioport_resource,
442 &mda1_console_resource);
443 request_resource(&ioport_resource,
444 &mda2_console_resource);
445 vga_video_font_height = 14;
446 }
447 } else {
448 /* If not, it is color. */
Jiri Slaby96fd9552016-10-03 11:18:38 +0200449 vga_can_do_color = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 vga_vram_base = 0xb8000;
451 vga_video_port_reg = VGA_CRT_IC;
452 vga_video_port_val = VGA_CRT_DC;
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700453 if ((screen_info.orig_video_ega_bx & 0xff) != 0x10) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 int i;
455
Bjorn Helgaas4f1bcaf2006-06-22 14:47:32 -0700456 vga_vram_size = 0x8000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700458 if (!screen_info.orig_video_isVGA) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 static struct resource ega_console_resource
Greg Kroah-Hartman740e5182006-06-12 14:47:06 -0700460 = { .name = "ega", .start = 0x3C0, .end = 0x3DF };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 vga_video_type = VIDEO_TYPE_EGAC;
462 display_desc = "EGA";
463 request_resource(&ioport_resource,
464 &ega_console_resource);
465 } else {
466 static struct resource vga_console_resource
Greg Kroah-Hartman740e5182006-06-12 14:47:06 -0700467 = { .name = "vga+", .start = 0x3C0, .end = 0x3DF };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 vga_video_type = VIDEO_TYPE_VGAC;
469 display_desc = "VGA+";
470 request_resource(&ioport_resource,
471 &vga_console_resource);
472
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 /*
474 * Normalise the palette registers, to point
475 * the 16 screen colours to the first 16
476 * DAC entries.
477 */
478
479 for (i = 0; i < 16; i++) {
480 inb_p(VGA_IS1_RC);
481 outb_p(i, VGA_ATT_W);
482 outb_p(i, VGA_ATT_W);
483 }
484 outb_p(0x20, VGA_ATT_W);
485
486 /*
487 * Now set the DAC registers back to their
488 * default values
489 */
490 for (i = 0; i < 16; i++) {
491 outb_p(color_table[i], VGA_PEL_IW);
492 outb_p(default_red[i], VGA_PEL_D);
493 outb_p(default_grn[i], VGA_PEL_D);
494 outb_p(default_blu[i], VGA_PEL_D);
495 }
496 }
497 } else {
498 static struct resource cga_console_resource =
Greg Kroah-Hartman740e5182006-06-12 14:47:06 -0700499 { .name = "cga", .start = 0x3D4, .end = 0x3D5 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 vga_video_type = VIDEO_TYPE_CGA;
Bjorn Helgaas4f1bcaf2006-06-22 14:47:32 -0700501 vga_vram_size = 0x2000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 display_desc = "*CGA";
503 request_resource(&ioport_resource,
504 &cga_console_resource);
505 vga_video_font_height = 8;
506 }
507 }
508
Bjorn Helgaas4f1bcaf2006-06-22 14:47:32 -0700509 vga_vram_base = VGA_MAP_MEM(vga_vram_base, vga_vram_size);
510 vga_vram_end = vga_vram_base + vga_vram_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
512 /*
513 * Find out if there is a graphics card present.
514 * Are there smarter methods around?
515 */
516 p = (volatile u16 *) vga_vram_base;
517 saved1 = scr_readw(p);
518 saved2 = scr_readw(p + 1);
519 scr_writew(0xAA55, p);
520 scr_writew(0x55AA, p + 1);
521 if (scr_readw(p) != 0xAA55 || scr_readw(p + 1) != 0x55AA) {
522 scr_writew(saved1, p);
523 scr_writew(saved2, p + 1);
524 goto no_vga;
525 }
526 scr_writew(0x55AA, p);
527 scr_writew(0xAA55, p + 1);
528 if (scr_readw(p) != 0x55AA || scr_readw(p + 1) != 0xAA55) {
529 scr_writew(saved1, p);
530 scr_writew(saved2, p + 1);
531 goto no_vga;
532 }
533 scr_writew(saved1, p);
534 scr_writew(saved2, p + 1);
535
536 if (vga_video_type == VIDEO_TYPE_EGAC
537 || vga_video_type == VIDEO_TYPE_VGAC
538 || vga_video_type == VIDEO_TYPE_EGAM) {
539 vga_hardscroll_enabled = vga_hardscroll_user_enable;
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700540 vga_default_font_height = screen_info.orig_video_points;
541 vga_video_font_height = screen_info.orig_video_points;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 /* This may be suboptimal but is a safe bet - go with it */
543 vga_scan_lines =
544 vga_video_font_height * vga_video_num_lines;
545 }
Antonino A. Daplas53dbb262006-01-08 01:02:36 -0800546
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700547 vgacon_xres = screen_info.orig_video_cols * VGA_FONTWIDTH;
Antonino A. Daplas53dbb262006-01-08 01:02:36 -0800548 vgacon_yres = vga_scan_lines;
Antonino A. Daplas50ec42e2006-06-26 00:27:02 -0700549
550 if (!vga_init_done) {
551 vgacon_scrollback_startup();
Jiri Slaby96fd9552016-10-03 11:18:38 +0200552 vga_init_done = true;
Antonino A. Daplas50ec42e2006-06-26 00:27:02 -0700553 }
554
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 return display_desc;
556}
557
558static void vgacon_init(struct vc_data *c, int init)
559{
Takashi Iwaie4bdab72014-05-13 12:09:28 +0200560 struct uni_pagedir *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
Antonino A. Daplas50ec42e2006-06-26 00:27:02 -0700562 /*
563 * We cannot be loaded as a module, therefore init is always 1,
564 * but vgacon_init can be called more than once, and init will
565 * not be 1.
566 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 c->vc_can_do_color = vga_can_do_color;
Antonino A. Daplas50ec42e2006-06-26 00:27:02 -0700568
569 /* set dimensions manually if init != 0 since vc_resize() will fail */
570 if (init) {
571 c->vc_cols = vga_video_num_columns;
572 c->vc_rows = vga_video_num_lines;
573 } else
574 vc_resize(c, vga_video_num_columns, vga_video_num_lines);
575
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 c->vc_scan_lines = vga_scan_lines;
577 c->vc_font.height = vga_video_font_height;
578 c->vc_complement_mask = 0x7700;
Bill Nottinghama40920b2005-05-01 08:59:07 -0700579 if (vga_512_chars)
580 c->vc_hi_font_mask = 0x0800;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 p = *c->vc_uni_pagedir_loc;
Takashi Iwai0f2893f2014-05-13 12:09:27 +0200582 if (c->vc_uni_pagedir_loc != &vgacon_uni_pagedir) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 con_free_unimap(c);
Takashi Iwai0f2893f2014-05-13 12:09:27 +0200584 c->vc_uni_pagedir_loc = &vgacon_uni_pagedir;
585 vgacon_refcount++;
586 }
587 if (!vgacon_uni_pagedir && p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 con_set_default_unimap(c);
Matthew Garrettf6c06b62009-11-13 15:14:11 -0500589
Matthew Garrettb434a682009-11-13 14:57:02 -0500590 /* Only set the default if the user didn't deliberately override it */
591 if (global_cursor_default == -1)
592 global_cursor_default =
593 !(screen_info.flags & VIDEO_FLAGS_NOCURSOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594}
595
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596static void vgacon_deinit(struct vc_data *c)
597{
Francisco Jerezf0c7d2b2009-09-22 16:47:53 -0700598 /* When closing the active console, reset video origin */
Jiri Slaby6ca8dfd2016-06-23 13:34:35 +0200599 if (con_is_visible(c)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 c->vc_visible_origin = vga_vram_base;
601 vga_set_mem_top(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 }
Francisco Jerezf0c7d2b2009-09-22 16:47:53 -0700603
Takashi Iwai0f2893f2014-05-13 12:09:27 +0200604 if (!--vgacon_refcount)
Francisco Jerezf0c7d2b2009-09-22 16:47:53 -0700605 con_free_unimap(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 c->vc_uni_pagedir_loc = &c->vc_uni_pagedir;
607 con_set_default_unimap(c);
608}
609
610static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity,
Jan Engelhardtfa6ce9a2007-05-08 00:38:04 -0700611 u8 blink, u8 underline, u8 reverse, u8 italic)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612{
613 u8 attr = color;
614
615 if (vga_can_do_color) {
Jan Engelhardtfa6ce9a2007-05-08 00:38:04 -0700616 if (italic)
617 attr = (attr & 0xF0) | c->vc_itcolor;
618 else if (underline)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 attr = (attr & 0xf0) | c->vc_ulcolor;
620 else if (intensity == 0)
621 attr = (attr & 0xf0) | c->vc_halfcolor;
622 }
623 if (reverse)
624 attr =
625 ((attr) & 0x88) | ((((attr) >> 4) | ((attr) << 4)) &
626 0x77);
627 if (blink)
628 attr ^= 0x80;
629 if (intensity == 2)
630 attr ^= 0x08;
631 if (!vga_can_do_color) {
Jan Engelhardtfa6ce9a2007-05-08 00:38:04 -0700632 if (italic)
633 attr = (attr & 0xF8) | 0x02;
634 else if (underline)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 attr = (attr & 0xf8) | 0x01;
636 else if (intensity == 0)
637 attr = (attr & 0xf0) | 0x08;
638 }
639 return attr;
640}
641
642static void vgacon_invert_region(struct vc_data *c, u16 * p, int count)
643{
Jiri Slaby96fd9552016-10-03 11:18:38 +0200644 const bool col = vga_can_do_color;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
646 while (count--) {
647 u16 a = scr_readw(p);
648 if (col)
649 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
650 (((a) & 0x0700) << 4);
651 else
652 a ^= ((a & 0x0700) == 0x0100) ? 0x7000 : 0x7700;
653 scr_writew(a, p++);
654 }
655}
656
657static void vgacon_set_cursor_size(int xpos, int from, int to)
658{
659 unsigned long flags;
660 int curs, cure;
661
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 if ((from == cursor_size_lastfrom) && (to == cursor_size_lastto))
663 return;
664 cursor_size_lastfrom = from;
665 cursor_size_lastto = to;
666
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200667 raw_spin_lock_irqsave(&vga_lock, flags);
Samuel Thibault2115aea2006-03-27 01:17:19 -0800668 if (vga_video_type >= VIDEO_TYPE_VGAC) {
669 outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg);
670 curs = inb_p(vga_video_port_val);
671 outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg);
672 cure = inb_p(vga_video_port_val);
673 } else {
674 curs = 0;
675 cure = 0;
676 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677
678 curs = (curs & 0xc0) | from;
679 cure = (cure & 0xe0) | to;
680
Samuel Thibault2115aea2006-03-27 01:17:19 -0800681 outb_p(VGA_CRTC_CURSOR_START, vga_video_port_reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 outb_p(curs, vga_video_port_val);
Samuel Thibault2115aea2006-03-27 01:17:19 -0800683 outb_p(VGA_CRTC_CURSOR_END, vga_video_port_reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 outb_p(cure, vga_video_port_val);
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200685 raw_spin_unlock_irqrestore(&vga_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686}
687
688static void vgacon_cursor(struct vc_data *c, int mode)
689{
Antonino A. Daplas2ae85472007-05-08 00:40:06 -0700690 if (c->vc_mode != KD_TEXT)
691 return;
692
Antonino A. Daplas15bdab92006-03-27 01:17:20 -0800693 vgacon_restore_screen(c);
694
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 switch (mode) {
696 case CM_ERASE:
Samuel Thibault88dcb6c2005-11-05 22:19:50 +0100697 write_vga(14, (c->vc_pos - vga_vram_base) / 2);
Samuel Thibault2115aea2006-03-27 01:17:19 -0800698 if (vga_video_type >= VIDEO_TYPE_VGAC)
699 vgacon_set_cursor_size(c->vc_x, 31, 30);
700 else
701 vgacon_set_cursor_size(c->vc_x, 31, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 break;
703
704 case CM_MOVE:
705 case CM_DRAW:
706 write_vga(14, (c->vc_pos - vga_vram_base) / 2);
707 switch (c->vc_cursor_type & 0x0f) {
708 case CUR_UNDERLINE:
709 vgacon_set_cursor_size(c->vc_x,
710 c->vc_font.height -
711 (c->vc_font.height <
712 10 ? 2 : 3),
713 c->vc_font.height -
714 (c->vc_font.height <
715 10 ? 1 : 2));
716 break;
717 case CUR_TWO_THIRDS:
718 vgacon_set_cursor_size(c->vc_x,
719 c->vc_font.height / 3,
720 c->vc_font.height -
721 (c->vc_font.height <
722 10 ? 1 : 2));
723 break;
724 case CUR_LOWER_THIRD:
725 vgacon_set_cursor_size(c->vc_x,
726 (c->vc_font.height * 2) / 3,
727 c->vc_font.height -
728 (c->vc_font.height <
729 10 ? 1 : 2));
730 break;
731 case CUR_LOWER_HALF:
732 vgacon_set_cursor_size(c->vc_x,
733 c->vc_font.height / 2,
734 c->vc_font.height -
735 (c->vc_font.height <
736 10 ? 1 : 2));
737 break;
738 case CUR_NONE:
Samuel Thibault2115aea2006-03-27 01:17:19 -0800739 if (vga_video_type >= VIDEO_TYPE_VGAC)
740 vgacon_set_cursor_size(c->vc_x, 31, 30);
741 else
742 vgacon_set_cursor_size(c->vc_x, 31, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 break;
744 default:
745 vgacon_set_cursor_size(c->vc_x, 1,
746 c->vc_font.height);
747 break;
748 }
749 break;
750 }
751}
752
Samuel Thibault28254d42005-09-09 13:01:58 -0700753static int vgacon_doresize(struct vc_data *c,
754 unsigned int width, unsigned int height)
755{
756 unsigned long flags;
757 unsigned int scanlines = height * c->vc_font.height;
Samuel Thibaultd15212602006-02-24 13:03:59 -0800758 u8 scanlines_lo = 0, r7 = 0, vsync_end = 0, mode, max_scan;
Samuel Thibault28254d42005-09-09 13:01:58 -0700759
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200760 raw_spin_lock_irqsave(&vga_lock, flags);
Samuel Thibault28254d42005-09-09 13:01:58 -0700761
Antonino A. Daplas53dbb262006-01-08 01:02:36 -0800762 vgacon_xres = width * VGA_FONTWIDTH;
763 vgacon_yres = height * c->vc_font.height;
Samuel Thibaultd15212602006-02-24 13:03:59 -0800764 if (vga_video_type >= VIDEO_TYPE_VGAC) {
765 outb_p(VGA_CRTC_MAX_SCAN, vga_video_port_reg);
766 max_scan = inb_p(vga_video_port_val);
Samuel Thibault28254d42005-09-09 13:01:58 -0700767
Samuel Thibaultd15212602006-02-24 13:03:59 -0800768 if (max_scan & 0x80)
769 scanlines <<= 1;
Samuel Thibault28254d42005-09-09 13:01:58 -0700770
Samuel Thibaultd15212602006-02-24 13:03:59 -0800771 outb_p(VGA_CRTC_MODE, vga_video_port_reg);
772 mode = inb_p(vga_video_port_val);
Samuel Thibault28254d42005-09-09 13:01:58 -0700773
Samuel Thibaultd15212602006-02-24 13:03:59 -0800774 if (mode & 0x04)
775 scanlines >>= 1;
Samuel Thibault28254d42005-09-09 13:01:58 -0700776
Samuel Thibaultd15212602006-02-24 13:03:59 -0800777 scanlines -= 1;
778 scanlines_lo = scanlines & 0xff;
Samuel Thibault28254d42005-09-09 13:01:58 -0700779
Samuel Thibaultd15212602006-02-24 13:03:59 -0800780 outb_p(VGA_CRTC_OVERFLOW, vga_video_port_reg);
781 r7 = inb_p(vga_video_port_val) & ~0x42;
782
783 if (scanlines & 0x100)
784 r7 |= 0x02;
785 if (scanlines & 0x200)
786 r7 |= 0x40;
787
788 /* deprotect registers */
789 outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
790 vsync_end = inb_p(vga_video_port_val);
791 outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
792 outb_p(vsync_end & ~0x80, vga_video_port_val);
793 }
Samuel Thibault28254d42005-09-09 13:01:58 -0700794
795 outb_p(VGA_CRTC_H_DISP, vga_video_port_reg);
796 outb_p(width - 1, vga_video_port_val);
797 outb_p(VGA_CRTC_OFFSET, vga_video_port_reg);
798 outb_p(width >> 1, vga_video_port_val);
799
Samuel Thibaultd15212602006-02-24 13:03:59 -0800800 if (vga_video_type >= VIDEO_TYPE_VGAC) {
801 outb_p(VGA_CRTC_V_DISP_END, vga_video_port_reg);
802 outb_p(scanlines_lo, vga_video_port_val);
803 outb_p(VGA_CRTC_OVERFLOW, vga_video_port_reg);
804 outb_p(r7,vga_video_port_val);
Samuel Thibault28254d42005-09-09 13:01:58 -0700805
Samuel Thibaultd15212602006-02-24 13:03:59 -0800806 /* reprotect registers */
807 outb_p(VGA_CRTC_V_SYNC_END, vga_video_port_reg);
808 outb_p(vsync_end, vga_video_port_val);
809 }
Samuel Thibault28254d42005-09-09 13:01:58 -0700810
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200811 raw_spin_unlock_irqrestore(&vga_lock, flags);
Samuel Thibault28254d42005-09-09 13:01:58 -0700812 return 0;
813}
814
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815static int vgacon_switch(struct vc_data *c)
816{
Antonino A. Daplas53dbb262006-01-08 01:02:36 -0800817 int x = c->vc_cols * VGA_FONTWIDTH;
818 int y = c->vc_rows * c->vc_font.height;
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700819 int rows = screen_info.orig_video_lines * vga_default_font_height/
Antonino A. Daplas53dbb262006-01-08 01:02:36 -0800820 c->vc_font.height;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 /*
822 * We need to save screen size here as it's the only way
823 * we can spot the screen has been resized and we need to
824 * set size of freshly allocated screens ourselves.
825 */
826 vga_video_num_columns = c->vc_cols;
827 vga_video_num_lines = c->vc_rows;
James Simmonsf18cd8f2005-06-21 17:17:00 -0700828
829 /* We can only copy out the size of the video buffer here,
830 * otherwise we get into VGA BIOS */
831
Samuel Thibault28254d42005-09-09 13:01:58 -0700832 if (!vga_is_gfx) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf,
Samuel Thibault28254d42005-09-09 13:01:58 -0700834 c->vc_screenbuf_size > vga_vram_size ?
835 vga_vram_size : c->vc_screenbuf_size);
Antonino A. Daplas53dbb262006-01-08 01:02:36 -0800836
837 if ((vgacon_xres != x || vgacon_yres != y) &&
838 (!(vga_video_num_columns % 2) &&
H. Peter Anvin3ea33512007-10-16 22:36:04 -0700839 vga_video_num_columns <= screen_info.orig_video_cols &&
Antonino A. Daplas53dbb262006-01-08 01:02:36 -0800840 vga_video_num_lines <= rows))
Samuel Thibault0aec4862005-10-16 20:29:22 -0700841 vgacon_doresize(c, c->vc_cols, c->vc_rows);
Samuel Thibault28254d42005-09-09 13:01:58 -0700842 }
843
Manuel Schöllingaabd31c2017-01-13 21:07:57 +0100844 vgacon_scrollback_switch(c->vc_num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 return 0; /* Redrawing not needed */
846}
847
Jiri Slaby8ede5cc2016-03-31 10:08:16 +0200848static void vga_set_palette(struct vc_data *vc, const unsigned char *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849{
850 int i, j;
851
Mark Rustad89f02442014-10-14 04:53:49 -0700852 vga_w(vgastate.vgabase, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 for (i = j = 0; i < 16; i++) {
Mark Rustad89f02442014-10-14 04:53:49 -0700854 vga_w(vgastate.vgabase, VGA_PEL_IW, table[i]);
855 vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
856 vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
857 vga_w(vgastate.vgabase, VGA_PEL_D, vc->vc_palette[j++] >> 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 }
859}
860
Jiri Slaby709280d2016-06-23 13:34:27 +0200861static void vgacon_set_palette(struct vc_data *vc, const unsigned char *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 if (vga_video_type != VIDEO_TYPE_VGAC || vga_palette_blanked
Jiri Slaby6ca8dfd2016-06-23 13:34:35 +0200864 || !con_is_visible(vc))
Jiri Slaby709280d2016-06-23 13:34:27 +0200865 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 vga_set_palette(vc, table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867}
868
869/* structure holding original VGA register settings */
870static struct {
871 unsigned char SeqCtrlIndex; /* Sequencer Index reg. */
872 unsigned char CrtCtrlIndex; /* CRT-Contr. Index reg. */
873 unsigned char CrtMiscIO; /* Miscellaneous register */
874 unsigned char HorizontalTotal; /* CRT-Controller:00h */
875 unsigned char HorizDisplayEnd; /* CRT-Controller:01h */
876 unsigned char StartHorizRetrace; /* CRT-Controller:04h */
877 unsigned char EndHorizRetrace; /* CRT-Controller:05h */
878 unsigned char Overflow; /* CRT-Controller:07h */
879 unsigned char StartVertRetrace; /* CRT-Controller:10h */
880 unsigned char EndVertRetrace; /* CRT-Controller:11h */
881 unsigned char ModeControl; /* CRT-Controller:17h */
882 unsigned char ClockingMode; /* Seq-Controller:01h */
883} vga_state;
884
885static void vga_vesa_blank(struct vgastate *state, int mode)
886{
887 /* save original values of VGA controller registers */
888 if (!vga_vesa_blanked) {
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200889 raw_spin_lock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 vga_state.SeqCtrlIndex = vga_r(state->vgabase, VGA_SEQ_I);
891 vga_state.CrtCtrlIndex = inb_p(vga_video_port_reg);
892 vga_state.CrtMiscIO = vga_r(state->vgabase, VGA_MIS_R);
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200893 raw_spin_unlock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
895 outb_p(0x00, vga_video_port_reg); /* HorizontalTotal */
896 vga_state.HorizontalTotal = inb_p(vga_video_port_val);
897 outb_p(0x01, vga_video_port_reg); /* HorizDisplayEnd */
898 vga_state.HorizDisplayEnd = inb_p(vga_video_port_val);
899 outb_p(0x04, vga_video_port_reg); /* StartHorizRetrace */
900 vga_state.StartHorizRetrace = inb_p(vga_video_port_val);
901 outb_p(0x05, vga_video_port_reg); /* EndHorizRetrace */
902 vga_state.EndHorizRetrace = inb_p(vga_video_port_val);
903 outb_p(0x07, vga_video_port_reg); /* Overflow */
904 vga_state.Overflow = inb_p(vga_video_port_val);
905 outb_p(0x10, vga_video_port_reg); /* StartVertRetrace */
906 vga_state.StartVertRetrace = inb_p(vga_video_port_val);
907 outb_p(0x11, vga_video_port_reg); /* EndVertRetrace */
908 vga_state.EndVertRetrace = inb_p(vga_video_port_val);
909 outb_p(0x17, vga_video_port_reg); /* ModeControl */
910 vga_state.ModeControl = inb_p(vga_video_port_val);
911 vga_state.ClockingMode = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE);
912 }
913
914 /* assure that video is enabled */
915 /* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200916 raw_spin_lock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, vga_state.ClockingMode | 0x20);
918
919 /* test for vertical retrace in process.... */
920 if ((vga_state.CrtMiscIO & 0x80) == 0x80)
921 vga_w(state->vgabase, VGA_MIS_W, vga_state.CrtMiscIO & 0xEF);
922
923 /*
924 * Set <End of vertical retrace> to minimum (0) and
925 * <Start of vertical Retrace> to maximum (incl. overflow)
926 * Result: turn off vertical sync (VSync) pulse.
927 */
928 if (mode & VESA_VSYNC_SUSPEND) {
929 outb_p(0x10, vga_video_port_reg); /* StartVertRetrace */
930 outb_p(0xff, vga_video_port_val); /* maximum value */
931 outb_p(0x11, vga_video_port_reg); /* EndVertRetrace */
932 outb_p(0x40, vga_video_port_val); /* minimum (bits 0..3) */
933 outb_p(0x07, vga_video_port_reg); /* Overflow */
934 outb_p(vga_state.Overflow | 0x84, vga_video_port_val); /* bits 9,10 of vert. retrace */
935 }
936
937 if (mode & VESA_HSYNC_SUSPEND) {
938 /*
939 * Set <End of horizontal retrace> to minimum (0) and
940 * <Start of horizontal Retrace> to maximum
941 * Result: turn off horizontal sync (HSync) pulse.
942 */
943 outb_p(0x04, vga_video_port_reg); /* StartHorizRetrace */
944 outb_p(0xff, vga_video_port_val); /* maximum */
945 outb_p(0x05, vga_video_port_reg); /* EndHorizRetrace */
946 outb_p(0x00, vga_video_port_val); /* minimum (0) */
947 }
948
949 /* restore both index registers */
950 vga_w(state->vgabase, VGA_SEQ_I, vga_state.SeqCtrlIndex);
951 outb_p(vga_state.CrtCtrlIndex, vga_video_port_reg);
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200952 raw_spin_unlock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953}
954
955static void vga_vesa_unblank(struct vgastate *state)
956{
957 /* restore original values of VGA controller registers */
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200958 raw_spin_lock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 vga_w(state->vgabase, VGA_MIS_W, vga_state.CrtMiscIO);
960
961 outb_p(0x00, vga_video_port_reg); /* HorizontalTotal */
962 outb_p(vga_state.HorizontalTotal, vga_video_port_val);
963 outb_p(0x01, vga_video_port_reg); /* HorizDisplayEnd */
964 outb_p(vga_state.HorizDisplayEnd, vga_video_port_val);
965 outb_p(0x04, vga_video_port_reg); /* StartHorizRetrace */
966 outb_p(vga_state.StartHorizRetrace, vga_video_port_val);
967 outb_p(0x05, vga_video_port_reg); /* EndHorizRetrace */
968 outb_p(vga_state.EndHorizRetrace, vga_video_port_val);
969 outb_p(0x07, vga_video_port_reg); /* Overflow */
970 outb_p(vga_state.Overflow, vga_video_port_val);
971 outb_p(0x10, vga_video_port_reg); /* StartVertRetrace */
972 outb_p(vga_state.StartVertRetrace, vga_video_port_val);
973 outb_p(0x11, vga_video_port_reg); /* EndVertRetrace */
974 outb_p(vga_state.EndVertRetrace, vga_video_port_val);
975 outb_p(0x17, vga_video_port_reg); /* ModeControl */
976 outb_p(vga_state.ModeControl, vga_video_port_val);
977 /* ClockingMode */
978 vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, vga_state.ClockingMode);
979
980 /* restore index/control registers */
981 vga_w(state->vgabase, VGA_SEQ_I, vga_state.SeqCtrlIndex);
982 outb_p(vga_state.CrtCtrlIndex, vga_video_port_reg);
Thomas Gleixner6b2c1802009-07-25 16:17:02 +0200983 raw_spin_unlock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984}
985
986static void vga_pal_blank(struct vgastate *state)
987{
988 int i;
989
Pozsar Balazs1a66ddc2005-10-30 15:03:06 -0800990 vga_w(state->vgabase, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 for (i = 0; i < 16; i++) {
992 vga_w(state->vgabase, VGA_PEL_IW, i);
993 vga_w(state->vgabase, VGA_PEL_D, 0);
994 vga_w(state->vgabase, VGA_PEL_D, 0);
995 vga_w(state->vgabase, VGA_PEL_D, 0);
996 }
997}
998
999static int vgacon_blank(struct vc_data *c, int blank, int mode_switch)
1000{
1001 switch (blank) {
1002 case 0: /* Unblank */
1003 if (vga_vesa_blanked) {
Mark Rustad89f02442014-10-14 04:53:49 -07001004 vga_vesa_unblank(&vgastate);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 vga_vesa_blanked = 0;
1006 }
1007 if (vga_palette_blanked) {
1008 vga_set_palette(c, color_table);
Jiri Slaby96fd9552016-10-03 11:18:38 +02001009 vga_palette_blanked = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 return 0;
1011 }
Jiri Slaby96fd9552016-10-03 11:18:38 +02001012 vga_is_gfx = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 /* Tell console.c that it has to restore the screen itself */
1014 return 1;
1015 case 1: /* Normal blanking */
1016 case -1: /* Obsolete */
1017 if (!mode_switch && vga_video_type == VIDEO_TYPE_VGAC) {
Mark Rustad89f02442014-10-14 04:53:49 -07001018 vga_pal_blank(&vgastate);
Jiri Slaby96fd9552016-10-03 11:18:38 +02001019 vga_palette_blanked = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 return 0;
1021 }
1022 vgacon_set_origin(c);
1023 scr_memsetw((void *) vga_vram_base, BLANK,
1024 c->vc_screenbuf_size);
1025 if (mode_switch)
Jiri Slaby96fd9552016-10-03 11:18:38 +02001026 vga_is_gfx = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 return 1;
1028 default: /* VESA blanking */
1029 if (vga_video_type == VIDEO_TYPE_VGAC) {
Mark Rustad89f02442014-10-14 04:53:49 -07001030 vga_vesa_blank(&vgastate, blank - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 vga_vesa_blanked = blank;
1032 }
1033 return 0;
1034 }
1035}
1036
1037/*
1038 * PIO_FONT support.
1039 *
1040 * The font loading code goes back to the codepage package by
1041 * Joel Hoffman (joel@wam.umd.edu). (He reports that the original
1042 * reference is: "From: p. 307 of _Programmer's Guide to PC & PS/2
1043 * Video Systems_ by Richard Wilton. 1987. Microsoft Press".)
1044 *
1045 * Change for certain monochrome monitors by Yury Shevchuck
1046 * (sizif@botik.yaroslavl.su).
1047 */
1048
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049#define colourmap 0xa0000
1050/* Pauline Middelink <middelin@polyware.iaf.nl> reports that we
1051 should use 0xA0000 for the bwmap as well.. */
1052#define blackwmap 0xa0000
1053#define cmapsz 8192
1054
Jiri Slaby96fd9552016-10-03 11:18:38 +02001055static int vgacon_do_font_op(struct vgastate *state, char *arg, int set,
1056 bool ch512)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057{
1058 unsigned short video_port_status = vga_video_port_reg + 6;
1059 int font_select = 0x00, beg, i;
1060 char *charmap;
Dave Airlie2a248302013-01-24 14:14:19 +10001061 bool clear_attribs = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 if (vga_video_type != VIDEO_TYPE_EGAM) {
Bjorn Helgaas4f1bcaf2006-06-22 14:47:32 -07001063 charmap = (char *) VGA_MAP_MEM(colourmap, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 beg = 0x0e;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 } else {
Bjorn Helgaas4f1bcaf2006-06-22 14:47:32 -07001066 charmap = (char *) VGA_MAP_MEM(blackwmap, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 beg = 0x0a;
1068 }
1069
1070#ifdef BROKEN_GRAPHICS_PROGRAMS
1071 /*
1072 * All fonts are loaded in slot 0 (0:1 for 512 ch)
1073 */
1074
1075 if (!arg)
1076 return -EINVAL; /* Return to default font not supported */
1077
Jiri Slaby96fd9552016-10-03 11:18:38 +02001078 vga_font_is_default = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 font_select = ch512 ? 0x04 : 0x00;
1080#else
1081 /*
1082 * The default font is kept in slot 0 and is never touched.
1083 * A custom font is loaded in slot 2 (256 ch) or 2:3 (512 ch)
1084 */
1085
1086 if (set) {
1087 vga_font_is_default = !arg;
1088 if (!arg)
Jiri Slaby96fd9552016-10-03 11:18:38 +02001089 ch512 = false; /* Default font is always 256 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 font_select = arg ? (ch512 ? 0x0e : 0x0a) : 0x00;
1091 }
1092
1093 if (!vga_font_is_default)
1094 charmap += 4 * cmapsz;
1095#endif
1096
Thomas Gleixner6b2c1802009-07-25 16:17:02 +02001097 raw_spin_lock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 /* First, the Sequencer */
1099 vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
1100 /* CPU writes only to map 2 */
1101 vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x04);
1102 /* Sequential addressing */
1103 vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x07);
1104 /* Clear synchronous reset */
1105 vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03);
1106
1107 /* Now, the graphics controller, select map 2 */
1108 vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x02);
1109 /* disable odd-even addressing */
1110 vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x00);
1111 /* map start at A000:0000 */
1112 vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x00);
Thomas Gleixner6b2c1802009-07-25 16:17:02 +02001113 raw_spin_unlock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
1115 if (arg) {
1116 if (set)
Marcelo Tosatti7e6d72c2013-06-20 18:05:56 -03001117 for (i = 0; i < cmapsz; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 vga_writeb(arg[i], charmap + i);
Marcelo Tosatti7e6d72c2013-06-20 18:05:56 -03001119 cond_resched();
1120 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 else
Marcelo Tosatti7e6d72c2013-06-20 18:05:56 -03001122 for (i = 0; i < cmapsz; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 arg[i] = vga_readb(charmap + i);
Marcelo Tosatti7e6d72c2013-06-20 18:05:56 -03001124 cond_resched();
1125 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126
1127 /*
1128 * In 512-character mode, the character map is not contiguous if
1129 * we want to remain EGA compatible -- which we do
1130 */
1131
1132 if (ch512) {
1133 charmap += 2 * cmapsz;
1134 arg += cmapsz;
1135 if (set)
Marcelo Tosatti7e6d72c2013-06-20 18:05:56 -03001136 for (i = 0; i < cmapsz; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 vga_writeb(arg[i], charmap + i);
Marcelo Tosatti7e6d72c2013-06-20 18:05:56 -03001138 cond_resched();
1139 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 else
Marcelo Tosatti7e6d72c2013-06-20 18:05:56 -03001141 for (i = 0; i < cmapsz; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 arg[i] = vga_readb(charmap + i);
Marcelo Tosatti7e6d72c2013-06-20 18:05:56 -03001143 cond_resched();
1144 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 }
1146 }
1147
Thomas Gleixner6b2c1802009-07-25 16:17:02 +02001148 raw_spin_lock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 /* First, the sequencer, Synchronous reset */
1150 vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x01);
1151 /* CPU writes to maps 0 and 1 */
1152 vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x03);
1153 /* odd-even addressing */
1154 vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x03);
1155 /* Character Map Select */
1156 if (set)
1157 vga_wseq(state->vgabase, VGA_SEQ_CHARACTER_MAP, font_select);
1158 /* clear synchronous reset */
1159 vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03);
1160
1161 /* Now, the graphics controller, select map 0 for CPU */
1162 vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x00);
1163 /* enable even-odd addressing */
1164 vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x10);
1165 /* map starts at b800:0 or b000:0 */
1166 vga_wgfx(state->vgabase, VGA_GFX_MISC, beg);
1167
1168 /* if 512 char mode is already enabled don't re-enable it. */
1169 if ((set) && (ch512 != vga_512_chars)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 vga_512_chars = ch512;
1171 /* 256-char: enable intensity bit
1172 512-char: disable intensity bit */
1173 inb_p(video_port_status); /* clear address flip-flop */
1174 /* color plane enable register */
1175 vga_wattr(state->vgabase, VGA_ATC_PLANE_ENABLE, ch512 ? 0x07 : 0x0f);
1176 /* Wilton (1987) mentions the following; I don't know what
1177 it means, but it works, and it appears necessary */
1178 inb_p(video_port_status);
1179 vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);
Dave Airlie2a248302013-01-24 14:14:19 +10001180 clear_attribs = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 }
Thomas Gleixner6b2c1802009-07-25 16:17:02 +02001182 raw_spin_unlock_irq(&vga_lock);
Dave Airlie2a248302013-01-24 14:14:19 +10001183
1184 if (clear_attribs) {
1185 for (i = 0; i < MAX_NR_CONSOLES; i++) {
1186 struct vc_data *c = vc_cons[i].d;
1187 if (c && c->vc_sw == &vga_con) {
1188 /* force hi font mask to 0, so we always clear
1189 the bit on either transition */
1190 c->vc_hi_font_mask = 0x00;
1191 clear_buffer_attributes(c);
1192 c->vc_hi_font_mask = ch512 ? 0x0800 : 0;
1193 }
1194 }
1195 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 return 0;
1197}
1198
1199/*
1200 * Adjust the screen to fit a font of a certain height
1201 */
1202static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
1203{
1204 unsigned char ovr, vde, fsr;
1205 int rows, maxscan, i;
1206
1207 rows = vc->vc_scan_lines / fontheight; /* Number of video rows we end up with */
1208 maxscan = rows * fontheight - 1; /* Scan lines to actually display-1 */
1209
1210 /* Reprogram the CRTC for the new font size
1211 Note: the attempt to read the overflow register will fail
1212 on an EGA, but using 0xff for the previous value appears to
1213 be OK for EGA text modes in the range 257-512 scan lines, so I
1214 guess we don't need to worry about it.
1215
1216 The same applies for the spill bits in the font size and cursor
1217 registers; they are write-only on EGA, but it appears that they
1218 are all don't care bits on EGA, so I guess it doesn't matter. */
1219
Thomas Gleixner6b2c1802009-07-25 16:17:02 +02001220 raw_spin_lock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 outb_p(0x07, vga_video_port_reg); /* CRTC overflow register */
1222 ovr = inb_p(vga_video_port_val);
1223 outb_p(0x09, vga_video_port_reg); /* Font size register */
1224 fsr = inb_p(vga_video_port_val);
Thomas Gleixner6b2c1802009-07-25 16:17:02 +02001225 raw_spin_unlock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226
1227 vde = maxscan & 0xff; /* Vertical display end reg */
1228 ovr = (ovr & 0xbd) + /* Overflow register */
1229 ((maxscan & 0x100) >> 7) + ((maxscan & 0x200) >> 3);
1230 fsr = (fsr & 0xe0) + (fontheight - 1); /* Font size register */
1231
Thomas Gleixner6b2c1802009-07-25 16:17:02 +02001232 raw_spin_lock_irq(&vga_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 outb_p(0x07, vga_video_port_reg); /* CRTC overflow register */
1234 outb_p(ovr, vga_video_port_val);
1235 outb_p(0x09, vga_video_port_reg); /* Font size */
1236 outb_p(fsr, vga_video_port_val);
1237 outb_p(0x12, vga_video_port_reg); /* Vertical display limit */
1238 outb_p(vde, vga_video_port_val);
Thomas Gleixner6b2c1802009-07-25 16:17:02 +02001239 raw_spin_unlock_irq(&vga_lock);
Antonino A. Daplas5ef897c2005-11-21 21:32:26 -08001240 vga_video_font_height = fontheight;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241
1242 for (i = 0; i < MAX_NR_CONSOLES; i++) {
1243 struct vc_data *c = vc_cons[i].d;
1244
1245 if (c && c->vc_sw == &vga_con) {
Jiri Slaby6ca8dfd2016-06-23 13:34:35 +02001246 if (con_is_visible(c)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 /* void size to cause regs to be rewritten */
1248 cursor_size_lastfrom = 0;
1249 cursor_size_lastto = 0;
1250 c->vc_sw->con_cursor(c, CM_DRAW);
1251 }
1252 c->vc_font.height = fontheight;
1253 vc_resize(c, 0, rows); /* Adjust console size */
1254 }
1255 }
1256 return 0;
1257}
1258
1259static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigned flags)
1260{
1261 unsigned charcount = font->charcount;
1262 int rc;
1263
1264 if (vga_video_type < VIDEO_TYPE_EGAM)
1265 return -EINVAL;
1266
Antonino A. Daplas53dbb262006-01-08 01:02:36 -08001267 if (font->width != VGA_FONTWIDTH ||
1268 (charcount != 256 && charcount != 512))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 return -EINVAL;
1270
Mark Rustad89f02442014-10-14 04:53:49 -07001271 rc = vgacon_do_font_op(&vgastate, font->data, 1, charcount == 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 if (rc)
1273 return rc;
1274
1275 if (!(flags & KD_FONT_FLAG_DONT_RECALC))
1276 rc = vgacon_adjust_height(c, font->height);
1277 return rc;
1278}
1279
1280static int vgacon_font_get(struct vc_data *c, struct console_font *font)
1281{
1282 if (vga_video_type < VIDEO_TYPE_EGAM)
1283 return -EINVAL;
1284
Antonino A. Daplas53dbb262006-01-08 01:02:36 -08001285 font->width = VGA_FONTWIDTH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 font->height = c->vc_font.height;
1287 font->charcount = vga_512_chars ? 512 : 256;
1288 if (!font->data)
1289 return 0;
Mark Rustad89f02442014-10-14 04:53:49 -07001290 return vgacon_do_font_op(&vgastate, font->data, 0, vga_512_chars);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291}
1292
Samuel Thibault28254d42005-09-09 13:01:58 -07001293static int vgacon_resize(struct vc_data *c, unsigned int width,
Antonino A. Daplase400b6e2007-10-16 01:29:35 -07001294 unsigned int height, unsigned int user)
Samuel Thibault28254d42005-09-09 13:01:58 -07001295{
H. Peter Anvin3ea33512007-10-16 22:36:04 -07001296 if (width % 2 || width > screen_info.orig_video_cols ||
1297 height > (screen_info.orig_video_lines * vga_default_font_height)/
Antonino A. Daplas6d36ba62005-09-15 21:14:56 +08001298 c->vc_font.height)
Antonino A. Daplase400b6e2007-10-16 01:29:35 -07001299 /* let svgatextmode tinker with video timings and
1300 return success */
1301 return (user) ? 0 : -EINVAL;
Samuel Thibault28254d42005-09-09 13:01:58 -07001302
Jiri Slaby6ca8dfd2016-06-23 13:34:35 +02001303 if (con_is_visible(c) && !vga_is_gfx) /* who knows */
Samuel Thibault28254d42005-09-09 13:01:58 -07001304 vgacon_doresize(c, width, height);
1305 return 0;
1306}
1307
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308static int vgacon_set_origin(struct vc_data *c)
1309{
1310 if (vga_is_gfx || /* We don't play origin tricks in graphic modes */
1311 (console_blanked && !vga_palette_blanked)) /* Nor we write to blanked screens */
1312 return 0;
1313 c->vc_origin = c->vc_visible_origin = vga_vram_base;
1314 vga_set_mem_top(c);
1315 vga_rolled_over = 0;
1316 return 1;
1317}
1318
1319static void vgacon_save_screen(struct vc_data *c)
1320{
1321 static int vga_bootup_console = 0;
1322
1323 if (!vga_bootup_console) {
1324 /* This is a gross hack, but here is the only place we can
1325 * set bootup console parameters without messing up generic
1326 * console initialization routines.
1327 */
1328 vga_bootup_console = 1;
H. Peter Anvin3ea33512007-10-16 22:36:04 -07001329 c->vc_x = screen_info.orig_x;
1330 c->vc_y = screen_info.orig_y;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 }
James Simmonsf18cd8f2005-06-21 17:17:00 -07001332
Frederik Schwarzer025dfda2008-10-16 19:02:37 +02001333 /* We can't copy in more than the size of the video buffer,
James Simmonsf18cd8f2005-06-21 17:17:00 -07001334 * or we'll be copying in VGA BIOS */
1335
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 if (!vga_is_gfx)
1337 scr_memcpyw((u16 *) c->vc_screenbuf, (u16 *) c->vc_origin,
James Simmonsf18cd8f2005-06-21 17:17:00 -07001338 c->vc_screenbuf_size > vga_vram_size ? vga_vram_size : c->vc_screenbuf_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339}
1340
Jiri Slabyd705ff32016-10-03 11:18:33 +02001341static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
1342 enum con_scroll dir, unsigned int lines)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343{
1344 unsigned long oldo;
1345 unsigned int delta;
1346
Antonino A. Daplas2ae85472007-05-08 00:40:06 -07001347 if (t || b != c->vc_rows || vga_is_gfx || c->vc_mode != KD_TEXT)
Jiri Slabyd705ff32016-10-03 11:18:33 +02001348 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 if (!vga_hardscroll_enabled || lines >= c->vc_rows / 2)
Jiri Slabyd705ff32016-10-03 11:18:33 +02001351 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
Antonino A. Daplas15bdab92006-03-27 01:17:20 -08001353 vgacon_restore_screen(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 oldo = c->vc_origin;
1355 delta = lines * c->vc_size_row;
1356 if (dir == SM_UP) {
Antonino A. Daplas15bdab92006-03-27 01:17:20 -08001357 vgacon_scrollback_update(c, t, lines);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 if (c->vc_scr_end + delta >= vga_vram_end) {
1359 scr_memcpyw((u16 *) vga_vram_base,
1360 (u16 *) (oldo + delta),
1361 c->vc_screenbuf_size - delta);
1362 c->vc_origin = vga_vram_base;
1363 vga_rolled_over = oldo - vga_vram_base;
1364 } else
1365 c->vc_origin += delta;
1366 scr_memsetw((u16 *) (c->vc_origin + c->vc_screenbuf_size -
Linus Torvalds93f78da2008-10-14 12:12:02 -07001367 delta), c->vc_video_erase_char,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 delta);
1369 } else {
1370 if (oldo - delta < vga_vram_base) {
1371 scr_memmovew((u16 *) (vga_vram_end -
1372 c->vc_screenbuf_size +
1373 delta), (u16 *) oldo,
1374 c->vc_screenbuf_size - delta);
1375 c->vc_origin = vga_vram_end - c->vc_screenbuf_size;
1376 vga_rolled_over = 0;
1377 } else
1378 c->vc_origin -= delta;
1379 c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
Linus Torvalds93f78da2008-10-14 12:12:02 -07001380 scr_memsetw((u16 *) (c->vc_origin), c->vc_video_erase_char,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 delta);
1382 }
1383 c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
1384 c->vc_visible_origin = c->vc_origin;
1385 vga_set_mem_top(c);
1386 c->vc_pos = (c->vc_pos - oldo) + c->vc_origin;
Jiri Slabyd705ff32016-10-03 11:18:33 +02001387 return true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388}
1389
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390/*
1391 * The console `switch' structure for the VGA based console
1392 */
1393
1394static int vgacon_dummy(struct vc_data *c)
1395{
1396 return 0;
1397}
1398
1399#define DUMMY (void *) vgacon_dummy
1400
1401const struct consw vga_con = {
1402 .owner = THIS_MODULE,
1403 .con_startup = vgacon_startup,
1404 .con_init = vgacon_init,
1405 .con_deinit = vgacon_deinit,
1406 .con_clear = DUMMY,
1407 .con_putc = DUMMY,
1408 .con_putcs = DUMMY,
1409 .con_cursor = vgacon_cursor,
1410 .con_scroll = vgacon_scroll,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 .con_switch = vgacon_switch,
1412 .con_blank = vgacon_blank,
1413 .con_font_set = vgacon_font_set,
1414 .con_font_get = vgacon_font_get,
Samuel Thibault28254d42005-09-09 13:01:58 -07001415 .con_resize = vgacon_resize,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 .con_set_palette = vgacon_set_palette,
1417 .con_scrolldelta = vgacon_scrolldelta,
1418 .con_set_origin = vgacon_set_origin,
1419 .con_save_screen = vgacon_save_screen,
1420 .con_build_attr = vgacon_build_attr,
1421 .con_invert_region = vgacon_invert_region,
Manuel Schöllingbcd375f2017-01-13 21:07:56 +01001422 .con_flush_scrollback = vgacon_flush_scrollback,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423};
Daniel Vettera4de0522014-06-05 16:20:46 +02001424EXPORT_SYMBOL(vga_con);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425
1426MODULE_LICENSE("GPL");