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