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