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