linux/arch/x86/boot/video.c
<<
>>
Prefs
   1/* -*- linux-c -*- ------------------------------------------------------- *
   2 *
   3 *   Copyright (C) 1991, 1992 Linus Torvalds
   4 *   Copyright 2007 rPath, Inc. - All Rights Reserved
   5 *   Copyright 2009 Intel Corporation; author H. Peter Anvin
   6 *
   7 *   This file is part of the Linux kernel, and is made available under
   8 *   the terms of the GNU General Public License version 2.
   9 *
  10 * ----------------------------------------------------------------------- */
  11
  12/*
  13 * Select video mode
  14 */
  15
  16#include "boot.h"
  17#include "video.h"
  18#include "vesa.h"
  19
  20static void store_cursor_position(void)
  21{
  22        struct biosregs ireg, oreg;
  23
  24        initregs(&ireg);
  25        ireg.ah = 0x03;
  26        intcall(0x10, &ireg, &oreg);
  27
  28        boot_params.screen_info.orig_x = oreg.dl;
  29        boot_params.screen_info.orig_y = oreg.dh;
  30}
  31
  32static void store_video_mode(void)
  33{
  34        struct biosregs ireg, oreg;
  35
  36        /* N.B.: the saving of the video page here is a bit silly,
  37           since we pretty much assume page 0 everywhere. */
  38        initregs(&ireg);
  39        ireg.ah = 0x0f;
  40        intcall(0x10, &ireg, &oreg);
  41
  42        /* Not all BIOSes are clean with respect to the top bit */
  43        boot_params.screen_info.orig_video_mode = oreg.al & 0x7f;
  44        boot_params.screen_info.orig_video_page = oreg.bh;
  45}
  46
  47/*
  48 * Store the video mode parameters for later usage by the kernel.
  49 * This is done by asking the BIOS except for the rows/columns
  50 * parameters in the default 80x25 mode -- these are set directly,
  51 * because some very obscure BIOSes supply insane values.
  52 */
  53static void store_mode_params(void)
  54{
  55        u16 font_size;
  56        int x, y;
  57
  58        /* For graphics mode, it is up to the mode-setting driver
  59           (currently only video-vesa.c) to store the parameters */
  60        if (graphic_mode)
  61                return;
  62
  63        store_cursor_position();
  64        store_video_mode();
  65
  66        if (boot_params.screen_info.orig_video_mode == 0x07) {
  67                /* MDA, HGC, or VGA in monochrome mode */
  68                video_segment = 0xb000;
  69        } else {
  70                /* CGA, EGA, VGA and so forth */
  71                video_segment = 0xb800;
  72        }
  73
  74        set_fs(0);
  75        font_size = rdfs16(0x485); /* Font size, BIOS area */
  76        boot_params.screen_info.orig_video_points = font_size;
  77
  78        x = rdfs16(0x44a);
  79        y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1;
  80
  81        if (force_x)
  82                x = force_x;
  83        if (force_y)
  84                y = force_y;
  85
  86        boot_params.screen_info.orig_video_cols  = x;
  87        boot_params.screen_info.orig_video_lines = y;
  88}
  89
  90static unsigned int get_entry(void)
  91{
  92        char entry_buf[4];
  93        int i, len = 0;
  94        int key;
  95        unsigned int v;
  96
  97        do {
  98                key = getchar();
  99
 100                if (key == '\b') {
 101                        if (len > 0) {
 102                                puts("\b \b");
 103                                len--;
 104                        }
 105                } else if ((key >= '0' && key <= '9') ||
 106                           (key >= 'A' && key <= 'Z') ||
 107                           (key >= 'a' && key <= 'z')) {
 108                        if (len < sizeof entry_buf) {
 109                                entry_buf[len++] = key;
 110                                putchar(key);
 111                        }
 112                }
 113        } while (key != '\r');
 114        putchar('\n');
 115
 116        if (len == 0)
 117                return VIDEO_CURRENT_MODE; /* Default */
 118
 119        v = 0;
 120        for (i = 0; i < len; i++) {
 121                v <<= 4;
 122                key = entry_buf[i] | 0x20;
 123                v += (key > '9') ? key-'a'+10 : key-'0';
 124        }
 125
 126        return v;
 127}
 128
 129static void display_menu(void)
 130{
 131        struct card_info *card;
 132        struct mode_info *mi;
 133        char ch;
 134        int i;
 135        int nmodes;
 136        int modes_per_line;
 137        int col;
 138
 139        nmodes = 0;
 140        for (card = video_cards; card < video_cards_end; card++)
 141                nmodes += card->nmodes;
 142
 143        modes_per_line = 1;
 144        if (nmodes >= 20)
 145                modes_per_line = 3;
 146
 147        for (col = 0; col < modes_per_line; col++)
 148                puts("Mode: Resolution:  Type: ");
 149        putchar('\n');
 150
 151        col = 0;
 152        ch = '0';
 153        for (card = video_cards; card < video_cards_end; card++) {
 154                mi = card->modes;
 155                for (i = 0; i < card->nmodes; i++, mi++) {
 156                        char resbuf[32];
 157                        int visible = mi->x && mi->y;
 158                        u16 mode_id = mi->mode ? mi->mode :
 159                                (mi->y << 8)+mi->x;
 160
 161                        if (!visible)
 162                                continue; /* Hidden mode */
 163
 164                        if (mi->depth)
 165                                sprintf(resbuf, "%dx%d", mi->y, mi->depth);
 166                        else
 167                                sprintf(resbuf, "%d", mi->y);
 168
 169                        printf("%c %03X %4dx%-7s %-6s",
 170                               ch, mode_id, mi->x, resbuf, card->card_name);
 171                        col++;
 172                        if (col >= modes_per_line) {
 173                                putchar('\n');
 174                                col = 0;
 175                        }
 176
 177                        if (ch == '9')
 178                                ch = 'a';
 179                        else if (ch == 'z' || ch == ' ')
 180                                ch = ' '; /* Out of keys... */
 181                        else
 182                                ch++;
 183                }
 184        }
 185        if (col)
 186                putchar('\n');
 187}
 188
 189#define H(x)    ((x)-'a'+10)
 190#define SCAN    ((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n'))
 191
 192static unsigned int mode_menu(void)
 193{
 194        int key;
 195        unsigned int sel;
 196
 197        puts("Press <ENTER> to see video modes available, "
 198             "<SPACE> to continue, or wait 30 sec\n");
 199
 200        kbd_flush();
 201        while (1) {
 202                key = getchar_timeout();
 203                if (key == ' ' || key == 0)
 204                        return VIDEO_CURRENT_MODE; /* Default */
 205                if (key == '\r')
 206                        break;
 207                putchar('\a');  /* Beep! */
 208        }
 209
 210
 211        for (;;) {
 212                display_menu();
 213
 214                puts("Enter a video mode or \"scan\" to scan for "
 215                     "additional modes: ");
 216                sel = get_entry();
 217                if (sel != SCAN)
 218                        return sel;
 219
 220                probe_cards(1);
 221        }
 222}
 223
 224/* Save screen content to the heap */
 225static struct saved_screen {
 226        int x, y;
 227        int curx, cury;
 228        u16 *data;
 229} saved;
 230
 231static void save_screen(void)
 232{
 233        /* Should be called after store_mode_params() */
 234        saved.x = boot_params.screen_info.orig_video_cols;
 235        saved.y = boot_params.screen_info.orig_video_lines;
 236        saved.curx = boot_params.screen_info.orig_x;
 237        saved.cury = boot_params.screen_info.orig_y;
 238
 239        if (!heap_free(saved.x*saved.y*sizeof(u16)+512))
 240                return;         /* Not enough heap to save the screen */
 241
 242        saved.data = GET_HEAP(u16, saved.x*saved.y);
 243
 244        set_fs(video_segment);
 245        copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16));
 246}
 247
 248static void restore_screen(void)
 249{
 250        /* Should be called after store_mode_params() */
 251        int xs = boot_params.screen_info.orig_video_cols;
 252        int ys = boot_params.screen_info.orig_video_lines;
 253        int y;
 254        addr_t dst = 0;
 255        u16 *src = saved.data;
 256        struct biosregs ireg;
 257
 258        if (graphic_mode)
 259                return;         /* Can't restore onto a graphic mode */
 260
 261        if (!src)
 262                return;         /* No saved screen contents */
 263
 264        /* Restore screen contents */
 265
 266        set_fs(video_segment);
 267        for (y = 0; y < ys; y++) {
 268                int npad;
 269
 270                if (y < saved.y) {
 271                        int copy = (xs < saved.x) ? xs : saved.x;
 272                        copy_to_fs(dst, src, copy*sizeof(u16));
 273                        dst += copy*sizeof(u16);
 274                        src += saved.x;
 275                        npad = (xs < saved.x) ? 0 : xs-saved.x;
 276                } else {
 277                        npad = xs;
 278                }
 279
 280                /* Writes "npad" blank characters to
 281                   video_segment:dst and advances dst */
 282                asm volatile("pushw %%es ; "
 283                             "movw %2,%%es ; "
 284                             "shrw %%cx ; "
 285                             "jnc 1f ; "
 286                             "stosw \n\t"
 287                             "1: rep;stosl ; "
 288                             "popw %%es"
 289                             : "+D" (dst), "+c" (npad)
 290                             : "bdS" (video_segment),
 291                               "a" (0x07200720));
 292        }
 293
 294        /* Restore cursor position */
 295        initregs(&ireg);
 296        ireg.ah = 0x02;         /* Set cursor position */
 297        ireg.dh = saved.cury;
 298        ireg.dl = saved.curx;
 299        intcall(0x10, &ireg, NULL);
 300}
 301
 302void set_video(void)
 303{
 304        u16 mode = boot_params.hdr.vid_mode;
 305
 306        RESET_HEAP();
 307
 308        store_mode_params();
 309        save_screen();
 310        probe_cards(0);
 311
 312        for (;;) {
 313                if (mode == ASK_VGA)
 314                        mode = mode_menu();
 315
 316                if (!set_mode(mode))
 317                        break;
 318
 319                printf("Undefined video mode number: %x\n", mode);
 320                mode = ASK_VGA;
 321        }
 322        boot_params.hdr.vid_mode = mode;
 323        vesa_store_edid();
 324        store_mode_params();
 325
 326        if (do_restore)
 327                restore_screen();
 328}
 329