linux/drivers/video/fbdev/core/fbcon.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/video/fbcon.c -- Low level frame buffer based console driver
   3 *
   4 *      Copyright (C) 1995 Geert Uytterhoeven
   5 *
   6 *
   7 *  This file is based on the original Amiga console driver (amicon.c):
   8 *
   9 *      Copyright (C) 1993 Hamish Macdonald
  10 *                         Greg Harp
  11 *      Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
  12 *
  13 *            with work by William Rucklidge (wjr@cs.cornell.edu)
  14 *                         Geert Uytterhoeven
  15 *                         Jes Sorensen (jds@kom.auc.dk)
  16 *                         Martin Apel
  17 *
  18 *  and on the original Atari console driver (atacon.c):
  19 *
  20 *      Copyright (C) 1993 Bjoern Brauel
  21 *                         Roman Hodek
  22 *
  23 *            with work by Guenther Kelleter
  24 *                         Martin Schaller
  25 *                         Andreas Schwab
  26 *
  27 *  Hardware cursor support added by Emmanuel Marty (core@ggi-project.org)
  28 *  Smart redraw scrolling, arbitrary font width support, 512char font support
  29 *  and software scrollback added by 
  30 *                         Jakub Jelinek (jj@ultra.linux.cz)
  31 *
  32 *  Random hacking by Martin Mares <mj@ucw.cz>
  33 *
  34 *      2001 - Documented with DocBook
  35 *      - Brad Douglas <brad@neruo.com>
  36 *
  37 *  The low level operations for the various display memory organizations are
  38 *  now in separate source files.
  39 *
  40 *  Currently the following organizations are supported:
  41 *
  42 *    o afb                     Amiga bitplanes
  43 *    o cfb{2,4,8,16,24,32}     Packed pixels
  44 *    o ilbm                    Amiga interleaved bitplanes
  45 *    o iplan2p[248]            Atari interleaved bitplanes
  46 *    o mfb                     Monochrome
  47 *    o vga                     VGA characters/attributes
  48 *
  49 *  To do:
  50 *
  51 *    - Implement 16 plane mode (iplan2p16)
  52 *
  53 *
  54 *  This file is subject to the terms and conditions of the GNU General Public
  55 *  License.  See the file COPYING in the main directory of this archive for
  56 *  more details.
  57 */
  58
  59#include <linux/module.h>
  60#include <linux/types.h>
  61#include <linux/fs.h>
  62#include <linux/kernel.h>
  63#include <linux/delay.h>        /* MSch: for IRQ probe */
  64#include <linux/console.h>
  65#include <linux/string.h>
  66#include <linux/kd.h>
  67#include <linux/slab.h>
  68#include <linux/fb.h>
  69#include <linux/fbcon.h>
  70#include <linux/vt_kern.h>
  71#include <linux/selection.h>
  72#include <linux/font.h>
  73#include <linux/smp.h>
  74#include <linux/init.h>
  75#include <linux/interrupt.h>
  76#include <linux/crc32.h> /* For counting font checksums */
  77#include <linux/uaccess.h>
  78#include <asm/fb.h>
  79#include <asm/irq.h>
  80
  81#include "fbcon.h"
  82
  83/*
  84 * FIXME: Locking
  85 *
  86 * - fbcon state itself is protected by the console_lock, and the code does a
  87 *   pretty good job at making sure that lock is held everywhere it's needed.
  88 *
  89 * - access to the registered_fb array is entirely unprotected. This should use
  90 *   proper object lifetime handling, i.e. get/put_fb_info. This also means
  91 *   switching from indices to proper pointers for fb_info everywhere.
  92 *
  93 * - fbcon doesn't bother with fb_lock/unlock at all. This is buggy, since it
  94 *   means concurrent access to the same fbdev from both fbcon and userspace
  95 *   will blow up. To fix this all fbcon calls from fbmem.c need to be moved out
  96 *   of fb_lock/unlock protected sections, since otherwise we'll recurse and
  97 *   deadlock eventually. Aside: Due to these deadlock issues the fbdev code in
  98 *   fbmem.c cannot use locking asserts, and there's lots of callers which get
  99 *   the rules wrong, e.g. fbsysfs.c entirely missed fb_lock/unlock calls too.
 100 */
 101
 102enum {
 103        FBCON_LOGO_CANSHOW      = -1,   /* the logo can be shown */
 104        FBCON_LOGO_DRAW         = -2,   /* draw the logo to a console */
 105        FBCON_LOGO_DONTSHOW     = -3    /* do not show the logo */
 106};
 107
 108static struct fbcon_display fb_display[MAX_NR_CONSOLES];
 109
 110static signed char con2fb_map[MAX_NR_CONSOLES];
 111static signed char con2fb_map_boot[MAX_NR_CONSOLES];
 112
 113static int logo_lines;
 114/* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
 115   enums.  */
 116static int logo_shown = FBCON_LOGO_CANSHOW;
 117/* console mappings */
 118static int first_fb_vc;
 119static int last_fb_vc = MAX_NR_CONSOLES - 1;
 120static int fbcon_is_default = 1; 
 121static int primary_device = -1;
 122static int fbcon_has_console_bind;
 123
 124#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
 125static int map_override;
 126
 127static inline void fbcon_map_override(void)
 128{
 129        map_override = 1;
 130}
 131#else
 132static inline void fbcon_map_override(void)
 133{
 134}
 135#endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */
 136
 137#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
 138static bool deferred_takeover = true;
 139#else
 140#define deferred_takeover false
 141#endif
 142
 143/* font data */
 144static char fontname[40];
 145
 146/* current fb_info */
 147static int info_idx = -1;
 148
 149/* console rotation */
 150static int initial_rotation = -1;
 151static int fbcon_has_sysfs;
 152static int margin_color;
 153
 154static const struct consw fb_con;
 155
 156#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
 157
 158static int fbcon_cursor_noblink;
 159
 160#define divides(a, b)   ((!(a) || (b)%(a)) ? 0 : 1)
 161
 162/*
 163 *  Interface used by the world
 164 */
 165
 166static const char *fbcon_startup(void);
 167static void fbcon_init(struct vc_data *vc, int init);
 168static void fbcon_deinit(struct vc_data *vc);
 169static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
 170                        int width);
 171static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos);
 172static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
 173                        int count, int ypos, int xpos);
 174static void fbcon_clear_margins(struct vc_data *vc, int bottom_only);
 175static void fbcon_cursor(struct vc_data *vc, int mode);
 176static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
 177                        int height, int width);
 178static int fbcon_switch(struct vc_data *vc);
 179static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);
 180static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table);
 181
 182/*
 183 *  Internal routines
 184 */
 185static __inline__ void ywrap_up(struct vc_data *vc, int count);
 186static __inline__ void ywrap_down(struct vc_data *vc, int count);
 187static __inline__ void ypan_up(struct vc_data *vc, int count);
 188static __inline__ void ypan_down(struct vc_data *vc, int count);
 189static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
 190                            int dy, int dx, int height, int width, u_int y_break);
 191static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
 192                           int unit);
 193static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
 194                              int line, int count, int dy);
 195static void fbcon_modechanged(struct fb_info *info);
 196static void fbcon_set_all_vcs(struct fb_info *info);
 197static void fbcon_start(void);
 198static void fbcon_exit(void);
 199static struct device *fbcon_device;
 200
 201#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
 202static inline void fbcon_set_rotation(struct fb_info *info)
 203{
 204        struct fbcon_ops *ops = info->fbcon_par;
 205
 206        if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&
 207            ops->p->con_rotate < 4)
 208                ops->rotate = ops->p->con_rotate;
 209        else
 210                ops->rotate = 0;
 211}
 212
 213static void fbcon_rotate(struct fb_info *info, u32 rotate)
 214{
 215        struct fbcon_ops *ops= info->fbcon_par;
 216        struct fb_info *fb_info;
 217
 218        if (!ops || ops->currcon == -1)
 219                return;
 220
 221        fb_info = registered_fb[con2fb_map[ops->currcon]];
 222
 223        if (info == fb_info) {
 224                struct fbcon_display *p = &fb_display[ops->currcon];
 225
 226                if (rotate < 4)
 227                        p->con_rotate = rotate;
 228                else
 229                        p->con_rotate = 0;
 230
 231                fbcon_modechanged(info);
 232        }
 233}
 234
 235static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
 236{
 237        struct fbcon_ops *ops = info->fbcon_par;
 238        struct vc_data *vc;
 239        struct fbcon_display *p;
 240        int i;
 241
 242        if (!ops || ops->currcon < 0 || rotate > 3)
 243                return;
 244
 245        for (i = first_fb_vc; i <= last_fb_vc; i++) {
 246                vc = vc_cons[i].d;
 247                if (!vc || vc->vc_mode != KD_TEXT ||
 248                    registered_fb[con2fb_map[i]] != info)
 249                        continue;
 250
 251                p = &fb_display[vc->vc_num];
 252                p->con_rotate = rotate;
 253        }
 254
 255        fbcon_set_all_vcs(info);
 256}
 257#else
 258static inline void fbcon_set_rotation(struct fb_info *info)
 259{
 260        struct fbcon_ops *ops = info->fbcon_par;
 261
 262        ops->rotate = FB_ROTATE_UR;
 263}
 264
 265static void fbcon_rotate(struct fb_info *info, u32 rotate)
 266{
 267        return;
 268}
 269
 270static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
 271{
 272        return;
 273}
 274#endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
 275
 276static int fbcon_get_rotate(struct fb_info *info)
 277{
 278        struct fbcon_ops *ops = info->fbcon_par;
 279
 280        return (ops) ? ops->rotate : 0;
 281}
 282
 283static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
 284{
 285        struct fbcon_ops *ops = info->fbcon_par;
 286
 287        return (info->state != FBINFO_STATE_RUNNING ||
 288                vc->vc_mode != KD_TEXT || ops->graphics);
 289}
 290
 291static int get_color(struct vc_data *vc, struct fb_info *info,
 292              u16 c, int is_fg)
 293{
 294        int depth = fb_get_color_depth(&info->var, &info->fix);
 295        int color = 0;
 296
 297        if (console_blanked) {
 298                unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
 299
 300                c = vc->vc_video_erase_char & charmask;
 301        }
 302
 303        if (depth != 1)
 304                color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
 305                        : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
 306
 307        switch (depth) {
 308        case 1:
 309        {
 310                int col = mono_col(info);
 311                /* 0 or 1 */
 312                int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0;
 313                int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col;
 314
 315                if (console_blanked)
 316                        fg = bg;
 317
 318                color = (is_fg) ? fg : bg;
 319                break;
 320        }
 321        case 2:
 322                /*
 323                 * Scale down 16-colors to 4 colors. Default 4-color palette
 324                 * is grayscale. However, simply dividing the values by 4
 325                 * will not work, as colors 1, 2 and 3 will be scaled-down
 326                 * to zero rendering them invisible.  So empirically convert
 327                 * colors to a sane 4-level grayscale.
 328                 */
 329                switch (color) {
 330                case 0:
 331                        color = 0; /* black */
 332                        break;
 333                case 1 ... 6:
 334                        color = 2; /* white */
 335                        break;
 336                case 7 ... 8:
 337                        color = 1; /* gray */
 338                        break;
 339                default:
 340                        color = 3; /* intense white */
 341                        break;
 342                }
 343                break;
 344        case 3:
 345                /*
 346                 * Last 8 entries of default 16-color palette is a more intense
 347                 * version of the first 8 (i.e., same chrominance, different
 348                 * luminance).
 349                 */
 350                color &= 7;
 351                break;
 352        }
 353
 354
 355        return color;
 356}
 357
 358static void fb_flashcursor(struct work_struct *work)
 359{
 360        struct fb_info *info = container_of(work, struct fb_info, queue);
 361        struct fbcon_ops *ops = info->fbcon_par;
 362        struct vc_data *vc = NULL;
 363        int c;
 364        int mode;
 365        int ret;
 366
 367        /* FIXME: we should sort out the unbind locking instead */
 368        /* instead we just fail to flash the cursor if we can't get
 369         * the lock instead of blocking fbcon deinit */
 370        ret = console_trylock();
 371        if (ret == 0)
 372                return;
 373
 374        if (ops && ops->currcon != -1)
 375                vc = vc_cons[ops->currcon].d;
 376
 377        if (!vc || !con_is_visible(vc) ||
 378            registered_fb[con2fb_map[vc->vc_num]] != info ||
 379            vc->vc_deccm != 1) {
 380                console_unlock();
 381                return;
 382        }
 383
 384        c = scr_readw((u16 *) vc->vc_pos);
 385        mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
 386                CM_ERASE : CM_DRAW;
 387        ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
 388                    get_color(vc, info, c, 0));
 389        console_unlock();
 390}
 391
 392static void cursor_timer_handler(struct timer_list *t)
 393{
 394        struct fbcon_ops *ops = from_timer(ops, t, cursor_timer);
 395        struct fb_info *info = ops->info;
 396
 397        queue_work(system_power_efficient_wq, &info->queue);
 398        mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies);
 399}
 400
 401static void fbcon_add_cursor_timer(struct fb_info *info)
 402{
 403        struct fbcon_ops *ops = info->fbcon_par;
 404
 405        if ((!info->queue.func || info->queue.func == fb_flashcursor) &&
 406            !(ops->flags & FBCON_FLAGS_CURSOR_TIMER) &&
 407            !fbcon_cursor_noblink) {
 408                if (!info->queue.func)
 409                        INIT_WORK(&info->queue, fb_flashcursor);
 410
 411                timer_setup(&ops->cursor_timer, cursor_timer_handler, 0);
 412                mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies);
 413                ops->flags |= FBCON_FLAGS_CURSOR_TIMER;
 414        }
 415}
 416
 417static void fbcon_del_cursor_timer(struct fb_info *info)
 418{
 419        struct fbcon_ops *ops = info->fbcon_par;
 420
 421        if (info->queue.func == fb_flashcursor &&
 422            ops->flags & FBCON_FLAGS_CURSOR_TIMER) {
 423                del_timer_sync(&ops->cursor_timer);
 424                ops->flags &= ~FBCON_FLAGS_CURSOR_TIMER;
 425        }
 426}
 427
 428#ifndef MODULE
 429static int __init fb_console_setup(char *this_opt)
 430{
 431        char *options;
 432        int i, j;
 433
 434        if (!this_opt || !*this_opt)
 435                return 1;
 436
 437        while ((options = strsep(&this_opt, ",")) != NULL) {
 438                if (!strncmp(options, "font:", 5)) {
 439                        strlcpy(fontname, options + 5, sizeof(fontname));
 440                        continue;
 441                }
 442                
 443                if (!strncmp(options, "scrollback:", 11)) {
 444                        pr_warn("Ignoring scrollback size option\n");
 445                        continue;
 446                }
 447                
 448                if (!strncmp(options, "map:", 4)) {
 449                        options += 4;
 450                        if (*options) {
 451                                for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
 452                                        if (!options[j])
 453                                                j = 0;
 454                                        con2fb_map_boot[i] =
 455                                                (options[j++]-'0') % FB_MAX;
 456                                }
 457
 458                                fbcon_map_override();
 459                        }
 460                        continue;
 461                }
 462
 463                if (!strncmp(options, "vc:", 3)) {
 464                        options += 3;
 465                        if (*options)
 466                                first_fb_vc = simple_strtoul(options, &options, 10) - 1;
 467                        if (first_fb_vc < 0)
 468                                first_fb_vc = 0;
 469                        if (*options++ == '-')
 470                                last_fb_vc = simple_strtoul(options, &options, 10) - 1;
 471                        fbcon_is_default = 0; 
 472                        continue;
 473                }
 474
 475                if (!strncmp(options, "rotate:", 7)) {
 476                        options += 7;
 477                        if (*options)
 478                                initial_rotation = simple_strtoul(options, &options, 0);
 479                        if (initial_rotation > 3)
 480                                initial_rotation = 0;
 481                        continue;
 482                }
 483
 484                if (!strncmp(options, "margin:", 7)) {
 485                        options += 7;
 486                        if (*options)
 487                                margin_color = simple_strtoul(options, &options, 0);
 488                        continue;
 489                }
 490#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
 491                if (!strcmp(options, "nodefer")) {
 492                        deferred_takeover = false;
 493                        continue;
 494                }
 495#endif
 496
 497                if (!strncmp(options, "logo-pos:", 9)) {
 498                        options += 9;
 499                        if (!strcmp(options, "center"))
 500                                fb_center_logo = true;
 501                        continue;
 502                }
 503
 504                if (!strncmp(options, "logo-count:", 11)) {
 505                        options += 11;
 506                        if (*options)
 507                                fb_logo_count = simple_strtol(options, &options, 0);
 508                        continue;
 509                }
 510        }
 511        return 1;
 512}
 513
 514__setup("fbcon=", fb_console_setup);
 515#endif
 516
 517static int search_fb_in_map(int idx)
 518{
 519        int i, retval = 0;
 520
 521        for (i = first_fb_vc; i <= last_fb_vc; i++) {
 522                if (con2fb_map[i] == idx)
 523                        retval = 1;
 524        }
 525        return retval;
 526}
 527
 528static int search_for_mapped_con(void)
 529{
 530        int i, retval = 0;
 531
 532        for (i = first_fb_vc; i <= last_fb_vc; i++) {
 533                if (con2fb_map[i] != -1)
 534                        retval = 1;
 535        }
 536        return retval;
 537}
 538
 539static int do_fbcon_takeover(int show_logo)
 540{
 541        int err, i;
 542
 543        if (!num_registered_fb)
 544                return -ENODEV;
 545
 546        if (!show_logo)
 547                logo_shown = FBCON_LOGO_DONTSHOW;
 548
 549        for (i = first_fb_vc; i <= last_fb_vc; i++)
 550                con2fb_map[i] = info_idx;
 551
 552        err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
 553                                fbcon_is_default);
 554
 555        if (err) {
 556                for (i = first_fb_vc; i <= last_fb_vc; i++)
 557                        con2fb_map[i] = -1;
 558                info_idx = -1;
 559        } else {
 560                fbcon_has_console_bind = 1;
 561        }
 562
 563        return err;
 564}
 565
 566#ifdef MODULE
 567static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
 568                               int cols, int rows, int new_cols, int new_rows)
 569{
 570        logo_shown = FBCON_LOGO_DONTSHOW;
 571}
 572#else
 573static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
 574                               int cols, int rows, int new_cols, int new_rows)
 575{
 576        /* Need to make room for the logo */
 577        struct fbcon_ops *ops = info->fbcon_par;
 578        int cnt, erase = vc->vc_video_erase_char, step;
 579        unsigned short *save = NULL, *r, *q;
 580        int logo_height;
 581
 582        if (info->fbops->owner) {
 583                logo_shown = FBCON_LOGO_DONTSHOW;
 584                return;
 585        }
 586
 587        /*
 588         * remove underline attribute from erase character
 589         * if black and white framebuffer.
 590         */
 591        if (fb_get_color_depth(&info->var, &info->fix) == 1)
 592                erase &= ~0x400;
 593        logo_height = fb_prepare_logo(info, ops->rotate);
 594        logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
 595        q = (unsigned short *) (vc->vc_origin +
 596                                vc->vc_size_row * rows);
 597        step = logo_lines * cols;
 598        for (r = q - logo_lines * cols; r < q; r++)
 599                if (scr_readw(r) != vc->vc_video_erase_char)
 600                        break;
 601        if (r != q && new_rows >= rows + logo_lines) {
 602                save = kmalloc(array3_size(logo_lines, new_cols, 2),
 603                               GFP_KERNEL);
 604                if (save) {
 605                        int i = cols < new_cols ? cols : new_cols;
 606                        scr_memsetw(save, erase, array3_size(logo_lines, new_cols, 2));
 607                        r = q - step;
 608                        for (cnt = 0; cnt < logo_lines; cnt++, r += i)
 609                                scr_memcpyw(save + cnt * new_cols, r, 2 * i);
 610                        r = q;
 611                }
 612        }
 613        if (r == q) {
 614                /* We can scroll screen down */
 615                r = q - step - cols;
 616                for (cnt = rows - logo_lines; cnt > 0; cnt--) {
 617                        scr_memcpyw(r + step, r, vc->vc_size_row);
 618                        r -= cols;
 619                }
 620                if (!save) {
 621                        int lines;
 622                        if (vc->state.y + logo_lines >= rows)
 623                                lines = rows - vc->state.y - 1;
 624                        else
 625                                lines = logo_lines;
 626                        vc->state.y += lines;
 627                        vc->vc_pos += lines * vc->vc_size_row;
 628                }
 629        }
 630        scr_memsetw((unsigned short *) vc->vc_origin,
 631                    erase,
 632                    vc->vc_size_row * logo_lines);
 633
 634        if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
 635                fbcon_clear_margins(vc, 0);
 636                update_screen(vc);
 637        }
 638
 639        if (save) {
 640                q = (unsigned short *) (vc->vc_origin +
 641                                        vc->vc_size_row *
 642                                        rows);
 643                scr_memcpyw(q, save, array3_size(logo_lines, new_cols, 2));
 644                vc->state.y += logo_lines;
 645                vc->vc_pos += logo_lines * vc->vc_size_row;
 646                kfree(save);
 647        }
 648
 649        if (logo_shown == FBCON_LOGO_DONTSHOW)
 650                return;
 651
 652        if (logo_lines > vc->vc_bottom) {
 653                logo_shown = FBCON_LOGO_CANSHOW;
 654                printk(KERN_INFO
 655                       "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
 656        } else {
 657                logo_shown = FBCON_LOGO_DRAW;
 658                vc->vc_top = logo_lines;
 659        }
 660}
 661#endif /* MODULE */
 662
 663#ifdef CONFIG_FB_TILEBLITTING
 664static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
 665{
 666        struct fbcon_ops *ops = info->fbcon_par;
 667
 668        ops->p = &fb_display[vc->vc_num];
 669
 670        if ((info->flags & FBINFO_MISC_TILEBLITTING))
 671                fbcon_set_tileops(vc, info);
 672        else {
 673                fbcon_set_rotation(info);
 674                fbcon_set_bitops(ops);
 675        }
 676}
 677
 678static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
 679{
 680        int err = 0;
 681
 682        if (info->flags & FBINFO_MISC_TILEBLITTING &&
 683            info->tileops->fb_get_tilemax(info) < charcount)
 684                err = 1;
 685
 686        return err;
 687}
 688#else
 689static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
 690{
 691        struct fbcon_ops *ops = info->fbcon_par;
 692
 693        info->flags &= ~FBINFO_MISC_TILEBLITTING;
 694        ops->p = &fb_display[vc->vc_num];
 695        fbcon_set_rotation(info);
 696        fbcon_set_bitops(ops);
 697}
 698
 699static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
 700{
 701        return 0;
 702}
 703
 704#endif /* CONFIG_MISC_TILEBLITTING */
 705
 706
 707static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info,
 708                                  int unit, int oldidx)
 709{
 710        struct fbcon_ops *ops = NULL;
 711        int err = 0;
 712
 713        if (!try_module_get(info->fbops->owner))
 714                err = -ENODEV;
 715
 716        if (!err && info->fbops->fb_open &&
 717            info->fbops->fb_open(info, 0))
 718                err = -ENODEV;
 719
 720        if (!err) {
 721                ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
 722                if (!ops)
 723                        err = -ENOMEM;
 724        }
 725
 726        if (!err) {
 727                ops->cur_blink_jiffies = HZ / 5;
 728                ops->info = info;
 729                info->fbcon_par = ops;
 730
 731                if (vc)
 732                        set_blitting_type(vc, info);
 733        }
 734
 735        if (err) {
 736                con2fb_map[unit] = oldidx;
 737                module_put(info->fbops->owner);
 738        }
 739
 740        return err;
 741}
 742
 743static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
 744                                  struct fb_info *newinfo, int unit,
 745                                  int oldidx, int found)
 746{
 747        struct fbcon_ops *ops = oldinfo->fbcon_par;
 748        int err = 0, ret;
 749
 750        if (oldinfo->fbops->fb_release &&
 751            oldinfo->fbops->fb_release(oldinfo, 0)) {
 752                con2fb_map[unit] = oldidx;
 753                if (!found && newinfo->fbops->fb_release)
 754                        newinfo->fbops->fb_release(newinfo, 0);
 755                if (!found)
 756                        module_put(newinfo->fbops->owner);
 757                err = -ENODEV;
 758        }
 759
 760        if (!err) {
 761                fbcon_del_cursor_timer(oldinfo);
 762                kfree(ops->cursor_state.mask);
 763                kfree(ops->cursor_data);
 764                kfree(ops->cursor_src);
 765                kfree(ops->fontbuffer);
 766                kfree(oldinfo->fbcon_par);
 767                oldinfo->fbcon_par = NULL;
 768                module_put(oldinfo->fbops->owner);
 769                /*
 770                  If oldinfo and newinfo are driving the same hardware,
 771                  the fb_release() method of oldinfo may attempt to
 772                  restore the hardware state.  This will leave the
 773                  newinfo in an undefined state. Thus, a call to
 774                  fb_set_par() may be needed for the newinfo.
 775                */
 776                if (newinfo && newinfo->fbops->fb_set_par) {
 777                        ret = newinfo->fbops->fb_set_par(newinfo);
 778
 779                        if (ret)
 780                                printk(KERN_ERR "con2fb_release_oldinfo: "
 781                                        "detected unhandled fb_set_par error, "
 782                                        "error code %d\n", ret);
 783                }
 784        }
 785
 786        return err;
 787}
 788
 789static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
 790                                int unit, int show_logo)
 791{
 792        struct fbcon_ops *ops = info->fbcon_par;
 793        int ret;
 794
 795        ops->currcon = fg_console;
 796
 797        if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT)) {
 798                ret = info->fbops->fb_set_par(info);
 799
 800                if (ret)
 801                        printk(KERN_ERR "con2fb_init_display: detected "
 802                                "unhandled fb_set_par error, "
 803                                "error code %d\n", ret);
 804        }
 805
 806        ops->flags |= FBCON_FLAGS_INIT;
 807        ops->graphics = 0;
 808        fbcon_set_disp(info, &info->var, unit);
 809
 810        if (show_logo) {
 811                struct vc_data *fg_vc = vc_cons[fg_console].d;
 812                struct fb_info *fg_info =
 813                        registered_fb[con2fb_map[fg_console]];
 814
 815                fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols,
 816                                   fg_vc->vc_rows, fg_vc->vc_cols,
 817                                   fg_vc->vc_rows);
 818        }
 819
 820        update_screen(vc_cons[fg_console].d);
 821}
 822
 823/**
 824 *      set_con2fb_map - map console to frame buffer device
 825 *      @unit: virtual console number to map
 826 *      @newidx: frame buffer index to map virtual console to
 827 *      @user: user request
 828 *
 829 *      Maps a virtual console @unit to a frame buffer device
 830 *      @newidx.
 831 *
 832 *      This should be called with the console lock held.
 833 */
 834static int set_con2fb_map(int unit, int newidx, int user)
 835{
 836        struct vc_data *vc = vc_cons[unit].d;
 837        int oldidx = con2fb_map[unit];
 838        struct fb_info *info = registered_fb[newidx];
 839        struct fb_info *oldinfo = NULL;
 840        int found, err = 0;
 841
 842        WARN_CONSOLE_UNLOCKED();
 843
 844        if (oldidx == newidx)
 845                return 0;
 846
 847        if (!info)
 848                return -EINVAL;
 849
 850        if (!search_for_mapped_con() || !con_is_bound(&fb_con)) {
 851                info_idx = newidx;
 852                return do_fbcon_takeover(0);
 853        }
 854
 855        if (oldidx != -1)
 856                oldinfo = registered_fb[oldidx];
 857
 858        found = search_fb_in_map(newidx);
 859
 860        con2fb_map[unit] = newidx;
 861        if (!err && !found)
 862                err = con2fb_acquire_newinfo(vc, info, unit, oldidx);
 863
 864        /*
 865         * If old fb is not mapped to any of the consoles,
 866         * fbcon should release it.
 867         */
 868        if (!err && oldinfo && !search_fb_in_map(oldidx))
 869                err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx,
 870                                             found);
 871
 872        if (!err) {
 873                int show_logo = (fg_console == 0 && !user &&
 874                                 logo_shown != FBCON_LOGO_DONTSHOW);
 875
 876                if (!found)
 877                        fbcon_add_cursor_timer(info);
 878                con2fb_map_boot[unit] = newidx;
 879                con2fb_init_display(vc, info, unit, show_logo);
 880        }
 881
 882        if (!search_fb_in_map(info_idx))
 883                info_idx = newidx;
 884
 885        return err;
 886}
 887
 888/*
 889 *  Low Level Operations
 890 */
 891/* NOTE: fbcon cannot be __init: it may be called from do_take_over_console later */
 892static int var_to_display(struct fbcon_display *disp,
 893                          struct fb_var_screeninfo *var,
 894                          struct fb_info *info)
 895{
 896        disp->xres_virtual = var->xres_virtual;
 897        disp->yres_virtual = var->yres_virtual;
 898        disp->bits_per_pixel = var->bits_per_pixel;
 899        disp->grayscale = var->grayscale;
 900        disp->nonstd = var->nonstd;
 901        disp->accel_flags = var->accel_flags;
 902        disp->height = var->height;
 903        disp->width = var->width;
 904        disp->red = var->red;
 905        disp->green = var->green;
 906        disp->blue = var->blue;
 907        disp->transp = var->transp;
 908        disp->rotate = var->rotate;
 909        disp->mode = fb_match_mode(var, &info->modelist);
 910        if (disp->mode == NULL)
 911                /* This should not happen */
 912                return -EINVAL;
 913        return 0;
 914}
 915
 916static void display_to_var(struct fb_var_screeninfo *var,
 917                           struct fbcon_display *disp)
 918{
 919        fb_videomode_to_var(var, disp->mode);
 920        var->xres_virtual = disp->xres_virtual;
 921        var->yres_virtual = disp->yres_virtual;
 922        var->bits_per_pixel = disp->bits_per_pixel;
 923        var->grayscale = disp->grayscale;
 924        var->nonstd = disp->nonstd;
 925        var->accel_flags = disp->accel_flags;
 926        var->height = disp->height;
 927        var->width = disp->width;
 928        var->red = disp->red;
 929        var->green = disp->green;
 930        var->blue = disp->blue;
 931        var->transp = disp->transp;
 932        var->rotate = disp->rotate;
 933}
 934
 935static const char *fbcon_startup(void)
 936{
 937        const char *display_desc = "frame buffer device";
 938        struct fbcon_display *p = &fb_display[fg_console];
 939        struct vc_data *vc = vc_cons[fg_console].d;
 940        const struct font_desc *font = NULL;
 941        struct module *owner;
 942        struct fb_info *info = NULL;
 943        struct fbcon_ops *ops;
 944        int rows, cols;
 945
 946        /*
 947         *  If num_registered_fb is zero, this is a call for the dummy part.
 948         *  The frame buffer devices weren't initialized yet.
 949         */
 950        if (!num_registered_fb || info_idx == -1)
 951                return display_desc;
 952        /*
 953         * Instead of blindly using registered_fb[0], we use info_idx, set by
 954         * fb_console_init();
 955         */
 956        info = registered_fb[info_idx];
 957        if (!info)
 958                return NULL;
 959        
 960        owner = info->fbops->owner;
 961        if (!try_module_get(owner))
 962                return NULL;
 963        if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
 964                module_put(owner);
 965                return NULL;
 966        }
 967
 968        ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
 969        if (!ops) {
 970                module_put(owner);
 971                return NULL;
 972        }
 973
 974        ops->currcon = -1;
 975        ops->graphics = 1;
 976        ops->cur_rotate = -1;
 977        ops->cur_blink_jiffies = HZ / 5;
 978        ops->info = info;
 979        info->fbcon_par = ops;
 980
 981        p->con_rotate = initial_rotation;
 982        if (p->con_rotate == -1)
 983                p->con_rotate = info->fbcon_rotate_hint;
 984        if (p->con_rotate == -1)
 985                p->con_rotate = FB_ROTATE_UR;
 986
 987        set_blitting_type(vc, info);
 988
 989        /* Setup default font */
 990        if (!p->fontdata && !vc->vc_font.data) {
 991                if (!fontname[0] || !(font = find_font(fontname)))
 992                        font = get_default_font(info->var.xres,
 993                                                info->var.yres,
 994                                                info->pixmap.blit_x,
 995                                                info->pixmap.blit_y);
 996                vc->vc_font.width = font->width;
 997                vc->vc_font.height = font->height;
 998                vc->vc_font.data = (void *)(p->fontdata = font->data);
 999                vc->vc_font.charcount = font->charcount;
1000        } else {
1001                p->fontdata = vc->vc_font.data;
1002        }
1003
1004        cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1005        rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1006        cols /= vc->vc_font.width;
1007        rows /= vc->vc_font.height;
1008        vc_resize(vc, cols, rows);
1009
1010        pr_debug("mode:   %s\n", info->fix.id);
1011        pr_debug("visual: %d\n", info->fix.visual);
1012        pr_debug("res:    %dx%d-%d\n", info->var.xres,
1013                 info->var.yres,
1014                 info->var.bits_per_pixel);
1015
1016        fbcon_add_cursor_timer(info);
1017        return display_desc;
1018}
1019
1020static void fbcon_init(struct vc_data *vc, int init)
1021{
1022        struct fb_info *info;
1023        struct fbcon_ops *ops;
1024        struct vc_data **default_mode = vc->vc_display_fg;
1025        struct vc_data *svc = *default_mode;
1026        struct fbcon_display *t, *p = &fb_display[vc->vc_num];
1027        int logo = 1, new_rows, new_cols, rows, cols;
1028        int ret;
1029
1030        if (WARN_ON(info_idx == -1))
1031            return;
1032
1033        if (con2fb_map[vc->vc_num] == -1)
1034                con2fb_map[vc->vc_num] = info_idx;
1035
1036        info = registered_fb[con2fb_map[vc->vc_num]];
1037
1038        if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
1039                logo_shown = FBCON_LOGO_DONTSHOW;
1040
1041        if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
1042            (info->fix.type == FB_TYPE_TEXT))
1043                logo = 0;
1044
1045        if (var_to_display(p, &info->var, info))
1046                return;
1047
1048        if (!info->fbcon_par)
1049                con2fb_acquire_newinfo(vc, info, vc->vc_num, -1);
1050
1051        /* If we are not the first console on this
1052           fb, copy the font from that console */
1053        t = &fb_display[fg_console];
1054        if (!p->fontdata) {
1055                if (t->fontdata) {
1056                        struct vc_data *fvc = vc_cons[fg_console].d;
1057
1058                        vc->vc_font.data = (void *)(p->fontdata =
1059                                                    fvc->vc_font.data);
1060                        vc->vc_font.width = fvc->vc_font.width;
1061                        vc->vc_font.height = fvc->vc_font.height;
1062                        vc->vc_font.charcount = fvc->vc_font.charcount;
1063                        p->userfont = t->userfont;
1064
1065                        if (p->userfont)
1066                                REFCOUNT(p->fontdata)++;
1067                } else {
1068                        const struct font_desc *font = NULL;
1069
1070                        if (!fontname[0] || !(font = find_font(fontname)))
1071                                font = get_default_font(info->var.xres,
1072                                                        info->var.yres,
1073                                                        info->pixmap.blit_x,
1074                                                        info->pixmap.blit_y);
1075                        vc->vc_font.width = font->width;
1076                        vc->vc_font.height = font->height;
1077                        vc->vc_font.data = (void *)(p->fontdata = font->data);
1078                        vc->vc_font.charcount = font->charcount;
1079                }
1080        }
1081
1082        vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1083        vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1084        if (vc->vc_font.charcount == 256) {
1085                vc->vc_hi_font_mask = 0;
1086        } else {
1087                vc->vc_hi_font_mask = 0x100;
1088                if (vc->vc_can_do_color)
1089                        vc->vc_complement_mask <<= 1;
1090        }
1091
1092        if (!*svc->vc_uni_pagedir_loc)
1093                con_set_default_unimap(svc);
1094        if (!*vc->vc_uni_pagedir_loc)
1095                con_copy_unimap(vc, svc);
1096
1097        ops = info->fbcon_par;
1098        ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1099
1100        p->con_rotate = initial_rotation;
1101        if (p->con_rotate == -1)
1102                p->con_rotate = info->fbcon_rotate_hint;
1103        if (p->con_rotate == -1)
1104                p->con_rotate = FB_ROTATE_UR;
1105
1106        set_blitting_type(vc, info);
1107
1108        cols = vc->vc_cols;
1109        rows = vc->vc_rows;
1110        new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1111        new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1112        new_cols /= vc->vc_font.width;
1113        new_rows /= vc->vc_font.height;
1114
1115        /*
1116         * We must always set the mode. The mode of the previous console
1117         * driver could be in the same resolution but we are using different
1118         * hardware so we have to initialize the hardware.
1119         *
1120         * We need to do it in fbcon_init() to prevent screen corruption.
1121         */
1122        if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
1123                if (info->fbops->fb_set_par &&
1124                    !(ops->flags & FBCON_FLAGS_INIT)) {
1125                        ret = info->fbops->fb_set_par(info);
1126
1127                        if (ret)
1128                                printk(KERN_ERR "fbcon_init: detected "
1129                                        "unhandled fb_set_par error, "
1130                                        "error code %d\n", ret);
1131                }
1132
1133                ops->flags |= FBCON_FLAGS_INIT;
1134        }
1135
1136        ops->graphics = 0;
1137
1138        /*
1139         * No more hw acceleration for fbcon.
1140         *
1141         * FIXME: Garbage collect all the now dead code after sufficient time
1142         * has passed.
1143         */
1144        p->scrollmode = SCROLL_REDRAW;
1145
1146        /*
1147         *  ++guenther: console.c:vc_allocate() relies on initializing
1148         *  vc_{cols,rows}, but we must not set those if we are only
1149         *  resizing the console.
1150         */
1151        if (init) {
1152                vc->vc_cols = new_cols;
1153                vc->vc_rows = new_rows;
1154        } else
1155                vc_resize(vc, new_cols, new_rows);
1156
1157        if (logo)
1158                fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
1159
1160        if (ops->rotate_font && ops->rotate_font(info, vc)) {
1161                ops->rotate = FB_ROTATE_UR;
1162                set_blitting_type(vc, info);
1163        }
1164
1165        ops->p = &fb_display[fg_console];
1166}
1167
1168static void fbcon_free_font(struct fbcon_display *p, bool freefont)
1169{
1170        if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1171                kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
1172        p->fontdata = NULL;
1173        p->userfont = 0;
1174}
1175
1176static void set_vc_hi_font(struct vc_data *vc, bool set);
1177
1178static void fbcon_deinit(struct vc_data *vc)
1179{
1180        struct fbcon_display *p = &fb_display[vc->vc_num];
1181        struct fb_info *info;
1182        struct fbcon_ops *ops;
1183        int idx;
1184        bool free_font = true;
1185
1186        idx = con2fb_map[vc->vc_num];
1187
1188        if (idx == -1)
1189                goto finished;
1190
1191        info = registered_fb[idx];
1192
1193        if (!info)
1194                goto finished;
1195
1196        if (info->flags & FBINFO_MISC_FIRMWARE)
1197                free_font = false;
1198        ops = info->fbcon_par;
1199
1200        if (!ops)
1201                goto finished;
1202
1203        if (con_is_visible(vc))
1204                fbcon_del_cursor_timer(info);
1205
1206        ops->flags &= ~FBCON_FLAGS_INIT;
1207finished:
1208
1209        fbcon_free_font(p, free_font);
1210        if (free_font)
1211                vc->vc_font.data = NULL;
1212
1213        if (vc->vc_hi_font_mask && vc->vc_screenbuf)
1214                set_vc_hi_font(vc, false);
1215
1216        if (!con_is_bound(&fb_con))
1217                fbcon_exit();
1218
1219        if (vc->vc_num == logo_shown)
1220                logo_shown = FBCON_LOGO_CANSHOW;
1221
1222        return;
1223}
1224
1225/* ====================================================================== */
1226
1227/*  fbcon_XXX routines - interface used by the world
1228 *
1229 *  This system is now divided into two levels because of complications
1230 *  caused by hardware scrolling. Top level functions:
1231 *
1232 *      fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins()
1233 *
1234 *  handles y values in range [0, scr_height-1] that correspond to real
1235 *  screen positions. y_wrap shift means that first line of bitmap may be
1236 *  anywhere on this display. These functions convert lineoffsets to
1237 *  bitmap offsets and deal with the wrap-around case by splitting blits.
1238 *
1239 *      fbcon_bmove_physical_8()    -- These functions fast implementations
1240 *      fbcon_clear_physical_8()    -- of original fbcon_XXX fns.
1241 *      fbcon_putc_physical_8()     -- (font width != 8) may be added later
1242 *
1243 *  WARNING:
1244 *
1245 *  At the moment fbcon_putc() cannot blit across vertical wrap boundary
1246 *  Implies should only really hardware scroll in rows. Only reason for
1247 *  restriction is simplicity & efficiency at the moment.
1248 */
1249
1250static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
1251                        int width)
1252{
1253        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1254        struct fbcon_ops *ops = info->fbcon_par;
1255
1256        struct fbcon_display *p = &fb_display[vc->vc_num];
1257        u_int y_break;
1258
1259        if (fbcon_is_inactive(vc, info))
1260                return;
1261
1262        if (!height || !width)
1263                return;
1264
1265        if (sy < vc->vc_top && vc->vc_top == logo_lines) {
1266                vc->vc_top = 0;
1267                /*
1268                 * If the font dimensions are not an integral of the display
1269                 * dimensions then the ops->clear below won't end up clearing
1270                 * the margins.  Call clear_margins here in case the logo
1271                 * bitmap stretched into the margin area.
1272                 */
1273                fbcon_clear_margins(vc, 0);
1274        }
1275
1276        /* Split blits that cross physical y_wrap boundary */
1277
1278        y_break = p->vrows - p->yscroll;
1279        if (sy < y_break && sy + height - 1 >= y_break) {
1280                u_int b = y_break - sy;
1281                ops->clear(vc, info, real_y(p, sy), sx, b, width);
1282                ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
1283                                 width);
1284        } else
1285                ops->clear(vc, info, real_y(p, sy), sx, height, width);
1286}
1287
1288static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
1289                        int count, int ypos, int xpos)
1290{
1291        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1292        struct fbcon_display *p = &fb_display[vc->vc_num];
1293        struct fbcon_ops *ops = info->fbcon_par;
1294
1295        if (!fbcon_is_inactive(vc, info))
1296                ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
1297                           get_color(vc, info, scr_readw(s), 1),
1298                           get_color(vc, info, scr_readw(s), 0));
1299}
1300
1301static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
1302{
1303        unsigned short chr;
1304
1305        scr_writew(c, &chr);
1306        fbcon_putcs(vc, &chr, 1, ypos, xpos);
1307}
1308
1309static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
1310{
1311        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1312        struct fbcon_ops *ops = info->fbcon_par;
1313
1314        if (!fbcon_is_inactive(vc, info))
1315                ops->clear_margins(vc, info, margin_color, bottom_only);
1316}
1317
1318static void fbcon_cursor(struct vc_data *vc, int mode)
1319{
1320        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1321        struct fbcon_ops *ops = info->fbcon_par;
1322        int c = scr_readw((u16 *) vc->vc_pos);
1323
1324        ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1325
1326        if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1)
1327                return;
1328
1329        if (vc->vc_cursor_type & CUR_SW)
1330                fbcon_del_cursor_timer(info);
1331        else
1332                fbcon_add_cursor_timer(info);
1333
1334        ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
1335
1336        if (!ops->cursor)
1337                return;
1338
1339        ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
1340                    get_color(vc, info, c, 0));
1341}
1342
1343static int scrollback_phys_max = 0;
1344static int scrollback_max = 0;
1345static int scrollback_current = 0;
1346
1347static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1348                           int unit)
1349{
1350        struct fbcon_display *p, *t;
1351        struct vc_data **default_mode, *vc;
1352        struct vc_data *svc;
1353        struct fbcon_ops *ops = info->fbcon_par;
1354        int rows, cols;
1355
1356        p = &fb_display[unit];
1357
1358        if (var_to_display(p, var, info))
1359                return;
1360
1361        vc = vc_cons[unit].d;
1362
1363        if (!vc)
1364                return;
1365
1366        default_mode = vc->vc_display_fg;
1367        svc = *default_mode;
1368        t = &fb_display[svc->vc_num];
1369
1370        if (!vc->vc_font.data) {
1371                vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
1372                vc->vc_font.width = (*default_mode)->vc_font.width;
1373                vc->vc_font.height = (*default_mode)->vc_font.height;
1374                vc->vc_font.charcount = (*default_mode)->vc_font.charcount;
1375                p->userfont = t->userfont;
1376                if (p->userfont)
1377                        REFCOUNT(p->fontdata)++;
1378        }
1379
1380        var->activate = FB_ACTIVATE_NOW;
1381        info->var.activate = var->activate;
1382        var->yoffset = info->var.yoffset;
1383        var->xoffset = info->var.xoffset;
1384        fb_set_var(info, var);
1385        ops->var = info->var;
1386        vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1387        vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1388        if (vc->vc_font.charcount == 256) {
1389                vc->vc_hi_font_mask = 0;
1390        } else {
1391                vc->vc_hi_font_mask = 0x100;
1392                if (vc->vc_can_do_color)
1393                        vc->vc_complement_mask <<= 1;
1394        }
1395
1396        if (!*svc->vc_uni_pagedir_loc)
1397                con_set_default_unimap(svc);
1398        if (!*vc->vc_uni_pagedir_loc)
1399                con_copy_unimap(vc, svc);
1400
1401        cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1402        rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1403        cols /= vc->vc_font.width;
1404        rows /= vc->vc_font.height;
1405        vc_resize(vc, cols, rows);
1406
1407        if (con_is_visible(vc)) {
1408                update_screen(vc);
1409        }
1410}
1411
1412static __inline__ void ywrap_up(struct vc_data *vc, int count)
1413{
1414        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1415        struct fbcon_ops *ops = info->fbcon_par;
1416        struct fbcon_display *p = &fb_display[vc->vc_num];
1417        
1418        p->yscroll += count;
1419        if (p->yscroll >= p->vrows)     /* Deal with wrap */
1420                p->yscroll -= p->vrows;
1421        ops->var.xoffset = 0;
1422        ops->var.yoffset = p->yscroll * vc->vc_font.height;
1423        ops->var.vmode |= FB_VMODE_YWRAP;
1424        ops->update_start(info);
1425        scrollback_max += count;
1426        if (scrollback_max > scrollback_phys_max)
1427                scrollback_max = scrollback_phys_max;
1428        scrollback_current = 0;
1429}
1430
1431static __inline__ void ywrap_down(struct vc_data *vc, int count)
1432{
1433        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1434        struct fbcon_ops *ops = info->fbcon_par;
1435        struct fbcon_display *p = &fb_display[vc->vc_num];
1436        
1437        p->yscroll -= count;
1438        if (p->yscroll < 0)     /* Deal with wrap */
1439                p->yscroll += p->vrows;
1440        ops->var.xoffset = 0;
1441        ops->var.yoffset = p->yscroll * vc->vc_font.height;
1442        ops->var.vmode |= FB_VMODE_YWRAP;
1443        ops->update_start(info);
1444        scrollback_max -= count;
1445        if (scrollback_max < 0)
1446                scrollback_max = 0;
1447        scrollback_current = 0;
1448}
1449
1450static __inline__ void ypan_up(struct vc_data *vc, int count)
1451{
1452        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1453        struct fbcon_display *p = &fb_display[vc->vc_num];
1454        struct fbcon_ops *ops = info->fbcon_par;
1455
1456        p->yscroll += count;
1457        if (p->yscroll > p->vrows - vc->vc_rows) {
1458                ops->bmove(vc, info, p->vrows - vc->vc_rows,
1459                            0, 0, 0, vc->vc_rows, vc->vc_cols);
1460                p->yscroll -= p->vrows - vc->vc_rows;
1461        }
1462
1463        ops->var.xoffset = 0;
1464        ops->var.yoffset = p->yscroll * vc->vc_font.height;
1465        ops->var.vmode &= ~FB_VMODE_YWRAP;
1466        ops->update_start(info);
1467        fbcon_clear_margins(vc, 1);
1468        scrollback_max += count;
1469        if (scrollback_max > scrollback_phys_max)
1470                scrollback_max = scrollback_phys_max;
1471        scrollback_current = 0;
1472}
1473
1474static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1475{
1476        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1477        struct fbcon_ops *ops = info->fbcon_par;
1478        struct fbcon_display *p = &fb_display[vc->vc_num];
1479
1480        p->yscroll += count;
1481
1482        if (p->yscroll > p->vrows - vc->vc_rows) {
1483                p->yscroll -= p->vrows - vc->vc_rows;
1484                fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
1485        }
1486
1487        ops->var.xoffset = 0;
1488        ops->var.yoffset = p->yscroll * vc->vc_font.height;
1489        ops->var.vmode &= ~FB_VMODE_YWRAP;
1490        ops->update_start(info);
1491        fbcon_clear_margins(vc, 1);
1492        scrollback_max += count;
1493        if (scrollback_max > scrollback_phys_max)
1494                scrollback_max = scrollback_phys_max;
1495        scrollback_current = 0;
1496}
1497
1498static __inline__ void ypan_down(struct vc_data *vc, int count)
1499{
1500        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1501        struct fbcon_display *p = &fb_display[vc->vc_num];
1502        struct fbcon_ops *ops = info->fbcon_par;
1503        
1504        p->yscroll -= count;
1505        if (p->yscroll < 0) {
1506                ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1507                            0, vc->vc_rows, vc->vc_cols);
1508                p->yscroll += p->vrows - vc->vc_rows;
1509        }
1510
1511        ops->var.xoffset = 0;
1512        ops->var.yoffset = p->yscroll * vc->vc_font.height;
1513        ops->var.vmode &= ~FB_VMODE_YWRAP;
1514        ops->update_start(info);
1515        fbcon_clear_margins(vc, 1);
1516        scrollback_max -= count;
1517        if (scrollback_max < 0)
1518                scrollback_max = 0;
1519        scrollback_current = 0;
1520}
1521
1522static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1523{
1524        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1525        struct fbcon_ops *ops = info->fbcon_par;
1526        struct fbcon_display *p = &fb_display[vc->vc_num];
1527
1528        p->yscroll -= count;
1529
1530        if (p->yscroll < 0) {
1531                p->yscroll += p->vrows - vc->vc_rows;
1532                fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
1533        }
1534
1535        ops->var.xoffset = 0;
1536        ops->var.yoffset = p->yscroll * vc->vc_font.height;
1537        ops->var.vmode &= ~FB_VMODE_YWRAP;
1538        ops->update_start(info);
1539        fbcon_clear_margins(vc, 1);
1540        scrollback_max -= count;
1541        if (scrollback_max < 0)
1542                scrollback_max = 0;
1543        scrollback_current = 0;
1544}
1545
1546static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
1547                              int line, int count, int dy)
1548{
1549        unsigned short *s = (unsigned short *)
1550                (vc->vc_origin + vc->vc_size_row * line);
1551
1552        while (count--) {
1553                unsigned short *start = s;
1554                unsigned short *le = advance_row(s, 1);
1555                unsigned short c;
1556                int x = 0;
1557                unsigned short attr = 1;
1558
1559                do {
1560                        c = scr_readw(s);
1561                        if (attr != (c & 0xff00)) {
1562                                attr = c & 0xff00;
1563                                if (s > start) {
1564                                        fbcon_putcs(vc, start, s - start,
1565                                                    dy, x);
1566                                        x += s - start;
1567                                        start = s;
1568                                }
1569                        }
1570                        console_conditional_schedule();
1571                        s++;
1572                } while (s < le);
1573                if (s > start)
1574                        fbcon_putcs(vc, start, s - start, dy, x);
1575                console_conditional_schedule();
1576                dy++;
1577        }
1578}
1579
1580static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
1581                        struct fbcon_display *p, int line, int count, int ycount)
1582{
1583        int offset = ycount * vc->vc_cols;
1584        unsigned short *d = (unsigned short *)
1585            (vc->vc_origin + vc->vc_size_row * line);
1586        unsigned short *s = d + offset;
1587        struct fbcon_ops *ops = info->fbcon_par;
1588
1589        while (count--) {
1590                unsigned short *start = s;
1591                unsigned short *le = advance_row(s, 1);
1592                unsigned short c;
1593                int x = 0;
1594
1595                do {
1596                        c = scr_readw(s);
1597
1598                        if (c == scr_readw(d)) {
1599                                if (s > start) {
1600                                        ops->bmove(vc, info, line + ycount, x,
1601                                                   line, x, 1, s-start);
1602                                        x += s - start + 1;
1603                                        start = s + 1;
1604                                } else {
1605                                        x++;
1606                                        start++;
1607                                }
1608                        }
1609
1610                        scr_writew(c, d);
1611                        console_conditional_schedule();
1612                        s++;
1613                        d++;
1614                } while (s < le);
1615                if (s > start)
1616                        ops->bmove(vc, info, line + ycount, x, line, x, 1,
1617                                   s-start);
1618                console_conditional_schedule();
1619                if (ycount > 0)
1620                        line++;
1621                else {
1622                        line--;
1623                        /* NOTE: We subtract two lines from these pointers */
1624                        s -= vc->vc_size_row;
1625                        d -= vc->vc_size_row;
1626                }
1627        }
1628}
1629
1630static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p,
1631                         int line, int count, int offset)
1632{
1633        unsigned short *d = (unsigned short *)
1634            (vc->vc_origin + vc->vc_size_row * line);
1635        unsigned short *s = d + offset;
1636
1637        while (count--) {
1638                unsigned short *start = s;
1639                unsigned short *le = advance_row(s, 1);
1640                unsigned short c;
1641                int x = 0;
1642                unsigned short attr = 1;
1643
1644                do {
1645                        c = scr_readw(s);
1646                        if (attr != (c & 0xff00)) {
1647                                attr = c & 0xff00;
1648                                if (s > start) {
1649                                        fbcon_putcs(vc, start, s - start,
1650                                                    line, x);
1651                                        x += s - start;
1652                                        start = s;
1653                                }
1654                        }
1655                        if (c == scr_readw(d)) {
1656                                if (s > start) {
1657                                        fbcon_putcs(vc, start, s - start,
1658                                                     line, x);
1659                                        x += s - start + 1;
1660                                        start = s + 1;
1661                                } else {
1662                                        x++;
1663                                        start++;
1664                                }
1665                        }
1666                        scr_writew(c, d);
1667                        console_conditional_schedule();
1668                        s++;
1669                        d++;
1670                } while (s < le);
1671                if (s > start)
1672                        fbcon_putcs(vc, start, s - start, line, x);
1673                console_conditional_schedule();
1674                if (offset > 0)
1675                        line++;
1676                else {
1677                        line--;
1678                        /* NOTE: We subtract two lines from these pointers */
1679                        s -= vc->vc_size_row;
1680                        d -= vc->vc_size_row;
1681                }
1682        }
1683}
1684
1685static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
1686                enum con_scroll dir, unsigned int count)
1687{
1688        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1689        struct fbcon_display *p = &fb_display[vc->vc_num];
1690        int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
1691
1692        if (fbcon_is_inactive(vc, info))
1693                return true;
1694
1695        fbcon_cursor(vc, CM_ERASE);
1696
1697        /*
1698         * ++Geert: Only use ywrap/ypan if the console is in text mode
1699         * ++Andrew: Only use ypan on hardware text mode when scrolling the
1700         *           whole screen (prevents flicker).
1701         */
1702
1703        switch (dir) {
1704        case SM_UP:
1705                if (count > vc->vc_rows)        /* Maximum realistic size */
1706                        count = vc->vc_rows;
1707                if (logo_shown >= 0)
1708                        goto redraw_up;
1709                switch (p->scrollmode) {
1710                case SCROLL_MOVE:
1711                        fbcon_redraw_blit(vc, info, p, t, b - t - count,
1712                                     count);
1713                        fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1714                        scr_memsetw((unsigned short *) (vc->vc_origin +
1715                                                        vc->vc_size_row *
1716                                                        (b - count)),
1717                                    vc->vc_video_erase_char,
1718                                    vc->vc_size_row * count);
1719                        return true;
1720
1721                case SCROLL_WRAP_MOVE:
1722                        if (b - t - count > 3 * vc->vc_rows >> 2) {
1723                                if (t > 0)
1724                                        fbcon_bmove(vc, 0, 0, count, 0, t,
1725                                                    vc->vc_cols);
1726                                ywrap_up(vc, count);
1727                                if (vc->vc_rows - b > 0)
1728                                        fbcon_bmove(vc, b - count, 0, b, 0,
1729                                                    vc->vc_rows - b,
1730                                                    vc->vc_cols);
1731                        } else if (info->flags & FBINFO_READS_FAST)
1732                                fbcon_bmove(vc, t + count, 0, t, 0,
1733                                            b - t - count, vc->vc_cols);
1734                        else
1735                                goto redraw_up;
1736                        fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1737                        break;
1738
1739                case SCROLL_PAN_REDRAW:
1740                        if ((p->yscroll + count <=
1741                             2 * (p->vrows - vc->vc_rows))
1742                            && ((!scroll_partial && (b - t == vc->vc_rows))
1743                                || (scroll_partial
1744                                    && (b - t - count >
1745                                        3 * vc->vc_rows >> 2)))) {
1746                                if (t > 0)
1747                                        fbcon_redraw_move(vc, p, 0, t, count);
1748                                ypan_up_redraw(vc, t, count);
1749                                if (vc->vc_rows - b > 0)
1750                                        fbcon_redraw_move(vc, p, b,
1751                                                          vc->vc_rows - b, b);
1752                        } else
1753                                fbcon_redraw_move(vc, p, t + count, b - t - count, t);
1754                        fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1755                        break;
1756
1757                case SCROLL_PAN_MOVE:
1758                        if ((p->yscroll + count <=
1759                             2 * (p->vrows - vc->vc_rows))
1760                            && ((!scroll_partial && (b - t == vc->vc_rows))
1761                                || (scroll_partial
1762                                    && (b - t - count >
1763                                        3 * vc->vc_rows >> 2)))) {
1764                                if (t > 0)
1765                                        fbcon_bmove(vc, 0, 0, count, 0, t,
1766                                                    vc->vc_cols);
1767                                ypan_up(vc, count);
1768                                if (vc->vc_rows - b > 0)
1769                                        fbcon_bmove(vc, b - count, 0, b, 0,
1770                                                    vc->vc_rows - b,
1771                                                    vc->vc_cols);
1772                        } else if (info->flags & FBINFO_READS_FAST)
1773                                fbcon_bmove(vc, t + count, 0, t, 0,
1774                                            b - t - count, vc->vc_cols);
1775                        else
1776                                goto redraw_up;
1777                        fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1778                        break;
1779
1780                case SCROLL_REDRAW:
1781                      redraw_up:
1782                        fbcon_redraw(vc, p, t, b - t - count,
1783                                     count * vc->vc_cols);
1784                        fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1785                        scr_memsetw((unsigned short *) (vc->vc_origin +
1786                                                        vc->vc_size_row *
1787                                                        (b - count)),
1788                                    vc->vc_video_erase_char,
1789                                    vc->vc_size_row * count);
1790                        return true;
1791                }
1792                break;
1793
1794        case SM_DOWN:
1795                if (count > vc->vc_rows)        /* Maximum realistic size */
1796                        count = vc->vc_rows;
1797                if (logo_shown >= 0)
1798                        goto redraw_down;
1799                switch (p->scrollmode) {
1800                case SCROLL_MOVE:
1801                        fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
1802                                     -count);
1803                        fbcon_clear(vc, t, 0, count, vc->vc_cols);
1804                        scr_memsetw((unsigned short *) (vc->vc_origin +
1805                                                        vc->vc_size_row *
1806                                                        t),
1807                                    vc->vc_video_erase_char,
1808                                    vc->vc_size_row * count);
1809                        return true;
1810
1811                case SCROLL_WRAP_MOVE:
1812                        if (b - t - count > 3 * vc->vc_rows >> 2) {
1813                                if (vc->vc_rows - b > 0)
1814                                        fbcon_bmove(vc, b, 0, b - count, 0,
1815                                                    vc->vc_rows - b,
1816                                                    vc->vc_cols);
1817                                ywrap_down(vc, count);
1818                                if (t > 0)
1819                                        fbcon_bmove(vc, count, 0, 0, 0, t,
1820                                                    vc->vc_cols);
1821                        } else if (info->flags & FBINFO_READS_FAST)
1822                                fbcon_bmove(vc, t, 0, t + count, 0,
1823                                            b - t - count, vc->vc_cols);
1824                        else
1825                                goto redraw_down;
1826                        fbcon_clear(vc, t, 0, count, vc->vc_cols);
1827                        break;
1828
1829                case SCROLL_PAN_MOVE:
1830                        if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1831                            && ((!scroll_partial && (b - t == vc->vc_rows))
1832                                || (scroll_partial
1833                                    && (b - t - count >
1834                                        3 * vc->vc_rows >> 2)))) {
1835                                if (vc->vc_rows - b > 0)
1836                                        fbcon_bmove(vc, b, 0, b - count, 0,
1837                                                    vc->vc_rows - b,
1838                                                    vc->vc_cols);
1839                                ypan_down(vc, count);
1840                                if (t > 0)
1841                                        fbcon_bmove(vc, count, 0, 0, 0, t,
1842                                                    vc->vc_cols);
1843                        } else if (info->flags & FBINFO_READS_FAST)
1844                                fbcon_bmove(vc, t, 0, t + count, 0,
1845                                            b - t - count, vc->vc_cols);
1846                        else
1847                                goto redraw_down;
1848                        fbcon_clear(vc, t, 0, count, vc->vc_cols);
1849                        break;
1850
1851                case SCROLL_PAN_REDRAW:
1852                        if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1853                            && ((!scroll_partial && (b - t == vc->vc_rows))
1854                                || (scroll_partial
1855                                    && (b - t - count >
1856                                        3 * vc->vc_rows >> 2)))) {
1857                                if (vc->vc_rows - b > 0)
1858                                        fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
1859                                                          b - count);
1860                                ypan_down_redraw(vc, t, count);
1861                                if (t > 0)
1862                                        fbcon_redraw_move(vc, p, count, t, 0);
1863                        } else
1864                                fbcon_redraw_move(vc, p, t, b - t - count, t + count);
1865                        fbcon_clear(vc, t, 0, count, vc->vc_cols);
1866                        break;
1867
1868                case SCROLL_REDRAW:
1869                      redraw_down:
1870                        fbcon_redraw(vc, p, b - 1, b - t - count,
1871                                     -count * vc->vc_cols);
1872                        fbcon_clear(vc, t, 0, count, vc->vc_cols);
1873                        scr_memsetw((unsigned short *) (vc->vc_origin +
1874                                                        vc->vc_size_row *
1875                                                        t),
1876                                    vc->vc_video_erase_char,
1877                                    vc->vc_size_row * count);
1878                        return true;
1879                }
1880        }
1881        return false;
1882}
1883
1884
1885static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1886                        int height, int width)
1887{
1888        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1889        struct fbcon_display *p = &fb_display[vc->vc_num];
1890        
1891        if (fbcon_is_inactive(vc, info))
1892                return;
1893
1894        if (!width || !height)
1895                return;
1896
1897        /*  Split blits that cross physical y_wrap case.
1898         *  Pathological case involves 4 blits, better to use recursive
1899         *  code rather than unrolled case
1900         *
1901         *  Recursive invocations don't need to erase the cursor over and
1902         *  over again, so we use fbcon_bmove_rec()
1903         */
1904        fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1905                        p->vrows - p->yscroll);
1906}
1907
1908static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
1909                            int dy, int dx, int height, int width, u_int y_break)
1910{
1911        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1912        struct fbcon_ops *ops = info->fbcon_par;
1913        u_int b;
1914
1915        if (sy < y_break && sy + height > y_break) {
1916                b = y_break - sy;
1917                if (dy < sy) {  /* Avoid trashing self */
1918                        fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1919                                        y_break);
1920                        fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1921                                        height - b, width, y_break);
1922                } else {
1923                        fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1924                                        height - b, width, y_break);
1925                        fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1926                                        y_break);
1927                }
1928                return;
1929        }
1930
1931        if (dy < y_break && dy + height > y_break) {
1932                b = y_break - dy;
1933                if (dy < sy) {  /* Avoid trashing self */
1934                        fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1935                                        y_break);
1936                        fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1937                                        height - b, width, y_break);
1938                } else {
1939                        fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1940                                        height - b, width, y_break);
1941                        fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1942                                        y_break);
1943                }
1944                return;
1945        }
1946        ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1947                   height, width);
1948}
1949
1950static void updatescrollmode(struct fbcon_display *p,
1951                                        struct fb_info *info,
1952                                        struct vc_data *vc)
1953{
1954        struct fbcon_ops *ops = info->fbcon_par;
1955        int fh = vc->vc_font.height;
1956        int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1957        int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
1958                                   info->var.xres_virtual);
1959
1960        p->vrows = vyres/fh;
1961        if (yres > (fh * (vc->vc_rows + 1)))
1962                p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
1963        if ((yres % fh) && (vyres % fh < yres % fh))
1964                p->vrows--;
1965}
1966
1967#define PITCH(w) (((w) + 7) >> 3)
1968#define CALC_FONTSZ(h, p, c) ((h) * (p) * (c)) /* size = height * pitch * charcount */
1969
1970static int fbcon_resize(struct vc_data *vc, unsigned int width, 
1971                        unsigned int height, unsigned int user)
1972{
1973        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1974        struct fbcon_ops *ops = info->fbcon_par;
1975        struct fbcon_display *p = &fb_display[vc->vc_num];
1976        struct fb_var_screeninfo var = info->var;
1977        int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
1978
1979        if (p->userfont && FNTSIZE(vc->vc_font.data)) {
1980                int size;
1981                int pitch = PITCH(vc->vc_font.width);
1982
1983                /*
1984                 * If user font, ensure that a possible change to user font
1985                 * height or width will not allow a font data out-of-bounds access.
1986                 * NOTE: must use original charcount in calculation as font
1987                 * charcount can change and cannot be used to determine the
1988                 * font data allocated size.
1989                 */
1990                if (pitch <= 0)
1991                        return -EINVAL;
1992                size = CALC_FONTSZ(vc->vc_font.height, pitch, vc->vc_font.charcount);
1993                if (size > FNTSIZE(vc->vc_font.data))
1994                        return -EINVAL;
1995        }
1996
1997        virt_w = FBCON_SWAP(ops->rotate, width, height);
1998        virt_h = FBCON_SWAP(ops->rotate, height, width);
1999        virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
2000                                 vc->vc_font.height);
2001        virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height,
2002                                 vc->vc_font.width);
2003        var.xres = virt_w * virt_fw;
2004        var.yres = virt_h * virt_fh;
2005        x_diff = info->var.xres - var.xres;
2006        y_diff = info->var.yres - var.yres;
2007        if (x_diff < 0 || x_diff > virt_fw ||
2008            y_diff < 0 || y_diff > virt_fh) {
2009                const struct fb_videomode *mode;
2010
2011                pr_debug("attempting resize %ix%i\n", var.xres, var.yres);
2012                mode = fb_find_best_mode(&var, &info->modelist);
2013                if (mode == NULL)
2014                        return -EINVAL;
2015                display_to_var(&var, p);
2016                fb_videomode_to_var(&var, mode);
2017
2018                if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh)
2019                        return -EINVAL;
2020
2021                pr_debug("resize now %ix%i\n", var.xres, var.yres);
2022                if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
2023                        var.activate = FB_ACTIVATE_NOW |
2024                                FB_ACTIVATE_FORCE;
2025                        fb_set_var(info, &var);
2026                }
2027                var_to_display(p, &info->var, info);
2028                ops->var = info->var;
2029        }
2030        updatescrollmode(p, info, vc);
2031        return 0;
2032}
2033
2034static int fbcon_switch(struct vc_data *vc)
2035{
2036        struct fb_info *info, *old_info = NULL;
2037        struct fbcon_ops *ops;
2038        struct fbcon_display *p = &fb_display[vc->vc_num];
2039        struct fb_var_screeninfo var;
2040        int i, ret, prev_console;
2041
2042        info = registered_fb[con2fb_map[vc->vc_num]];
2043        ops = info->fbcon_par;
2044
2045        if (logo_shown >= 0) {
2046                struct vc_data *conp2 = vc_cons[logo_shown].d;
2047
2048                if (conp2->vc_top == logo_lines
2049                    && conp2->vc_bottom == conp2->vc_rows)
2050                        conp2->vc_top = 0;
2051                logo_shown = FBCON_LOGO_CANSHOW;
2052        }
2053
2054        prev_console = ops->currcon;
2055        if (prev_console != -1)
2056                old_info = registered_fb[con2fb_map[prev_console]];
2057        /*
2058         * FIXME: If we have multiple fbdev's loaded, we need to
2059         * update all info->currcon.  Perhaps, we can place this
2060         * in a centralized structure, but this might break some
2061         * drivers.
2062         *
2063         * info->currcon = vc->vc_num;
2064         */
2065        for_each_registered_fb(i) {
2066                if (registered_fb[i]->fbcon_par) {
2067                        struct fbcon_ops *o = registered_fb[i]->fbcon_par;
2068
2069                        o->currcon = vc->vc_num;
2070                }
2071        }
2072        memset(&var, 0, sizeof(struct fb_var_screeninfo));
2073        display_to_var(&var, p);
2074        var.activate = FB_ACTIVATE_NOW;
2075
2076        /*
2077         * make sure we don't unnecessarily trip the memcmp()
2078         * in fb_set_var()
2079         */
2080        info->var.activate = var.activate;
2081        var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
2082        fb_set_var(info, &var);
2083        ops->var = info->var;
2084
2085        if (old_info != NULL && (old_info != info ||
2086                                 info->flags & FBINFO_MISC_ALWAYS_SETPAR)) {
2087                if (info->fbops->fb_set_par) {
2088                        ret = info->fbops->fb_set_par(info);
2089
2090                        if (ret)
2091                                printk(KERN_ERR "fbcon_switch: detected "
2092                                        "unhandled fb_set_par error, "
2093                                        "error code %d\n", ret);
2094                }
2095
2096                if (old_info != info)
2097                        fbcon_del_cursor_timer(old_info);
2098        }
2099
2100        if (fbcon_is_inactive(vc, info) ||
2101            ops->blank_state != FB_BLANK_UNBLANK)
2102                fbcon_del_cursor_timer(info);
2103        else
2104                fbcon_add_cursor_timer(info);
2105
2106        set_blitting_type(vc, info);
2107        ops->cursor_reset = 1;
2108
2109        if (ops->rotate_font && ops->rotate_font(info, vc)) {
2110                ops->rotate = FB_ROTATE_UR;
2111                set_blitting_type(vc, info);
2112        }
2113
2114        vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
2115        vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
2116
2117        if (vc->vc_font.charcount > 256)
2118                vc->vc_complement_mask <<= 1;
2119
2120        updatescrollmode(p, info, vc);
2121
2122        switch (p->scrollmode) {
2123        case SCROLL_WRAP_MOVE:
2124                scrollback_phys_max = p->vrows - vc->vc_rows;
2125                break;
2126        case SCROLL_PAN_MOVE:
2127        case SCROLL_PAN_REDRAW:
2128                scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
2129                if (scrollback_phys_max < 0)
2130                        scrollback_phys_max = 0;
2131                break;
2132        default:
2133                scrollback_phys_max = 0;
2134                break;
2135        }
2136
2137        scrollback_max = 0;
2138        scrollback_current = 0;
2139
2140        if (!fbcon_is_inactive(vc, info)) {
2141            ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2142            ops->update_start(info);
2143        }
2144
2145        fbcon_set_palette(vc, color_table);     
2146        fbcon_clear_margins(vc, 0);
2147
2148        if (logo_shown == FBCON_LOGO_DRAW) {
2149
2150                logo_shown = fg_console;
2151                /* This is protected above by initmem_freed */
2152                fb_show_logo(info, ops->rotate);
2153                update_region(vc,
2154                              vc->vc_origin + vc->vc_size_row * vc->vc_top,
2155                              vc->vc_size_row * (vc->vc_bottom -
2156                                                 vc->vc_top) / 2);
2157                return 0;
2158        }
2159        return 1;
2160}
2161
2162static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
2163                                int blank)
2164{
2165        if (blank) {
2166                unsigned short charmask = vc->vc_hi_font_mask ?
2167                        0x1ff : 0xff;
2168                unsigned short oldc;
2169
2170                oldc = vc->vc_video_erase_char;
2171                vc->vc_video_erase_char &= charmask;
2172                fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols);
2173                vc->vc_video_erase_char = oldc;
2174        }
2175}
2176
2177static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
2178{
2179        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2180        struct fbcon_ops *ops = info->fbcon_par;
2181
2182        if (mode_switch) {
2183                struct fb_var_screeninfo var = info->var;
2184
2185                ops->graphics = 1;
2186
2187                if (!blank) {
2188                        var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
2189                                FB_ACTIVATE_KD_TEXT;
2190                        fb_set_var(info, &var);
2191                        ops->graphics = 0;
2192                        ops->var = info->var;
2193                }
2194        }
2195
2196        if (!fbcon_is_inactive(vc, info)) {
2197                if (ops->blank_state != blank) {
2198                        ops->blank_state = blank;
2199                        fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
2200                        ops->cursor_flash = (!blank);
2201
2202                        if (fb_blank(info, blank))
2203                                fbcon_generic_blank(vc, info, blank);
2204                }
2205
2206                if (!blank)
2207                        update_screen(vc);
2208        }
2209
2210        if (mode_switch || fbcon_is_inactive(vc, info) ||
2211            ops->blank_state != FB_BLANK_UNBLANK)
2212                fbcon_del_cursor_timer(info);
2213        else
2214                fbcon_add_cursor_timer(info);
2215
2216        return 0;
2217}
2218
2219static int fbcon_debug_enter(struct vc_data *vc)
2220{
2221        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2222        struct fbcon_ops *ops = info->fbcon_par;
2223
2224        ops->save_graphics = ops->graphics;
2225        ops->graphics = 0;
2226        if (info->fbops->fb_debug_enter)
2227                info->fbops->fb_debug_enter(info);
2228        fbcon_set_palette(vc, color_table);
2229        return 0;
2230}
2231
2232static int fbcon_debug_leave(struct vc_data *vc)
2233{
2234        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2235        struct fbcon_ops *ops = info->fbcon_par;
2236
2237        ops->graphics = ops->save_graphics;
2238        if (info->fbops->fb_debug_leave)
2239                info->fbops->fb_debug_leave(info);
2240        return 0;
2241}
2242
2243static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
2244{
2245        u8 *fontdata = vc->vc_font.data;
2246        u8 *data = font->data;
2247        int i, j;
2248
2249        font->width = vc->vc_font.width;
2250        font->height = vc->vc_font.height;
2251        font->charcount = vc->vc_hi_font_mask ? 512 : 256;
2252        if (!font->data)
2253                return 0;
2254
2255        if (font->width <= 8) {
2256                j = vc->vc_font.height;
2257                if (font->charcount * j > FNTSIZE(fontdata))
2258                        return -EINVAL;
2259
2260                for (i = 0; i < font->charcount; i++) {
2261                        memcpy(data, fontdata, j);
2262                        memset(data + j, 0, 32 - j);
2263                        data += 32;
2264                        fontdata += j;
2265                }
2266        } else if (font->width <= 16) {
2267                j = vc->vc_font.height * 2;
2268                if (font->charcount * j > FNTSIZE(fontdata))
2269                        return -EINVAL;
2270
2271                for (i = 0; i < font->charcount; i++) {
2272                        memcpy(data, fontdata, j);
2273                        memset(data + j, 0, 64 - j);
2274                        data += 64;
2275                        fontdata += j;
2276                }
2277        } else if (font->width <= 24) {
2278                if (font->charcount * (vc->vc_font.height * sizeof(u32)) > FNTSIZE(fontdata))
2279                        return -EINVAL;
2280
2281                for (i = 0; i < font->charcount; i++) {
2282                        for (j = 0; j < vc->vc_font.height; j++) {
2283                                *data++ = fontdata[0];
2284                                *data++ = fontdata[1];
2285                                *data++ = fontdata[2];
2286                                fontdata += sizeof(u32);
2287                        }
2288                        memset(data, 0, 3 * (32 - j));
2289                        data += 3 * (32 - j);
2290                }
2291        } else {
2292                j = vc->vc_font.height * 4;
2293                if (font->charcount * j > FNTSIZE(fontdata))
2294                        return -EINVAL;
2295
2296                for (i = 0; i < font->charcount; i++) {
2297                        memcpy(data, fontdata, j);
2298                        memset(data + j, 0, 128 - j);
2299                        data += 128;
2300                        fontdata += j;
2301                }
2302        }
2303        return 0;
2304}
2305
2306/* set/clear vc_hi_font_mask and update vc attrs accordingly */
2307static void set_vc_hi_font(struct vc_data *vc, bool set)
2308{
2309        if (!set) {
2310                vc->vc_hi_font_mask = 0;
2311                if (vc->vc_can_do_color) {
2312                        vc->vc_complement_mask >>= 1;
2313                        vc->vc_s_complement_mask >>= 1;
2314                }
2315                        
2316                /* ++Edmund: reorder the attribute bits */
2317                if (vc->vc_can_do_color) {
2318                        unsigned short *cp =
2319                            (unsigned short *) vc->vc_origin;
2320                        int count = vc->vc_screenbuf_size / 2;
2321                        unsigned short c;
2322                        for (; count > 0; count--, cp++) {
2323                                c = scr_readw(cp);
2324                                scr_writew(((c & 0xfe00) >> 1) |
2325                                           (c & 0xff), cp);
2326                        }
2327                        c = vc->vc_video_erase_char;
2328                        vc->vc_video_erase_char =
2329                            ((c & 0xfe00) >> 1) | (c & 0xff);
2330                        vc->vc_attr >>= 1;
2331                }
2332        } else {
2333                vc->vc_hi_font_mask = 0x100;
2334                if (vc->vc_can_do_color) {
2335                        vc->vc_complement_mask <<= 1;
2336                        vc->vc_s_complement_mask <<= 1;
2337                }
2338                        
2339                /* ++Edmund: reorder the attribute bits */
2340                {
2341                        unsigned short *cp =
2342                            (unsigned short *) vc->vc_origin;
2343                        int count = vc->vc_screenbuf_size / 2;
2344                        unsigned short c;
2345                        for (; count > 0; count--, cp++) {
2346                                unsigned short newc;
2347                                c = scr_readw(cp);
2348                                if (vc->vc_can_do_color)
2349                                        newc =
2350                                            ((c & 0xff00) << 1) | (c &
2351                                                                   0xff);
2352                                else
2353                                        newc = c & ~0x100;
2354                                scr_writew(newc, cp);
2355                        }
2356                        c = vc->vc_video_erase_char;
2357                        if (vc->vc_can_do_color) {
2358                                vc->vc_video_erase_char =
2359                                    ((c & 0xff00) << 1) | (c & 0xff);
2360                                vc->vc_attr <<= 1;
2361                        } else
2362                                vc->vc_video_erase_char = c & ~0x100;
2363                }
2364        }
2365}
2366
2367static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
2368                             const u8 * data, int userfont)
2369{
2370        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2371        struct fbcon_ops *ops = info->fbcon_par;
2372        struct fbcon_display *p = &fb_display[vc->vc_num];
2373        int resize;
2374        char *old_data = NULL;
2375
2376        resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
2377        if (p->userfont)
2378                old_data = vc->vc_font.data;
2379        vc->vc_font.data = (void *)(p->fontdata = data);
2380        if ((p->userfont = userfont))
2381                REFCOUNT(data)++;
2382        vc->vc_font.width = w;
2383        vc->vc_font.height = h;
2384        vc->vc_font.charcount = charcount;
2385        if (vc->vc_hi_font_mask && charcount == 256)
2386                set_vc_hi_font(vc, false);
2387        else if (!vc->vc_hi_font_mask && charcount == 512)
2388                set_vc_hi_font(vc, true);
2389
2390        if (resize) {
2391                int cols, rows;
2392
2393                cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2394                rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2395                cols /= w;
2396                rows /= h;
2397                vc_resize(vc, cols, rows);
2398        } else if (con_is_visible(vc)
2399                   && vc->vc_mode == KD_TEXT) {
2400                fbcon_clear_margins(vc, 0);
2401                update_screen(vc);
2402        }
2403
2404        if (old_data && (--REFCOUNT(old_data) == 0))
2405                kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
2406        return 0;
2407}
2408
2409/*
2410 *  User asked to set font; we are guaranteed that
2411 *      a) width and height are in range 1..32
2412 *      b) charcount does not exceed 512
2413 *  but lets not assume that, since someone might someday want to use larger
2414 *  fonts. And charcount of 512 is small for unicode support.
2415 *
2416 *  However, user space gives the font in 32 rows , regardless of
2417 *  actual font height. So a new API is needed if support for larger fonts
2418 *  is ever implemented.
2419 */
2420
2421static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
2422                          unsigned int flags)
2423{
2424        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2425        unsigned charcount = font->charcount;
2426        int w = font->width;
2427        int h = font->height;
2428        int size;
2429        int i, csum;
2430        u8 *new_data, *data = font->data;
2431        int pitch = PITCH(font->width);
2432
2433        /* Is there a reason why fbconsole couldn't handle any charcount >256?
2434         * If not this check should be changed to charcount < 256 */
2435        if (charcount != 256 && charcount != 512)
2436                return -EINVAL;
2437
2438        /* Make sure drawing engine can handle the font */
2439        if (!(info->pixmap.blit_x & (1 << (font->width - 1))) ||
2440            !(info->pixmap.blit_y & (1 << (font->height - 1))))
2441                return -EINVAL;
2442
2443        /* Make sure driver can handle the font length */
2444        if (fbcon_invalid_charcount(info, charcount))
2445                return -EINVAL;
2446
2447        size = CALC_FONTSZ(h, pitch, charcount);
2448
2449        new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
2450
2451        if (!new_data)
2452                return -ENOMEM;
2453
2454        memset(new_data, 0, FONT_EXTRA_WORDS * sizeof(int));
2455
2456        new_data += FONT_EXTRA_WORDS * sizeof(int);
2457        FNTSIZE(new_data) = size;
2458        REFCOUNT(new_data) = 0; /* usage counter */
2459        for (i=0; i< charcount; i++) {
2460                memcpy(new_data + i*h*pitch, data +  i*32*pitch, h*pitch);
2461        }
2462
2463        /* Since linux has a nice crc32 function use it for counting font
2464         * checksums. */
2465        csum = crc32(0, new_data, size);
2466
2467        FNTSUM(new_data) = csum;
2468        /* Check if the same font is on some other console already */
2469        for (i = first_fb_vc; i <= last_fb_vc; i++) {
2470                struct vc_data *tmp = vc_cons[i].d;
2471                
2472                if (fb_display[i].userfont &&
2473                    fb_display[i].fontdata &&
2474                    FNTSUM(fb_display[i].fontdata) == csum &&
2475                    FNTSIZE(fb_display[i].fontdata) == size &&
2476                    tmp->vc_font.width == w &&
2477                    !memcmp(fb_display[i].fontdata, new_data, size)) {
2478                        kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
2479                        new_data = (u8 *)fb_display[i].fontdata;
2480                        break;
2481                }
2482        }
2483        return fbcon_do_set_font(vc, font->width, font->height, charcount, new_data, 1);
2484}
2485
2486static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)
2487{
2488        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2489        const struct font_desc *f;
2490
2491        if (!name)
2492                f = get_default_font(info->var.xres, info->var.yres,
2493                                     info->pixmap.blit_x, info->pixmap.blit_y);
2494        else if (!(f = find_font(name)))
2495                return -ENOENT;
2496
2497        font->width = f->width;
2498        font->height = f->height;
2499        return fbcon_do_set_font(vc, f->width, f->height, f->charcount, f->data, 0);
2500}
2501
2502static u16 palette_red[16];
2503static u16 palette_green[16];
2504static u16 palette_blue[16];
2505
2506static struct fb_cmap palette_cmap = {
2507        0, 16, palette_red, palette_green, palette_blue, NULL
2508};
2509
2510static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
2511{
2512        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2513        int i, j, k, depth;
2514        u8 val;
2515
2516        if (fbcon_is_inactive(vc, info))
2517                return;
2518
2519        if (!con_is_visible(vc))
2520                return;
2521
2522        depth = fb_get_color_depth(&info->var, &info->fix);
2523        if (depth > 3) {
2524                for (i = j = 0; i < 16; i++) {
2525                        k = table[i];
2526                        val = vc->vc_palette[j++];
2527                        palette_red[k] = (val << 8) | val;
2528                        val = vc->vc_palette[j++];
2529                        palette_green[k] = (val << 8) | val;
2530                        val = vc->vc_palette[j++];
2531                        palette_blue[k] = (val << 8) | val;
2532                }
2533                palette_cmap.len = 16;
2534                palette_cmap.start = 0;
2535        /*
2536         * If framebuffer is capable of less than 16 colors,
2537         * use default palette of fbcon.
2538         */
2539        } else
2540                fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
2541
2542        fb_set_cmap(&palette_cmap, info);
2543}
2544
2545static u16 *fbcon_screen_pos(const struct vc_data *vc, int offset)
2546{
2547        return (u16 *) (vc->vc_origin + offset);
2548}
2549
2550static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2551                                 int *px, int *py)
2552{
2553        unsigned long ret;
2554        int x, y;
2555
2556        if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2557                unsigned long offset = (pos - vc->vc_origin) / 2;
2558
2559                x = offset % vc->vc_cols;
2560                y = offset / vc->vc_cols;
2561                ret = pos + (vc->vc_cols - x) * 2;
2562        } else {
2563                /* Should not happen */
2564                x = y = 0;
2565                ret = vc->vc_origin;
2566        }
2567        if (px)
2568                *px = x;
2569        if (py)
2570                *py = y;
2571        return ret;
2572}
2573
2574/* As we might be inside of softback, we may work with non-contiguous buffer,
2575   that's why we have to use a separate routine. */
2576static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2577{
2578        while (cnt--) {
2579                u16 a = scr_readw(p);
2580                if (!vc->vc_can_do_color)
2581                        a ^= 0x0800;
2582                else if (vc->vc_hi_font_mask == 0x100)
2583                        a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2584                            (((a) & 0x0e00) << 4);
2585                else
2586                        a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2587                            (((a) & 0x0700) << 4);
2588                scr_writew(a, p++);
2589        }
2590}
2591
2592void fbcon_suspended(struct fb_info *info)
2593{
2594        struct vc_data *vc = NULL;
2595        struct fbcon_ops *ops = info->fbcon_par;
2596
2597        if (!ops || ops->currcon < 0)
2598                return;
2599        vc = vc_cons[ops->currcon].d;
2600
2601        /* Clear cursor, restore saved data */
2602        fbcon_cursor(vc, CM_ERASE);
2603}
2604
2605void fbcon_resumed(struct fb_info *info)
2606{
2607        struct vc_data *vc;
2608        struct fbcon_ops *ops = info->fbcon_par;
2609
2610        if (!ops || ops->currcon < 0)
2611                return;
2612        vc = vc_cons[ops->currcon].d;
2613
2614        update_screen(vc);
2615}
2616
2617static void fbcon_modechanged(struct fb_info *info)
2618{
2619        struct fbcon_ops *ops = info->fbcon_par;
2620        struct vc_data *vc;
2621        struct fbcon_display *p;
2622        int rows, cols;
2623
2624        if (!ops || ops->currcon < 0)
2625                return;
2626        vc = vc_cons[ops->currcon].d;
2627        if (vc->vc_mode != KD_TEXT ||
2628            registered_fb[con2fb_map[ops->currcon]] != info)
2629                return;
2630
2631        p = &fb_display[vc->vc_num];
2632        set_blitting_type(vc, info);
2633
2634        if (con_is_visible(vc)) {
2635                var_to_display(p, &info->var, info);
2636                cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2637                rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2638                cols /= vc->vc_font.width;
2639                rows /= vc->vc_font.height;
2640                vc_resize(vc, cols, rows);
2641                updatescrollmode(p, info, vc);
2642                scrollback_max = 0;
2643                scrollback_current = 0;
2644
2645                if (!fbcon_is_inactive(vc, info)) {
2646                    ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2647                    ops->update_start(info);
2648                }
2649
2650                fbcon_set_palette(vc, color_table);
2651                update_screen(vc);
2652        }
2653}
2654
2655static void fbcon_set_all_vcs(struct fb_info *info)
2656{
2657        struct fbcon_ops *ops = info->fbcon_par;
2658        struct vc_data *vc;
2659        struct fbcon_display *p;
2660        int i, rows, cols, fg = -1;
2661
2662        if (!ops || ops->currcon < 0)
2663                return;
2664
2665        for (i = first_fb_vc; i <= last_fb_vc; i++) {
2666                vc = vc_cons[i].d;
2667                if (!vc || vc->vc_mode != KD_TEXT ||
2668                    registered_fb[con2fb_map[i]] != info)
2669                        continue;
2670
2671                if (con_is_visible(vc)) {
2672                        fg = i;
2673                        continue;
2674                }
2675
2676                p = &fb_display[vc->vc_num];
2677                set_blitting_type(vc, info);
2678                var_to_display(p, &info->var, info);
2679                cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2680                rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2681                cols /= vc->vc_font.width;
2682                rows /= vc->vc_font.height;
2683                vc_resize(vc, cols, rows);
2684        }
2685
2686        if (fg != -1)
2687                fbcon_modechanged(info);
2688}
2689
2690
2691void fbcon_update_vcs(struct fb_info *info, bool all)
2692{
2693        if (all)
2694                fbcon_set_all_vcs(info);
2695        else
2696                fbcon_modechanged(info);
2697}
2698EXPORT_SYMBOL(fbcon_update_vcs);
2699
2700int fbcon_mode_deleted(struct fb_info *info,
2701                       struct fb_videomode *mode)
2702{
2703        struct fb_info *fb_info;
2704        struct fbcon_display *p;
2705        int i, j, found = 0;
2706
2707        /* before deletion, ensure that mode is not in use */
2708        for (i = first_fb_vc; i <= last_fb_vc; i++) {
2709                j = con2fb_map[i];
2710                if (j == -1)
2711                        continue;
2712                fb_info = registered_fb[j];
2713                if (fb_info != info)
2714                        continue;
2715                p = &fb_display[i];
2716                if (!p || !p->mode)
2717                        continue;
2718                if (fb_mode_is_equal(p->mode, mode)) {
2719                        found = 1;
2720                        break;
2721                }
2722        }
2723        return found;
2724}
2725
2726#ifdef CONFIG_VT_HW_CONSOLE_BINDING
2727static void fbcon_unbind(void)
2728{
2729        int ret;
2730
2731        ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
2732                                fbcon_is_default);
2733
2734        if (!ret)
2735                fbcon_has_console_bind = 0;
2736}
2737#else
2738static inline void fbcon_unbind(void) {}
2739#endif /* CONFIG_VT_HW_CONSOLE_BINDING */
2740
2741/* called with console_lock held */
2742void fbcon_fb_unbind(struct fb_info *info)
2743{
2744        int i, new_idx = -1, ret = 0;
2745        int idx = info->node;
2746
2747        WARN_CONSOLE_UNLOCKED();
2748
2749        if (!fbcon_has_console_bind)
2750                return;
2751
2752        for (i = first_fb_vc; i <= last_fb_vc; i++) {
2753                if (con2fb_map[i] != idx &&
2754                    con2fb_map[i] != -1) {
2755                        new_idx = con2fb_map[i];
2756                        break;
2757                }
2758        }
2759
2760        if (new_idx != -1) {
2761                for (i = first_fb_vc; i <= last_fb_vc; i++) {
2762                        if (con2fb_map[i] == idx)
2763                                set_con2fb_map(i, new_idx, 0);
2764                }
2765        } else {
2766                struct fb_info *info = registered_fb[idx];
2767
2768                /* This is sort of like set_con2fb_map, except it maps
2769                 * the consoles to no device and then releases the
2770                 * oldinfo to free memory and cancel the cursor blink
2771                 * timer. I can imagine this just becoming part of
2772                 * set_con2fb_map where new_idx is -1
2773                 */
2774                for (i = first_fb_vc; i <= last_fb_vc; i++) {
2775                        if (con2fb_map[i] == idx) {
2776                                con2fb_map[i] = -1;
2777                                if (!search_fb_in_map(idx)) {
2778                                        ret = con2fb_release_oldinfo(vc_cons[i].d,
2779                                                                     info, NULL, i,
2780                                                                     idx, 0);
2781                                        if (ret) {
2782                                                con2fb_map[i] = idx;
2783                                                return;
2784                                        }
2785                                }
2786                        }
2787                }
2788                fbcon_unbind();
2789        }
2790}
2791
2792/* called with console_lock held */
2793void fbcon_fb_unregistered(struct fb_info *info)
2794{
2795        int i, idx;
2796
2797        WARN_CONSOLE_UNLOCKED();
2798
2799        if (deferred_takeover)
2800                return;
2801
2802        idx = info->node;
2803        for (i = first_fb_vc; i <= last_fb_vc; i++) {
2804                if (con2fb_map[i] == idx)
2805                        con2fb_map[i] = -1;
2806        }
2807
2808        if (idx == info_idx) {
2809                info_idx = -1;
2810
2811                for_each_registered_fb(i) {
2812                        info_idx = i;
2813                        break;
2814                }
2815        }
2816
2817        if (info_idx != -1) {
2818                for (i = first_fb_vc; i <= last_fb_vc; i++) {
2819                        if (con2fb_map[i] == -1)
2820                                con2fb_map[i] = info_idx;
2821                }
2822        }
2823
2824        if (primary_device == idx)
2825                primary_device = -1;
2826
2827        if (!num_registered_fb)
2828                do_unregister_con_driver(&fb_con);
2829}
2830
2831void fbcon_remap_all(struct fb_info *info)
2832{
2833        int i, idx = info->node;
2834
2835        console_lock();
2836        if (deferred_takeover) {
2837                for (i = first_fb_vc; i <= last_fb_vc; i++)
2838                        con2fb_map_boot[i] = idx;
2839                fbcon_map_override();
2840                console_unlock();
2841                return;
2842        }
2843
2844        for (i = first_fb_vc; i <= last_fb_vc; i++)
2845                set_con2fb_map(i, idx, 0);
2846
2847        if (con_is_bound(&fb_con)) {
2848                printk(KERN_INFO "fbcon: Remapping primary device, "
2849                       "fb%i, to tty %i-%i\n", idx,
2850                       first_fb_vc + 1, last_fb_vc + 1);
2851                info_idx = idx;
2852        }
2853        console_unlock();
2854}
2855
2856#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
2857static void fbcon_select_primary(struct fb_info *info)
2858{
2859        if (!map_override && primary_device == -1 &&
2860            fb_is_primary_device(info)) {
2861                int i;
2862
2863                printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n",
2864                       info->fix.id, info->node);
2865                primary_device = info->node;
2866
2867                for (i = first_fb_vc; i <= last_fb_vc; i++)
2868                        con2fb_map_boot[i] = primary_device;
2869
2870                if (con_is_bound(&fb_con)) {
2871                        printk(KERN_INFO "fbcon: Remapping primary device, "
2872                               "fb%i, to tty %i-%i\n", info->node,
2873                               first_fb_vc + 1, last_fb_vc + 1);
2874                        info_idx = primary_device;
2875                }
2876        }
2877
2878}
2879#else
2880static inline void fbcon_select_primary(struct fb_info *info)
2881{
2882        return;
2883}
2884#endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
2885
2886/* called with console_lock held */
2887int fbcon_fb_registered(struct fb_info *info)
2888{
2889        int ret = 0, i, idx;
2890
2891        WARN_CONSOLE_UNLOCKED();
2892
2893        idx = info->node;
2894        fbcon_select_primary(info);
2895
2896        if (deferred_takeover) {
2897                pr_info("fbcon: Deferring console take-over\n");
2898                return 0;
2899        }
2900
2901        if (info_idx == -1) {
2902                for (i = first_fb_vc; i <= last_fb_vc; i++) {
2903                        if (con2fb_map_boot[i] == idx) {
2904                                info_idx = idx;
2905                                break;
2906                        }
2907                }
2908
2909                if (info_idx != -1)
2910                        ret = do_fbcon_takeover(1);
2911        } else {
2912                for (i = first_fb_vc; i <= last_fb_vc; i++) {
2913                        if (con2fb_map_boot[i] == idx)
2914                                set_con2fb_map(i, idx, 0);
2915                }
2916        }
2917
2918        return ret;
2919}
2920
2921void fbcon_fb_blanked(struct fb_info *info, int blank)
2922{
2923        struct fbcon_ops *ops = info->fbcon_par;
2924        struct vc_data *vc;
2925
2926        if (!ops || ops->currcon < 0)
2927                return;
2928
2929        vc = vc_cons[ops->currcon].d;
2930        if (vc->vc_mode != KD_TEXT ||
2931                        registered_fb[con2fb_map[ops->currcon]] != info)
2932                return;
2933
2934        if (con_is_visible(vc)) {
2935                if (blank)
2936                        do_blank_screen(0);
2937                else
2938                        do_unblank_screen(0);
2939        }
2940        ops->blank_state = blank;
2941}
2942
2943void fbcon_new_modelist(struct fb_info *info)
2944{
2945        int i;
2946        struct vc_data *vc;
2947        struct fb_var_screeninfo var;
2948        const struct fb_videomode *mode;
2949
2950        for (i = first_fb_vc; i <= last_fb_vc; i++) {
2951                if (registered_fb[con2fb_map[i]] != info)
2952                        continue;
2953                if (!fb_display[i].mode)
2954                        continue;
2955                vc = vc_cons[i].d;
2956                display_to_var(&var, &fb_display[i]);
2957                mode = fb_find_nearest_mode(fb_display[i].mode,
2958                                            &info->modelist);
2959                fb_videomode_to_var(&var, mode);
2960                fbcon_set_disp(info, &var, vc->vc_num);
2961        }
2962}
2963
2964void fbcon_get_requirement(struct fb_info *info,
2965                           struct fb_blit_caps *caps)
2966{
2967        struct vc_data *vc;
2968
2969        if (caps->flags) {
2970                int i, charcnt;
2971
2972                for (i = first_fb_vc; i <= last_fb_vc; i++) {
2973                        vc = vc_cons[i].d;
2974                        if (vc && vc->vc_mode == KD_TEXT &&
2975                            info->node == con2fb_map[i]) {
2976                                caps->x |= 1 << (vc->vc_font.width - 1);
2977                                caps->y |= 1 << (vc->vc_font.height - 1);
2978                                charcnt = vc->vc_font.charcount;
2979                                if (caps->len < charcnt)
2980                                        caps->len = charcnt;
2981                        }
2982                }
2983        } else {
2984                vc = vc_cons[fg_console].d;
2985
2986                if (vc && vc->vc_mode == KD_TEXT &&
2987                    info->node == con2fb_map[fg_console]) {
2988                        caps->x = 1 << (vc->vc_font.width - 1);
2989                        caps->y = 1 << (vc->vc_font.height - 1);
2990                        caps->len = vc->vc_font.charcount;
2991                }
2992        }
2993}
2994
2995int fbcon_set_con2fb_map_ioctl(void __user *argp)
2996{
2997        struct fb_con2fbmap con2fb;
2998        int ret;
2999
3000        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3001                return -EFAULT;
3002        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3003                return -EINVAL;
3004        if (con2fb.framebuffer >= FB_MAX)
3005                return -EINVAL;
3006        if (!registered_fb[con2fb.framebuffer])
3007                request_module("fb%d", con2fb.framebuffer);
3008        if (!registered_fb[con2fb.framebuffer]) {
3009                return -EINVAL;
3010        }
3011
3012        console_lock();
3013        ret = set_con2fb_map(con2fb.console - 1,
3014                             con2fb.framebuffer, 1);
3015        console_unlock();
3016
3017        return ret;
3018}
3019
3020int fbcon_get_con2fb_map_ioctl(void __user *argp)
3021{
3022        struct fb_con2fbmap con2fb;
3023
3024        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3025                return -EFAULT;
3026        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3027                return -EINVAL;
3028
3029        console_lock();
3030        con2fb.framebuffer = con2fb_map[con2fb.console - 1];
3031        console_unlock();
3032
3033        return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
3034}
3035
3036/*
3037 *  The console `switch' structure for the frame buffer based console
3038 */
3039
3040static const struct consw fb_con = {
3041        .owner                  = THIS_MODULE,
3042        .con_startup            = fbcon_startup,
3043        .con_init               = fbcon_init,
3044        .con_deinit             = fbcon_deinit,
3045        .con_clear              = fbcon_clear,
3046        .con_putc               = fbcon_putc,
3047        .con_putcs              = fbcon_putcs,
3048        .con_cursor             = fbcon_cursor,
3049        .con_scroll             = fbcon_scroll,
3050        .con_switch             = fbcon_switch,
3051        .con_blank              = fbcon_blank,
3052        .con_font_set           = fbcon_set_font,
3053        .con_font_get           = fbcon_get_font,
3054        .con_font_default       = fbcon_set_def_font,
3055        .con_set_palette        = fbcon_set_palette,
3056        .con_invert_region      = fbcon_invert_region,
3057        .con_screen_pos         = fbcon_screen_pos,
3058        .con_getxy              = fbcon_getxy,
3059        .con_resize             = fbcon_resize,
3060        .con_debug_enter        = fbcon_debug_enter,
3061        .con_debug_leave        = fbcon_debug_leave,
3062};
3063
3064static ssize_t store_rotate(struct device *device,
3065                            struct device_attribute *attr, const char *buf,
3066                            size_t count)
3067{
3068        struct fb_info *info;
3069        int rotate, idx;
3070        char **last = NULL;
3071
3072        console_lock();
3073        idx = con2fb_map[fg_console];
3074
3075        if (idx == -1 || registered_fb[idx] == NULL)
3076                goto err;
3077
3078        info = registered_fb[idx];
3079        rotate = simple_strtoul(buf, last, 0);
3080        fbcon_rotate(info, rotate);
3081err:
3082        console_unlock();
3083        return count;
3084}
3085
3086static ssize_t store_rotate_all(struct device *device,
3087                                struct device_attribute *attr,const char *buf,
3088                                size_t count)
3089{
3090        struct fb_info *info;
3091        int rotate, idx;
3092        char **last = NULL;
3093
3094        console_lock();
3095        idx = con2fb_map[fg_console];
3096
3097        if (idx == -1 || registered_fb[idx] == NULL)
3098                goto err;
3099
3100        info = registered_fb[idx];
3101        rotate = simple_strtoul(buf, last, 0);
3102        fbcon_rotate_all(info, rotate);
3103err:
3104        console_unlock();
3105        return count;
3106}
3107
3108static ssize_t show_rotate(struct device *device,
3109                           struct device_attribute *attr,char *buf)
3110{
3111        struct fb_info *info;
3112        int rotate = 0, idx;
3113
3114        console_lock();
3115        idx = con2fb_map[fg_console];
3116
3117        if (idx == -1 || registered_fb[idx] == NULL)
3118                goto err;
3119
3120        info = registered_fb[idx];
3121        rotate = fbcon_get_rotate(info);
3122err:
3123        console_unlock();
3124        return snprintf(buf, PAGE_SIZE, "%d\n", rotate);
3125}
3126
3127static ssize_t show_cursor_blink(struct device *device,
3128                                 struct device_attribute *attr, char *buf)
3129{
3130        struct fb_info *info;
3131        struct fbcon_ops *ops;
3132        int idx, blink = -1;
3133
3134        console_lock();
3135        idx = con2fb_map[fg_console];
3136
3137        if (idx == -1 || registered_fb[idx] == NULL)
3138                goto err;
3139
3140        info = registered_fb[idx];
3141        ops = info->fbcon_par;
3142
3143        if (!ops)
3144                goto err;
3145
3146        blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0;
3147err:
3148        console_unlock();
3149        return snprintf(buf, PAGE_SIZE, "%d\n", blink);
3150}
3151
3152static ssize_t store_cursor_blink(struct device *device,
3153                                  struct device_attribute *attr,
3154                                  const char *buf, size_t count)
3155{
3156        struct fb_info *info;
3157        int blink, idx;
3158        char **last = NULL;
3159
3160        console_lock();
3161        idx = con2fb_map[fg_console];
3162
3163        if (idx == -1 || registered_fb[idx] == NULL)
3164                goto err;
3165
3166        info = registered_fb[idx];
3167
3168        if (!info->fbcon_par)
3169                goto err;
3170
3171        blink = simple_strtoul(buf, last, 0);
3172
3173        if (blink) {
3174                fbcon_cursor_noblink = 0;
3175                fbcon_add_cursor_timer(info);
3176        } else {
3177                fbcon_cursor_noblink = 1;
3178                fbcon_del_cursor_timer(info);
3179        }
3180
3181err:
3182        console_unlock();
3183        return count;
3184}
3185
3186static struct device_attribute device_attrs[] = {
3187        __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
3188        __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
3189        __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink,
3190               store_cursor_blink),
3191};
3192
3193static int fbcon_init_device(void)
3194{
3195        int i, error = 0;
3196
3197        fbcon_has_sysfs = 1;
3198
3199        for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
3200                error = device_create_file(fbcon_device, &device_attrs[i]);
3201
3202                if (error)
3203                        break;
3204        }
3205
3206        if (error) {
3207                while (--i >= 0)
3208                        device_remove_file(fbcon_device, &device_attrs[i]);
3209
3210                fbcon_has_sysfs = 0;
3211        }
3212
3213        return 0;
3214}
3215
3216#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3217static void fbcon_register_existing_fbs(struct work_struct *work)
3218{
3219        int i;
3220
3221        console_lock();
3222
3223        for_each_registered_fb(i)
3224                fbcon_fb_registered(registered_fb[i]);
3225
3226        console_unlock();
3227}
3228
3229static struct notifier_block fbcon_output_nb;
3230static DECLARE_WORK(fbcon_deferred_takeover_work, fbcon_register_existing_fbs);
3231
3232static int fbcon_output_notifier(struct notifier_block *nb,
3233                                 unsigned long action, void *data)
3234{
3235        WARN_CONSOLE_UNLOCKED();
3236
3237        pr_info("fbcon: Taking over console\n");
3238
3239        dummycon_unregister_output_notifier(&fbcon_output_nb);
3240        deferred_takeover = false;
3241        logo_shown = FBCON_LOGO_DONTSHOW;
3242
3243        /* We may get called in atomic context */
3244        schedule_work(&fbcon_deferred_takeover_work);
3245
3246        return NOTIFY_OK;
3247}
3248#endif
3249
3250static void fbcon_start(void)
3251{
3252        WARN_CONSOLE_UNLOCKED();
3253
3254#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3255        if (conswitchp != &dummy_con)
3256                deferred_takeover = false;
3257
3258        if (deferred_takeover) {
3259                fbcon_output_nb.notifier_call = fbcon_output_notifier;
3260                dummycon_register_output_notifier(&fbcon_output_nb);
3261                return;
3262        }
3263#endif
3264
3265        if (num_registered_fb) {
3266                int i;
3267
3268                for_each_registered_fb(i) {
3269                        info_idx = i;
3270                        break;
3271                }
3272
3273                do_fbcon_takeover(0);
3274        }
3275}
3276
3277static void fbcon_exit(void)
3278{
3279        struct fb_info *info;
3280        int i, j, mapped;
3281
3282#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3283        if (deferred_takeover) {
3284                dummycon_unregister_output_notifier(&fbcon_output_nb);
3285                deferred_takeover = false;
3286        }
3287#endif
3288
3289        for_each_registered_fb(i) {
3290                int pending = 0;
3291
3292                mapped = 0;
3293                info = registered_fb[i];
3294
3295                if (info->queue.func)
3296                        pending = cancel_work_sync(&info->queue);
3297                pr_debug("fbcon: %s pending work\n", (pending ? "canceled" : "no"));
3298
3299                for (j = first_fb_vc; j <= last_fb_vc; j++) {
3300                        if (con2fb_map[j] == i) {
3301                                mapped = 1;
3302                                con2fb_map[j] = -1;
3303                        }
3304                }
3305
3306                if (mapped) {
3307                        if (info->fbops->fb_release)
3308                                info->fbops->fb_release(info, 0);
3309                        module_put(info->fbops->owner);
3310
3311                        if (info->fbcon_par) {
3312                                struct fbcon_ops *ops = info->fbcon_par;
3313
3314                                fbcon_del_cursor_timer(info);
3315                                kfree(ops->cursor_src);
3316                                kfree(ops->cursor_state.mask);
3317                                kfree(info->fbcon_par);
3318                                info->fbcon_par = NULL;
3319                        }
3320
3321                        if (info->queue.func == fb_flashcursor)
3322                                info->queue.func = NULL;
3323                }
3324        }
3325}
3326
3327void __init fb_console_init(void)
3328{
3329        int i;
3330
3331        console_lock();
3332        fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
3333                                     "fbcon");
3334
3335        if (IS_ERR(fbcon_device)) {
3336                printk(KERN_WARNING "Unable to create device "
3337                       "for fbcon; errno = %ld\n",
3338                       PTR_ERR(fbcon_device));
3339                fbcon_device = NULL;
3340        } else
3341                fbcon_init_device();
3342
3343        for (i = 0; i < MAX_NR_CONSOLES; i++)
3344                con2fb_map[i] = -1;
3345
3346        fbcon_start();
3347        console_unlock();
3348}
3349
3350#ifdef MODULE
3351
3352static void __exit fbcon_deinit_device(void)
3353{
3354        int i;
3355
3356        if (fbcon_has_sysfs) {
3357                for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
3358                        device_remove_file(fbcon_device, &device_attrs[i]);
3359
3360                fbcon_has_sysfs = 0;
3361        }
3362}
3363
3364void __exit fb_console_exit(void)
3365{
3366        console_lock();
3367        fbcon_deinit_device();
3368        device_destroy(fb_class, MKDEV(0, 0));
3369        fbcon_exit();
3370        do_unregister_con_driver(&fb_con);
3371        console_unlock();
3372}       
3373#endif
3374