2 * linux/drivers/video/vga16.c -- VGA 16-color framebuffer driver
4 * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
5 * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
6 * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
8 * This file is subject to the terms and conditions of the GNU General
9 * Public License. See the file COPYING in the main directory of this
10 * archive for more details. */
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/errno.h>
15 #include <linux/string.h>
17 #include <linux/tty.h>
18 #include <linux/malloc.h>
19 #include <linux/delay.h>
21 #include <linux/console.h>
22 #include <linux/selection.h>
23 #include <linux/ioport.h>
24 #include <linux/init.h>
28 #include <video/fbcon.h>
29 #include <video/fbcon-vga-planes.h>
32 #define dac_reg (0x3c8)
33 #define dac_val (0x3c9)
35 #define VGA_FB_PHYS 0xA0000
36 #define VGA_FB_PHYS_LEN 65535
38 /* --------------------------------------------------------------------- */
44 static struct vga16fb_info
{
45 struct fb_info fb_info
;
46 char *video_vbase
; /* 0xa0000 map address */
49 /* structure holding original VGA register settings when the
52 unsigned char SeqCtrlIndex
; /* Sequencer Index reg. */
53 unsigned char CrtCtrlIndex
; /* CRT-Contr. Index reg. */
54 unsigned char CrtMiscIO
; /* Miscellaneous register */
55 unsigned char HorizontalTotal
; /* CRT-Controller:00h */
56 unsigned char HorizDisplayEnd
; /* CRT-Controller:01h */
57 unsigned char StartHorizRetrace
; /* CRT-Controller:04h */
58 unsigned char EndHorizRetrace
; /* CRT-Controller:05h */
59 unsigned char Overflow
; /* CRT-Controller:07h */
60 unsigned char StartVertRetrace
; /* CRT-Controller:10h */
61 unsigned char EndVertRetrace
; /* CRT-Controller:11h */
62 unsigned char ModeControl
; /* CRT-Controller:17h */
63 unsigned char ClockingMode
; /* Seq-Controller:01h */
78 struct fb_var_screeninfo var
;
81 /* --------------------------------------------------------------------- */
83 static struct fb_var_screeninfo vga16fb_defined
= {
84 640,480,640,480,/* W,H, W, H (virtual) load xres,xres_virtual*/
85 0,0, /* virtual -> visible no offset */
86 4, /* depth -> load bits_per_pixel */
91 {0,0,0}, /* transparency */
92 0, /* standard pixel format */
97 96, 2, 0, /* No sync info */
98 FB_VMODE_NONINTERLACED
,
102 static struct display disp
;
103 static struct { u_short blue
, green
, red
, pad
; } palette
[256];
105 static int currcon
= 0;
107 /* --------------------------------------------------------------------- */
109 static void vga16fb_pan_var(struct fb_info
*info
, struct fb_var_screeninfo
*var
)
111 u32 pos
= (var
->xres_virtual
* var
->yoffset
+ var
->xoffset
) >> 3;
112 outb(VGA_CRTC_START_HI
, VGA_CRT_IC
);
113 outb(pos
>> 8, VGA_CRT_DC
);
114 outb(VGA_CRTC_START_LO
, VGA_CRT_IC
);
115 outb(pos
& 0xFF, VGA_CRT_DC
);
117 /* if someone supports xoffset in bit resolution */
118 inb(VGA_IS1_RC
); /* reset flip-flop */
119 outb(VGA_ATC_PEL
, VGA_ATT_IW
);
120 outb(xoffset
& 7, VGA_ATT_IW
);
122 outb(0x20, VGA_ATT_IW
);
126 static int vga16fb_update_var(int con
, struct fb_info
*info
)
128 vga16fb_pan_var(info
, &fb_display
[con
].var
);
132 static int vga16fb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
133 struct fb_info
*info
)
140 p
= fb_display
+ con
;
142 memset(fix
, 0, sizeof(struct fb_fix_screeninfo
));
143 strcpy(fix
->id
,"VGA16 VGA");
145 fix
->smem_start
= VGA_FB_PHYS
;
146 fix
->smem_len
= VGA_FB_PHYS_LEN
;
147 fix
->type
= FB_TYPE_VGA_PLANES
;
148 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
152 fix
->line_length
= p
->var
.xres_virtual
/ 8;
156 static int vga16fb_get_var(struct fb_var_screeninfo
*var
, int con
,
157 struct fb_info
*info
)
160 memcpy(var
, &vga16fb_defined
, sizeof(struct fb_var_screeninfo
));
162 *var
=fb_display
[con
].var
;
166 static void vga16fb_set_disp(int con
, struct vga16fb_info
*info
)
168 struct fb_fix_screeninfo fix
;
169 struct display
*display
;
174 display
= fb_display
+ con
;
177 vga16fb_get_fix(&fix
, con
, &info
->fb_info
);
179 display
->screen_base
= info
->video_vbase
;
180 display
->visual
= fix
.visual
;
181 display
->type
= fix
.type
;
182 display
->type_aux
= fix
.type_aux
;
183 display
->ypanstep
= fix
.ypanstep
;
184 display
->ywrapstep
= fix
.ywrapstep
;
185 display
->line_length
= fix
.line_length
;
186 display
->next_line
= fix
.line_length
;
187 display
->can_soft_blank
= 1;
188 display
->inverse
= 0;
191 display
->dispsw
= &fbcon_vga_planes
;
193 display
->dispsw
= &fbcon_ega_planes
;
194 display
->scrollmode
= SCROLL_YREDRAW
;
197 static void vga16fb_encode_var(struct fb_var_screeninfo
*var
,
198 const struct vga16fb_par
*par
,
199 const struct vga16fb_info
*info
)
204 static void vga16fb_clock_chip(struct vga16fb_par
*par
,
205 unsigned int pixclock
,
206 const struct vga16fb_info
*info
)
212 } *ptr
, *best
, vgaclocks
[] = {
213 { 79442 /* 12.587 */, 0x00, 0x08},
214 { 70616 /* 14.161 */, 0x04, 0x08},
215 { 39721 /* 25.175 */, 0x00, 0x00},
216 { 35308 /* 28.322 */, 0x04, 0x00},
217 { 0 /* bad */, 0x00, 0x00}};
221 err
= pixclock
- best
->pixclock
;
222 if (err
< 0) err
= -err
;
223 for (ptr
= vgaclocks
+ 1; ptr
->pixclock
; ptr
++) {
226 tmp
= pixclock
- ptr
->pixclock
;
227 if (tmp
< 0) tmp
= -tmp
;
233 par
->misc
|= best
->misc
;
234 par
->seq
[VGA_SEQ_CLOCK_MODE
] |= best
->seq_clock_mode
;
235 par
->var
.pixclock
= best
->pixclock
;
238 #define FAIL(X) return -EINVAL
240 static int vga16fb_decode_var(const struct fb_var_screeninfo
*var
,
241 struct vga16fb_par
*par
,
242 const struct vga16fb_info
*info
)
244 u32 xres
, right
, hslen
, left
, xtotal
;
245 u32 yres
, lower
, vslen
, upper
, ytotal
;
246 u32 vxres
, xoffset
, vyres
, yoffset
;
251 if (var
->bits_per_pixel
!= 4)
253 xres
= (var
->xres
+ 7) & ~7;
254 vxres
= (var
->xres_virtual
+ 0xF) & ~0xF;
255 xoffset
= (var
->xoffset
+ 7) & ~7;
256 left
= (var
->left_margin
+ 7) & ~7;
257 right
= (var
->right_margin
+ 7) & ~7;
258 hslen
= (var
->hsync_len
+ 7) & ~7;
262 if (xres
+ xoffset
> vxres
)
263 xoffset
= vxres
- xres
;
265 par
->var
.xres
= xres
;
266 par
->var
.right_margin
= right
;
267 par
->var
.hsync_len
= hslen
;
268 par
->var
.left_margin
= left
;
269 par
->var
.xres_virtual
= vxres
;
270 par
->var
.xoffset
= xoffset
;
277 xtotal
= xres
+ right
+ hslen
+ left
;
279 FAIL("xtotal too big");
281 FAIL("hslen too big");
282 if (right
+ hslen
+ left
> 64)
283 FAIL("hblank too big");
284 par
->crtc
[VGA_CRTC_H_TOTAL
] = xtotal
- 5;
285 par
->crtc
[VGA_CRTC_H_BLANK_START
] = xres
- 1;
286 par
->crtc
[VGA_CRTC_H_DISP
] = xres
- 1;
288 par
->crtc
[VGA_CRTC_H_SYNC_START
] = pos
;
290 par
->crtc
[VGA_CRTC_H_SYNC_END
] = pos
& 0x1F;
291 pos
+= left
- 2; /* blank_end + 2 <= total + 5 */
292 par
->crtc
[VGA_CRTC_H_BLANK_END
] = (pos
& 0x1F) | 0x80;
294 par
->crtc
[VGA_CRTC_H_SYNC_END
] |= 0x80;
297 lower
= var
->lower_margin
;
298 vslen
= var
->vsync_len
;
299 upper
= var
->upper_margin
;
300 vyres
= var
->yres_virtual
;
301 yoffset
= var
->yoffset
;
305 if (vxres
* vyres
> 65536) {
306 vyres
= 65536 / vxres
;
310 if (yoffset
+ yres
> vyres
)
311 yoffset
= vyres
- yres
;
312 par
->var
.yres
= yres
;
313 par
->var
.lower_margin
= lower
;
314 par
->var
.vsync_len
= vslen
;
315 par
->var
.upper_margin
= upper
;
316 par
->var
.yres_virtual
= vyres
;
317 par
->var
.yoffset
= yoffset
;
319 if (var
->vmode
& FB_VMODE_DOUBLE
) {
325 ytotal
= yres
+ lower
+ vslen
+ upper
;
336 FAIL("ytotal too big");
338 FAIL("vslen too big");
339 par
->crtc
[VGA_CRTC_V_TOTAL
] = ytotal
- 2;
340 r7
= 0x10; /* disable linecompare */
341 if (ytotal
& 0x100) r7
|= 0x01;
342 if (ytotal
& 0x200) r7
|= 0x20;
343 par
->crtc
[VGA_CRTC_PRESET_ROW
] = 0;
344 par
->crtc
[VGA_CRTC_MAX_SCAN
] = 0x40; /* 1 scanline, no linecmp */
345 par
->var
.vmode
= var
->vmode
;
346 if (var
->vmode
& FB_VMODE_DOUBLE
)
347 par
->crtc
[VGA_CRTC_MAX_SCAN
] |= 0x80;
348 par
->crtc
[VGA_CRTC_CURSOR_START
] = 0x20;
349 par
->crtc
[VGA_CRTC_CURSOR_END
] = 0x00;
350 pos
= yoffset
* vxres
+ (xoffset
>> 3);
351 par
->crtc
[VGA_CRTC_START_HI
] = pos
>> 8;
352 par
->crtc
[VGA_CRTC_START_LO
] = pos
& 0xFF;
353 par
->crtc
[VGA_CRTC_CURSOR_HI
] = 0x00;
354 par
->crtc
[VGA_CRTC_CURSOR_LO
] = 0x00;
356 par
->crtc
[VGA_CRTC_V_DISP_END
] = pos
& 0xFF;
357 par
->crtc
[VGA_CRTC_V_BLANK_START
] = pos
& 0xFF;
359 r7
|= 0x0A; /* 0x02 -> DISP_END, 0x08 -> BLANK_START */
361 r7
|= 0x40; /* 0x40 -> DISP_END */
362 par
->crtc
[VGA_CRTC_MAX_SCAN
] |= 0x20; /* BLANK_START */
365 par
->crtc
[VGA_CRTC_V_SYNC_START
] = pos
& 0xFF;
371 par
->crtc
[VGA_CRTC_V_SYNC_END
] = (pos
& 0x0F) & ~0x10; /* disabled IRQ */
372 pos
+= upper
- 1; /* blank_end + 1 <= ytotal + 2 */
373 par
->crtc
[VGA_CRTC_V_BLANK_END
] = pos
& 0xFF; /* 0x7F for original VGA,
374 but some SVGA chips requires all 8 bits to set */
376 FAIL("vxres too long");
377 par
->crtc
[VGA_CRTC_OFFSET
] = vxres
>> 1;
378 par
->crtc
[VGA_CRTC_UNDERLINE
] = 0x1F;
379 par
->crtc
[VGA_CRTC_MODE
] = rMode
| 0xE3;
380 par
->crtc
[VGA_CRTC_LINE_COMPARE
] = 0xFF;
381 par
->crtc
[VGA_CRTC_OVERFLOW
] = r7
;
383 par
->vss
= 0x00; /* 3DA */
385 for (i
= 0x00; i
< 0x10; i
++)
387 par
->atc
[VGA_ATC_MODE
] = 0x81;
388 par
->atc
[VGA_ATC_OVERSCAN
] = 0x00; /* 0 for EGA, 0xFF for VGA */
389 par
->atc
[VGA_ATC_PLANE_ENABLE
] = 0x0F;
390 par
->atc
[VGA_ATC_PEL
] = xoffset
& 7;
391 par
->atc
[VGA_ATC_COLOR_PAGE
] = 0x00;
393 par
->misc
= 0xC3; /* enable CPU, ports 0x3Dx, positive sync */
394 par
->var
.sync
= var
->sync
;
395 if (var
->sync
& FB_SYNC_HOR_HIGH_ACT
)
397 if (var
->sync
& FB_SYNC_VERT_HIGH_ACT
)
400 par
->seq
[VGA_SEQ_CLOCK_MODE
] = 0x01;
401 par
->seq
[VGA_SEQ_PLANE_WRITE
] = 0x0F;
402 par
->seq
[VGA_SEQ_CHARACTER_MAP
] = 0x00;
403 par
->seq
[VGA_SEQ_MEMORY_MODE
] = 0x06;
405 par
->gdc
[VGA_GFX_SR_VALUE
] = 0x00;
406 par
->gdc
[VGA_GFX_SR_ENABLE
] = 0x0F;
407 par
->gdc
[VGA_GFX_COMPARE_VALUE
] = 0x00;
408 par
->gdc
[VGA_GFX_DATA_ROTATE
] = 0x20;
409 par
->gdc
[VGA_GFX_PLANE_READ
] = 0;
410 par
->gdc
[VGA_GFX_MODE
] = 0x00;
411 par
->gdc
[VGA_GFX_MISC
] = 0x05;
412 par
->gdc
[VGA_GFX_COMPARE_MASK
] = 0x0F;
413 par
->gdc
[VGA_GFX_BIT_MASK
] = 0xFF;
415 vga16fb_clock_chip(par
, var
->pixclock
, info
);
417 par
->var
.bits_per_pixel
= 4;
418 par
->var
.grayscale
= var
->grayscale
;
419 par
->var
.red
.offset
= par
->var
.green
.offset
= par
->var
.blue
.offset
=
420 par
->var
.transp
.offset
= 0;
421 par
->var
.red
.length
= par
->var
.green
.length
= par
->var
.blue
.length
=
422 (info
->isVGA
) ? 6 : 2;
423 par
->var
.transp
.length
= 0;
425 par
->var
.activate
= FB_ACTIVATE_NOW
;
426 par
->var
.height
= -1;
428 par
->var
.accel_flags
= 0;
434 static int vga16fb_set_par(const struct vga16fb_par
*par
,
435 struct vga16fb_info
*info
)
439 outb(inb(VGA_MIS_R
) | 0x01, VGA_MIS_W
);
441 /* Enable graphics register modification */
443 outb(0x00, EGA_GFX_E0
);
444 outb(0x01, EGA_GFX_E1
);
447 /* update misc output register */
448 outb(par
->misc
, VGA_MIS_W
);
450 /* synchronous reset on */
451 outb(0x00, VGA_SEQ_I
);
452 outb(0x01, VGA_SEQ_D
);
454 /* write sequencer registers */
456 outb(par
->seq
[1] | 0x20, VGA_SEQ_D
);
457 for (i
= 2; i
< VGA_SEQ_C
; i
++) {
459 outb(par
->seq
[i
], VGA_SEQ_D
);
462 /* synchronous reset off */
463 outb(0x00, VGA_SEQ_I
);
464 outb(0x03, VGA_SEQ_D
);
466 /* deprotect CRT registers 0-7 */
467 outb(0x11, VGA_CRT_IC
);
468 outb(par
->crtc
[0x11], VGA_CRT_DC
);
470 /* write CRT registers */
471 for (i
= 0; i
< VGA_CRT_C
; i
++) {
473 outb(par
->crtc
[i
], VGA_CRT_DC
);
476 /* write graphics controller registers */
477 for (i
= 0; i
< VGA_GFX_C
; i
++) {
479 outb(par
->gdc
[i
], VGA_GFX_D
);
482 /* write attribute controller registers */
483 for (i
= 0; i
< VGA_ATT_C
; i
++) {
484 inb_p(VGA_IS1_RC
); /* reset flip-flop */
485 outb_p(i
, VGA_ATT_IW
);
486 outb_p(par
->atc
[i
], VGA_ATT_IW
);
489 /* Wait for screen to stabilize. */
492 outb(0x01, VGA_SEQ_I
);
493 outb(par
->seq
[1], VGA_SEQ_D
);
496 outb(0x20, VGA_ATT_IW
);
501 static int vga16fb_set_var(struct fb_var_screeninfo
*var
, int con
,
504 struct vga16fb_info
*info
= (struct vga16fb_info
*)fb
;
505 struct vga16fb_par par
;
506 struct display
*display
;
512 display
= fb_display
+ con
;
513 if ((err
= vga16fb_decode_var(var
, &par
, info
)) != 0)
515 vga16fb_encode_var(var
, &par
, info
);
517 if ((var
->activate
& FB_ACTIVATE_MASK
) == FB_ACTIVATE_TEST
)
520 if ((var
->activate
& FB_ACTIVATE_MASK
) == FB_ACTIVATE_NOW
) {
521 u32 oldxres
, oldyres
, oldvxres
, oldvyres
, oldbpp
;
523 oldxres
= display
->var
.xres
;
524 oldyres
= display
->var
.yres
;
525 oldvxres
= display
->var
.xres_virtual
;
526 oldvyres
= display
->var
.yres_virtual
;
527 oldbpp
= display
->var
.bits_per_pixel
;
531 if (oldxres
!= var
->xres
|| oldyres
!= var
->yres
||
532 oldvxres
!= var
->xres_virtual
|| oldvyres
!= var
->yres_virtual
||
533 oldbpp
!= var
->bits_per_pixel
) {
534 vga16fb_set_disp(con
, info
);
535 if (info
->fb_info
.changevar
)
536 info
->fb_info
.changevar(con
);
539 vga16fb_set_par(&par
, info
);
545 static void ega16_setpalette(int regno
, unsigned red
, unsigned green
, unsigned blue
)
547 static unsigned char map
[] = { 000, 001, 010, 011 };
550 val
= map
[red
>>14] | ((map
[green
>>14]) << 1) | ((map
[blue
>>14]) << 2);
551 inb_p(0x3DA); /* ! 0x3BA */
552 outb_p(regno
, 0x3C0);
554 inb_p(0x3DA); /* some clones need it */
555 outb_p(0x20, 0x3C0); /* unblank screen */
558 static int vga16_getcolreg(unsigned regno
, unsigned *red
, unsigned *green
,
559 unsigned *blue
, unsigned *transp
,
560 struct fb_info
*fb_info
)
563 * Read a single color register and split it into colors/transparent.
564 * Return != 0 for invalid regno.
570 *red
= palette
[regno
].red
;
571 *green
= palette
[regno
].green
;
572 *blue
= palette
[regno
].blue
;
577 static void vga16_setpalette(int regno
, unsigned red
, unsigned green
, unsigned blue
)
579 outb(regno
, dac_reg
);
580 outb(red
>> 10, dac_val
);
581 outb(green
>> 10, dac_val
);
582 outb(blue
>> 10, dac_val
);
585 static int vga16_setcolreg(unsigned regno
, unsigned red
, unsigned green
,
586 unsigned blue
, unsigned transp
,
587 struct fb_info
*fb_info
)
592 * Set a single color register. The values supplied are
593 * already rounded down to the hardware's capabilities
594 * (according to the entries in the `var' structure). Return
595 * != 0 for invalid regno.
601 palette
[regno
].red
= red
;
602 palette
[regno
].green
= green
;
603 palette
[regno
].blue
= blue
;
606 gray
= disp
.var
.grayscale
;
608 gray
= fb_display
[currcon
].var
.grayscale
;
610 /* gray = 0.30*R + 0.59*G + 0.11*B */
611 red
= green
= blue
= (red
* 77 + green
* 151 + blue
* 28) >> 8;
613 if (((struct vga16fb_info
*) fb_info
)->isVGA
)
614 vga16_setpalette(regno
,red
,green
,blue
);
616 ega16_setpalette(regno
,red
,green
,blue
);
621 static void do_install_cmap(int con
, struct fb_info
*info
)
625 if (fb_display
[con
].cmap
.len
)
626 fb_set_cmap(&fb_display
[con
].cmap
, 1, vga16_setcolreg
, info
);
628 fb_set_cmap(fb_default_cmap(16), 1, vga16_setcolreg
,
632 static int vga16fb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
633 struct fb_info
*info
)
635 if (con
== currcon
) /* current console? */
636 return fb_get_cmap(cmap
, kspc
, vga16_getcolreg
, info
);
637 else if (fb_display
[con
].cmap
.len
) /* non default colormap? */
638 fb_copy_cmap(&fb_display
[con
].cmap
, cmap
, kspc
? 0 : 2);
640 fb_copy_cmap(fb_default_cmap(16),
645 static int vga16fb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
646 struct fb_info
*info
)
650 if (!fb_display
[con
].cmap
.len
) { /* no colormap allocated? */
651 err
= fb_alloc_cmap(&fb_display
[con
].cmap
,16,0);
655 if (con
== currcon
) /* current console? */
656 return fb_set_cmap(cmap
, kspc
, vga16_setcolreg
, info
);
658 fb_copy_cmap(cmap
, &fb_display
[con
].cmap
, kspc
? 0 : 1);
662 static int vga16fb_pan_display(struct fb_var_screeninfo
*var
, int con
,
663 struct fb_info
*info
)
665 if (var
->xoffset
+ fb_display
[con
].var
.xres
> fb_display
[con
].var
.xres_virtual
||
666 var
->yoffset
+ fb_display
[con
].var
.yres
> fb_display
[con
].var
.yres_virtual
)
669 vga16fb_pan_var(info
, var
);
670 fb_display
[con
].var
.xoffset
= var
->xoffset
;
671 fb_display
[con
].var
.yoffset
= var
->yoffset
;
672 fb_display
[con
].var
.vmode
&= ~FB_VMODE_YWRAP
;
676 static struct fb_ops vga16fb_ops
= {
678 fb_get_fix
: vga16fb_get_fix
,
679 fb_get_var
: vga16fb_get_var
,
680 fb_set_var
: vga16fb_set_var
,
681 fb_get_cmap
: vga16fb_get_cmap
,
682 fb_set_cmap
: vga16fb_set_cmap
,
683 fb_pan_display
: vga16fb_pan_display
,
686 int vga16fb_setup(char *options
)
690 vga16fb
.fb_info
.fontname
[0] = '\0';
692 if (!options
|| !*options
)
695 for(this_opt
=strtok(options
,","); this_opt
; this_opt
=strtok(NULL
,",")) {
696 if (!*this_opt
) continue;
698 if (!strncmp(this_opt
, "font:", 5))
699 strcpy(vga16fb
.fb_info
.fontname
, this_opt
+5);
704 static int vga16fb_switch(int con
, struct fb_info
*fb
)
706 struct vga16fb_par par
;
707 struct vga16fb_info
*info
= (struct vga16fb_info
*)fb
;
709 /* Do we have to save the colormap? */
710 if (fb_display
[currcon
].cmap
.len
)
711 fb_get_cmap(&fb_display
[currcon
].cmap
, 1, vga16_getcolreg
,
715 vga16fb_decode_var(&fb_display
[con
].var
, &par
, info
);
716 vga16fb_set_par(&par
, info
);
717 vga16fb_set_disp(con
, info
);
719 /* Install new colormap */
720 do_install_cmap(con
, fb
);
721 /* vga16fb_update_var(con, fb); */
725 /* The following VESA blanking code is taken from vgacon.c. The VGA
726 blanking code was originally by Huang shi chao, and modified by
727 Christoph Rimek (chrimek@toppoint.de) and todd j. derr
728 (tjd@barefoot.org) for Linux. */
729 #define attrib_port 0x3c0
730 #define seq_port_reg 0x3c4
731 #define seq_port_val 0x3c5
732 #define gr_port_reg 0x3ce
733 #define gr_port_val 0x3cf
734 #define video_misc_rd 0x3cc
735 #define video_misc_wr 0x3c2
736 #define vga_video_port_reg 0x3d4
737 #define vga_video_port_val 0x3d5
739 static void vga_vesa_blank(struct vga16fb_info
*info
, int mode
)
741 unsigned char SeqCtrlIndex
;
742 unsigned char CrtCtrlIndex
;
745 SeqCtrlIndex
= inb_p(seq_port_reg
);
746 CrtCtrlIndex
= inb_p(vga_video_port_reg
);
748 /* save original values of VGA controller registers */
749 if(!info
->vesa_blanked
) {
750 info
->vga_state
.CrtMiscIO
= inb_p(video_misc_rd
);
753 outb_p(0x00,vga_video_port_reg
); /* HorizontalTotal */
754 info
->vga_state
.HorizontalTotal
= inb_p(vga_video_port_val
);
755 outb_p(0x01,vga_video_port_reg
); /* HorizDisplayEnd */
756 info
->vga_state
.HorizDisplayEnd
= inb_p(vga_video_port_val
);
757 outb_p(0x04,vga_video_port_reg
); /* StartHorizRetrace */
758 info
->vga_state
.StartHorizRetrace
= inb_p(vga_video_port_val
);
759 outb_p(0x05,vga_video_port_reg
); /* EndHorizRetrace */
760 info
->vga_state
.EndHorizRetrace
= inb_p(vga_video_port_val
);
761 outb_p(0x07,vga_video_port_reg
); /* Overflow */
762 info
->vga_state
.Overflow
= inb_p(vga_video_port_val
);
763 outb_p(0x10,vga_video_port_reg
); /* StartVertRetrace */
764 info
->vga_state
.StartVertRetrace
= inb_p(vga_video_port_val
);
765 outb_p(0x11,vga_video_port_reg
); /* EndVertRetrace */
766 info
->vga_state
.EndVertRetrace
= inb_p(vga_video_port_val
);
767 outb_p(0x17,vga_video_port_reg
); /* ModeControl */
768 info
->vga_state
.ModeControl
= inb_p(vga_video_port_val
);
769 outb_p(0x01,seq_port_reg
); /* ClockingMode */
770 info
->vga_state
.ClockingMode
= inb_p(seq_port_val
);
773 /* assure that video is enabled */
774 /* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
776 outb_p(0x01,seq_port_reg
);
777 outb_p(info
->vga_state
.ClockingMode
| 0x20,seq_port_val
);
779 /* test for vertical retrace in process.... */
780 if ((info
->vga_state
.CrtMiscIO
& 0x80) == 0x80)
781 outb_p(info
->vga_state
.CrtMiscIO
& 0xef,video_misc_wr
);
784 * Set <End of vertical retrace> to minimum (0) and
785 * <Start of vertical Retrace> to maximum (incl. overflow)
786 * Result: turn off vertical sync (VSync) pulse.
788 if (mode
& VESA_VSYNC_SUSPEND
) {
789 outb_p(0x10,vga_video_port_reg
); /* StartVertRetrace */
790 outb_p(0xff,vga_video_port_val
); /* maximum value */
791 outb_p(0x11,vga_video_port_reg
); /* EndVertRetrace */
792 outb_p(0x40,vga_video_port_val
); /* minimum (bits 0..3) */
793 outb_p(0x07,vga_video_port_reg
); /* Overflow */
794 outb_p(info
->vga_state
.Overflow
| 0x84,vga_video_port_val
); /* bits 9,10 of vert. retrace */
797 if (mode
& VESA_HSYNC_SUSPEND
) {
799 * Set <End of horizontal retrace> to minimum (0) and
800 * <Start of horizontal Retrace> to maximum
801 * Result: turn off horizontal sync (HSync) pulse.
803 outb_p(0x04,vga_video_port_reg
); /* StartHorizRetrace */
804 outb_p(0xff,vga_video_port_val
); /* maximum */
805 outb_p(0x05,vga_video_port_reg
); /* EndHorizRetrace */
806 outb_p(0x00,vga_video_port_val
); /* minimum (0) */
809 /* restore both index registers */
810 outb_p(SeqCtrlIndex
,seq_port_reg
);
811 outb_p(CrtCtrlIndex
,vga_video_port_reg
);
815 static void vga_vesa_unblank(struct vga16fb_info
*info
)
817 unsigned char SeqCtrlIndex
;
818 unsigned char CrtCtrlIndex
;
821 SeqCtrlIndex
= inb_p(seq_port_reg
);
822 CrtCtrlIndex
= inb_p(vga_video_port_reg
);
824 /* restore original values of VGA controller registers */
825 outb_p(info
->vga_state
.CrtMiscIO
,video_misc_wr
);
827 outb_p(0x00,vga_video_port_reg
); /* HorizontalTotal */
828 outb_p(info
->vga_state
.HorizontalTotal
,vga_video_port_val
);
829 outb_p(0x01,vga_video_port_reg
); /* HorizDisplayEnd */
830 outb_p(info
->vga_state
.HorizDisplayEnd
,vga_video_port_val
);
831 outb_p(0x04,vga_video_port_reg
); /* StartHorizRetrace */
832 outb_p(info
->vga_state
.StartHorizRetrace
,vga_video_port_val
);
833 outb_p(0x05,vga_video_port_reg
); /* EndHorizRetrace */
834 outb_p(info
->vga_state
.EndHorizRetrace
,vga_video_port_val
);
835 outb_p(0x07,vga_video_port_reg
); /* Overflow */
836 outb_p(info
->vga_state
.Overflow
,vga_video_port_val
);
837 outb_p(0x10,vga_video_port_reg
); /* StartVertRetrace */
838 outb_p(info
->vga_state
.StartVertRetrace
,vga_video_port_val
);
839 outb_p(0x11,vga_video_port_reg
); /* EndVertRetrace */
840 outb_p(info
->vga_state
.EndVertRetrace
,vga_video_port_val
);
841 outb_p(0x17,vga_video_port_reg
); /* ModeControl */
842 outb_p(info
->vga_state
.ModeControl
,vga_video_port_val
);
843 outb_p(0x01,seq_port_reg
); /* ClockingMode */
844 outb_p(info
->vga_state
.ClockingMode
,seq_port_val
);
846 /* restore index/control registers */
847 outb_p(SeqCtrlIndex
,seq_port_reg
);
848 outb_p(CrtCtrlIndex
,vga_video_port_reg
);
852 static void vga_pal_blank(void)
856 for (i
=0; i
<16; i
++) {
857 outb_p (i
, dac_reg
) ;
858 outb_p (0, dac_val
) ;
859 outb_p (0, dac_val
) ;
860 outb_p (0, dac_val
) ;
864 /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
865 static void vga16fb_blank(int blank
, struct fb_info
*fb_info
)
867 struct vga16fb_info
*info
= (struct vga16fb_info
*)fb_info
;
870 case 0: /* Unblank */
871 if (info
->vesa_blanked
) {
872 vga_vesa_unblank(info
);
873 info
->vesa_blanked
= 0;
875 if (info
->palette_blanked
) {
876 do_install_cmap(currcon
, fb_info
);
877 info
->palette_blanked
= 0;
882 info
->palette_blanked
= 1;
884 default: /* VESA blanking */
885 vga_vesa_blank(info
, blank
-1);
886 info
->vesa_blanked
= 1;
891 int __init
vga16fb_init(void)
895 printk(KERN_DEBUG
"vga16fb: initializing\n");
897 /* XXX share VGA_FB_PHYS region with vgacon */
899 vga16fb
.video_vbase
= ioremap(VGA_FB_PHYS
, VGA_FB_PHYS_LEN
);
900 printk(KERN_INFO
"vga16fb: mapped to 0x%p\n", vga16fb
.video_vbase
);
902 vga16fb
.isVGA
= ORIG_VIDEO_ISVGA
;
903 vga16fb
.palette_blanked
= 0;
904 vga16fb
.vesa_blanked
= 0;
906 i
= vga16fb
.isVGA
? 6 : 2;
908 vga16fb_defined
.red
.length
= i
;
909 vga16fb_defined
.green
.length
= i
;
910 vga16fb_defined
.blue
.length
= i
;
911 for(i
= 0; i
< 16; i
++) {
913 palette
[i
].red
= default_red
[j
];
914 palette
[i
].green
= default_grn
[j
];
915 palette
[i
].blue
= default_blu
[j
];
918 /* XXX share VGA I/O region with vgacon and others */
920 disp
.var
= vga16fb_defined
;
922 /* name should not depend on EGA/VGA */
923 strcpy(vga16fb
.fb_info
.modename
, "VGA16 VGA");
924 vga16fb
.fb_info
.changevar
= NULL
;
925 vga16fb
.fb_info
.node
= -1;
926 vga16fb
.fb_info
.fbops
= &vga16fb_ops
;
927 vga16fb
.fb_info
.disp
=&disp
;
928 vga16fb
.fb_info
.switch_con
=&vga16fb_switch
;
929 vga16fb
.fb_info
.updatevar
=&vga16fb_update_var
;
930 vga16fb
.fb_info
.blank
=&vga16fb_blank
;
931 vga16fb
.fb_info
.flags
=FBINFO_FLAG_DEFAULT
;
932 vga16fb_set_disp(-1, &vga16fb
);
934 if (register_framebuffer(&vga16fb
.fb_info
)<0)
937 printk(KERN_INFO
"fb%d: %s frame buffer device\n",
938 GET_FB_IDX(vga16fb
.fb_info
.node
), vga16fb
.fb_info
.modename
);
943 static void __exit
vga16fb_exit(void)
945 unregister_framebuffer(&vga16fb
.fb_info
);
946 iounmap(vga16fb
.video_vbase
);
947 /* XXX unshare VGA regions */
951 module_init(vga16fb_init
);
953 module_exit(vga16fb_exit
);
957 * Overrides for Emacs so that we follow Linus's tabbing style.
958 * ---------------------------------------------------------------------------