Root/drivers/video/console/fbcon.c

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

Archive Download this file



interactive