/*
Low level VGA based console driver
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/console.h>
#include <linux/string.h>
#include <linux/kd.h>
#include <linux/slab.h>
#include <linux/vt_kern.h>
#include <linux/sched.h>
#include <linux/selection.h>
#include <linux/spinlock.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/screen_info.h>
#include <video/vga.h>
#include <asm/io.h>
static DEFINE_RAW_SPINLOCK(vga_lock);
static int cursor_size_lastfrom;
static int cursor_size_lastto;
static u32 vgacon_xres;
static u32 vgacon_yres;
static struct vgastate state;
#define BLANK 0x0020
#define CAN_LOAD_EGA_FONTS /* undefine if the user must not do this */
#define CAN_LOAD_PALETTE /* undefine if the user must not do this */
/* You really do _NOT_ want to define this, unless you have buggy
* Trident VGA which will resize cursor when moving it between column
* 15 & 16. If you define this and your VGA is OK, inverse bug will
* appear.
*/
#undef TRIDENT_GLITCH
#define VGA_FONTWIDTH 8 /* VGA does not support fontwidths != 8 */
/*
* Interface used by the world
*/
static const char *vgacon_startup(void);
static void vgacon_init(struct vc_data *c, int init);
static void vgacon_deinit(struct vc_data *c);
static void vgacon_cursor(struct vc_data *c, int mode);
static int vgacon_switch(struct vc_data *c);
static int vgacon_blank(struct vc_data *c, int blank, int mode_switch);
static int vgacon_set_palette(struct vc_data *vc, unsigned char *table);
static int vgacon_scrolldelta(struct vc_data *c, int lines);
static int vgacon_set_origin(struct vc_data *c);
static void vgacon_save_screen(struct vc_data *c);
static int vgacon_scroll(struct vc_data *c, int t, int b, int dir,
int lines);
static void vgacon_invert_region(struct vc_data *c, u16 * p, int count);
static unsigned long vgacon_uni_pagedir[2];
/* Description of the hardware situation */
static int vga_init_done __read_mostly;
static unsigned long vga_vram_base __read_mostly; /* Base of video memory */
static unsigned long vga_vram_end __read_mostly; /* End of video memory */
static unsigned int vga_vram_size __read_mostly; /* Size of video memory */
static u16 vga_video_port_reg __read_mostly; /* Video register select port */
static u16 vga_video_port_val __read_mostly; /* Video register value port */
static unsigned int vga_video_num_columns; /* Number of text columns */
static unsigned int vga_video_num_lines; /* Number of text lines */
static int vga_can_do_color __read_mostly; /* Do we support colors? */
static unsigned int vga_default_font_height __read_mostly; /* Height of default screen font */
static unsigned char vga_video_type __read_mostly; /* Card type */
static unsigned char vga_hardscroll_enabled __read_mostly;
static unsigned char vga_hardscroll_user_enable __read_mostly = 1;
static unsigned char vga_font_is_default = 1;
static int vga_vesa_blanked;
static int vga_palette_blanked;
static int vga_is_gfx;
static int vga_512_chars;
static int vga_video_font_height;
static int vga_scan_lines __read_mostly;
static unsigned int vga_rolled_over;
static int vgacon_text_mode_force;
bool vgacon_text_force(void)
{
return vgacon_text_mode_force ? true : false;
}
EXPORT_SYMBOL(vgacon_text_force);
static int __init text_mode(char *str)
{
vgacon_text_mode_force = 1;
return 1;
}
/* force text mode - used by kernel modesetting */
__setup("nomodeset", text_mode);
static int __init no_scroll(char *str)
{
/*
* Disabling scrollback is required for the Braillex ib80-piezo
* Braille reader made by F.H. Papenmeier (Germany).
* Use the "no-scroll" bootflag.
*/
vga_hardscroll_user_enable = vga_hardscroll_enabled = 0;
return 1;
}
__setup("no-scroll", no_scroll);
/*
* By replacing the four outb_p with two back to back outw, we can reduce
* the window of opportunity to see text mislocated to the RHS of the
* console during heavy scrolling activity. However there is the remote
* possibility that some pre-dinosaur hardware won't like the back to back
* I/O. Since the Xservers get away with it, we should be able to as well.
*/
static inline void write_vga(unsigned char reg, unsigned int val)
{
unsigned int v1, v2;
unsigned long flags;
/*
* ddprintk might set the console position from interrupt
* handlers, thus the write has to be IRQ-atomic.
*/
raw_spin_lock_irqsave(&vga_lock, flags);
#ifndef SLOW_VGA
v1 = reg + (val & 0xff00);
v2 = reg + 1 + ((val << 8) & 0xff00);
outw(v1, vga_video_port_reg);
outw(v2, vga_video_port_reg);
#else
outb_p(reg, vga_video_port_reg);
outb_p(val >> 8, vga_video_port_val);
outb_p(reg + 1, vga_video_port_reg);
outb_p(val & 0xff, vga_video_port_val);
#endif
raw_spin_unlock_irqrestore(&vga_lock, flags);
}
static inline void vga_set_mem_top(struct vc_data *c)
{
write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2);
}
#ifdef CONFIG_VGACON_SOFT_SCROLLBACK
/* software scrollback */
static void *vgacon_scrollback;
static int vgacon_scrollback_tail;
static int vgacon_scrollback_size;
static int vgacon_scrollback_rows;
static int vgacon_scrollback_cnt;
static int vgacon_scrollback_cur;
static int vgacon_scrollback_save;
static int vgacon_scrollback_restore;
static void vgacon_scrollback_init(int pitch)
{
int rows = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024/pitch;
if (vgacon_scrollback) {
vgacon_scrollback_cnt = 0;
vgacon_scrollback_tail = 0;
vgacon_scrollback_cur = 0;
vgacon_scrollback_rows = rows - 1;
vgacon_scrollback_size = rows * pitch;
}
}
static void vgacon_scrollback_startup(void)
{
vgacon_scrollback = kcalloc(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE, 1024, GFP_NOWAIT);
vgacon_scrollback_init(vga_video_num_columns * 2);
}
static void vgacon_scrollback_update(struct vc_data *c, int t, int count)
{
void *p;
if (!vgacon_scrollback_size || c->vc_num != fg_console)
return;
p = (void *) (c->vc_origin + t * c->vc_size_row);
while (count--) {
scr_memcpyw(vgacon_scrollback + vgacon_scrollback_tail,
p, c->vc_size_row);
vgacon_scrollback_cnt++;
p += c->vc_size_row;
vgacon_scrollback_tail += c->vc_size_row;
if (vgacon_scrollback_tail >= vgacon_scrollback_size)
vgacon_scrollback_tail = 0;
if (vgacon_scrollback_cnt > vgacon_scrollback_rows)
vgacon_scrollback_cnt = vgacon_scrollback_rows;
vgacon_scrollback_cur = vgacon_scrollback_cnt;
}
}
static void vgacon_restore_screen(struct vc_data *c)
{
vgacon_scrollback_save = 0;
if (!vga_is_gfx && !vgacon_scrollback_restore) {
scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf,
c->vc_screenbuf_size > vga_vram_size ?
vga_vram_size : c->vc_screenbuf_size);
vgacon_scrollback_restore = 1;
vgacon_scrollback_cur = vgacon_scrollback_cnt;
}
}
static int vgacon_scrolldelta(struct vc_data *c, int lines)
{
int start, end, count, soff;
if (!lines) {
c->vc_visible_origin = c->vc_origin;
vga_set_mem_top(c);
return 1;
}
if (!vgacon_scrollback)
return 1;
if (!vgacon_scrollback_save) {
vgacon_cursor(c, CM_ERASE);
vgacon_save_screen(c);
vgacon_scrollback_save = 1;
}
vgacon_scrollback_restore = 0;
start = vgacon_scrollback_cur + lines;
end = start + abs(lines);
if (start < 0)
start = 0;
if (start > vgacon_scrollback_cnt)
start = vgacon_scrollback_cnt;
if (end < 0)
end = 0;
if (end > vgacon_scrollback_cnt)
end = vgacon_scrollback_cnt;
vgacon_scrollback_cur = start;
count = end - start;
soff = vgacon_scrollback_tail - ((vgacon_scrollback_cnt - end) *
c->vc_size_row);
soff -= count * c->vc_size_row;
if (soff < 0)
soff += vgacon_scrollback_size;
count = vgacon_scrollback_cnt - start;
if (count > c->vc_rows)
count = c->vc_rows;
if (count) {
int copysize;
int diff = c->vc_rows - count;
void *d = (void *) c->vc_origin;
void *s = (void *) c->vc_screenbuf;
count *= c->vc_size_row;
/* how much memory to end of buffer left? */
copysize = min(count, vg