[PATCH] VT binding: Add binding/unbinding support for the VT console
[deliverable/linux.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
61#include <linux/config.h>
62#include <linux/module.h>
63#include <linux/types.h>
64#include <linux/sched.h>
65#include <linux/fs.h>
66#include <linux/kernel.h>
67#include <linux/delay.h> /* MSch: for IRQ probe */
68#include <linux/tty.h>
69#include <linux/console.h>
70#include <linux/string.h>
71#include <linux/kd.h>
72#include <linux/slab.h>
73#include <linux/fb.h>
74#include <linux/vt_kern.h>
75#include <linux/selection.h>
76#include <linux/font.h>
77#include <linux/smp.h>
78#include <linux/init.h>
79#include <linux/interrupt.h>
80#include <linux/crc32.h> /* For counting font checksums */
81#include <asm/irq.h>
82#include <asm/system.h>
83#include <asm/uaccess.h>
84#ifdef CONFIG_ATARI
85#include <asm/atariints.h>
86#endif
87#ifdef CONFIG_MAC
88#include <asm/macints.h>
89#endif
90#if defined(__mc68000__) || defined(CONFIG_APUS)
91#include <asm/machdep.h>
92#include <asm/setup.h>
93#endif
94
95#include "fbcon.h"
96
97#ifdef FBCONDEBUG
98# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
99#else
100# define DPRINTK(fmt, args...)
101#endif
102
103enum {
104 FBCON_LOGO_CANSHOW = -1, /* the logo can be shown */
105 FBCON_LOGO_DRAW = -2, /* draw the logo to a console */
106 FBCON_LOGO_DONTSHOW = -3 /* do not show the logo */
107};
108
ab767201 109static struct display fb_display[MAX_NR_CONSOLES];
e4fc2761 110
1da177e4
LT
111static signed char con2fb_map[MAX_NR_CONSOLES];
112static signed char con2fb_map_boot[MAX_NR_CONSOLES];
113static int logo_height;
114static int logo_lines;
115/* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
116 enums. */
117static int logo_shown = FBCON_LOGO_CANSHOW;
118/* Software scrollback */
119static int fbcon_softback_size = 32768;
120static unsigned long softback_buf, softback_curr;
121static unsigned long softback_in;
122static unsigned long softback_top, softback_end;
123static int softback_lines;
124/* console mappings */
125static int first_fb_vc;
126static int last_fb_vc = MAX_NR_CONSOLES - 1;
127static int fbcon_is_default = 1;
128/* font data */
129static char fontname[40];
130
131/* current fb_info */
132static int info_idx = -1;
133
e4fc2761
AD
134/* console rotation */
135static int rotate;
136
1da177e4
LT
137static const struct consw fb_con;
138
139#define CM_SOFTBACK (8)
140
141#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
142
143static void fbcon_free_font(struct display *);
144static int fbcon_set_origin(struct vc_data *);
145
146#define CURSOR_DRAW_DELAY (1)
147
148/* # VBL ints between cursor state changes */
1da177e4
LT
149#define ATARI_CURSOR_BLINK_RATE (42)
150#define MAC_CURSOR_BLINK_RATE (32)
151#define DEFAULT_CURSOR_BLINK_RATE (20)
152
153static int vbl_cursor_cnt;
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,
190 struct vc_data *vc);
191static void fbcon_preset_disp(struct fb_info *info, struct fb_var_screeninfo *var,
192 int unit);
193static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
194 int line, int count, int dy);
a812c94b
AD
195static void fbcon_modechanged(struct fb_info *info);
196static void fbcon_set_all_vcs(struct fb_info *info);
5428b044
AD
197static void fbcon_start(void);
198static void fbcon_exit(void);
9a179176
AD
199static struct class_device *fbcon_class_device;
200
1da177e4
LT
201#ifdef CONFIG_MAC
202/*
203 * On the Macintoy, there may or may not be a working VBL int. We need to probe
204 */
205static int vbl_detected;
206
207static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
208{
209 vbl_detected++;
210 return IRQ_HANDLED;
211}
212#endif
213
dbcbfe1e 214#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
b73deed3 215static inline void fbcon_set_rotation(struct fb_info *info)
dbcbfe1e
AD
216{
217 struct fbcon_ops *ops = info->fbcon_par;
218
219 if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&
b73deed3
AD
220 ops->p->con_rotate < 4)
221 ops->rotate = ops->p->con_rotate;
dbcbfe1e
AD
222 else
223 ops->rotate = 0;
224}
a812c94b
AD
225
226static void fbcon_rotate(struct fb_info *info, u32 rotate)
227{
228 struct fbcon_ops *ops= info->fbcon_par;
229 struct fb_info *fb_info;
230
231 if (!ops || ops->currcon == -1)
232 return;
233
234 fb_info = registered_fb[con2fb_map[ops->currcon]];
235
236 if (info == fb_info) {
237 struct display *p = &fb_display[ops->currcon];
238
239 if (rotate < 4)
240 p->con_rotate = rotate;
241 else
242 p->con_rotate = 0;
243
244 fbcon_modechanged(info);
245 }
246}
247
248static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
249{
250 struct fbcon_ops *ops = info->fbcon_par;
251 struct vc_data *vc;
252 struct display *p;
253 int i;
254
255 if (!ops || ops->currcon < 0 || rotate > 3)
256 return;
257
258 for (i = 0; i < MAX_NR_CONSOLES; i++) {
259 vc = vc_cons[i].d;
260 if (!vc || vc->vc_mode != KD_TEXT ||
261 registered_fb[con2fb_map[i]] != info)
262 continue;
263
264 p = &fb_display[vc->vc_num];
265 p->con_rotate = rotate;
266 }
267
268 fbcon_set_all_vcs(info);
269}
dbcbfe1e 270#else
b73deed3 271static inline void fbcon_set_rotation(struct fb_info *info)
e4fc2761
AD
272{
273 struct fbcon_ops *ops = info->fbcon_par;
274
275 ops->rotate = FB_ROTATE_UR;
276}
a812c94b
AD
277
278static void fbcon_rotate(struct fb_info *info, u32 rotate)
279{
280 return;
281}
282
283static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
284{
285 return;
286}
dbcbfe1e 287#endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
e4fc2761 288
a812c94b
AD
289static int fbcon_get_rotate(struct fb_info *info)
290{
291 struct fbcon_ops *ops = info->fbcon_par;
292
293 return (ops) ? ops->rotate : 0;
294}
295
1da177e4
LT
296static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
297{
298 struct fbcon_ops *ops = info->fbcon_par;
299
300 return (info->state != FBINFO_STATE_RUNNING ||
301 vc->vc_mode != KD_TEXT || ops->graphics);
302}
303
304static inline int get_color(struct vc_data *vc, struct fb_info *info,
305 u16 c, int is_fg)
306{
b8c90945 307 int depth = fb_get_color_depth(&info->var, &info->fix);
1da177e4
LT
308 int color = 0;
309
310 if (console_blanked) {
311 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
312
313 c = vc->vc_video_erase_char & charmask;
314 }
315
316 if (depth != 1)
317 color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
318 : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
319
320 switch (depth) {
321 case 1:
322 {
b8c90945
AD
323 int col = ~(0xfff << (max(info->var.green.length,
324 max(info->var.red.length,
325 info->var.blue.length)))) & 0xff;
326
1da177e4 327 /* 0 or 1 */
b8c90945
AD
328 int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0;
329 int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col;
1da177e4
LT
330
331 if (console_blanked)
332 fg = bg;
333
334 color = (is_fg) ? fg : bg;
335 break;
336 }
337 case 2:
338 /*
339 * Scale down 16-colors to 4 colors. Default 4-color palette
2cc38ed1
AD
340 * is grayscale. However, simply dividing the values by 4
341 * will not work, as colors 1, 2 and 3 will be scaled-down
342 * to zero rendering them invisible. So empirically convert
343 * colors to a sane 4-level grayscale.
1da177e4 344 */
2cc38ed1
AD
345 switch (color) {
346 case 0:
347 color = 0; /* black */
348 break;
349 case 1 ... 6:
350 color = 2; /* white */
351 break;
352 case 7 ... 8:
353 color = 1; /* gray */
354 break;
355 default:
356 color = 3; /* intense white */
357 break;
358 }
359 break;
1da177e4
LT
360 case 3:
361 /*
362 * Last 8 entries of default 16-color palette is a more intense
363 * version of the first 8 (i.e., same chrominance, different
364 * luminance).
365 */
366 color &= 7;
367 break;
368 }
369
370
371 return color;
372}
373
4d9c5b6e
AD
374static void fbcon_update_softback(struct vc_data *vc)
375{
376 int l = fbcon_softback_size / vc->vc_size_row;
377
378 if (l > 5)
379 softback_end = softback_buf + l * vc->vc_size_row;
380 else
381 /* Smaller scrollback makes no sense, and 0 would screw
382 the operation totally */
383 softback_top = 0;
384}
385
1da177e4
LT
386static void fb_flashcursor(void *private)
387{
388 struct fb_info *info = private;
389 struct fbcon_ops *ops = info->fbcon_par;
390 struct display *p;
391 struct vc_data *vc = NULL;
392 int c;
393 int mode;
394
5428b044
AD
395 acquire_console_sem();
396 if (ops && ops->currcon != -1)
1da177e4
LT
397 vc = vc_cons[ops->currcon].d;
398
399 if (!vc || !CON_IS_VISIBLE(vc) ||
400 fbcon_is_inactive(vc, info) ||
dbd4f128 401 registered_fb[con2fb_map[vc->vc_num]] != info ||
5428b044
AD
402 vc_cons[ops->currcon].d->vc_deccm != 1) {
403 release_console_sem();
1da177e4 404 return;
5428b044
AD
405 }
406
1da177e4
LT
407 p = &fb_display[vc->vc_num];
408 c = scr_readw((u16 *) vc->vc_pos);
409 mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
410 CM_ERASE : CM_DRAW;
b73deed3 411 ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1),
1da177e4
LT
412 get_color(vc, info, c, 0));
413 release_console_sem();
414}
415
41359dca 416#if defined(CONFIG_ATARI) || defined(CONFIG_MAC)
1da177e4
LT
417static int cursor_blink_rate;
418static irqreturn_t fb_vbl_handler(int irq, void *dev_id, struct pt_regs *fp)
419{
420 struct fb_info *info = dev_id;
421
422 if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
423 schedule_work(&info->queue);
424 vbl_cursor_cnt = cursor_blink_rate;
425 }
426 return IRQ_HANDLED;
427}
428#endif
429
430static void cursor_timer_handler(unsigned long dev_addr)
431{
432 struct fb_info *info = (struct fb_info *) dev_addr;
433 struct fbcon_ops *ops = info->fbcon_par;
434
435 schedule_work(&info->queue);
436 mod_timer(&ops->cursor_timer, jiffies + HZ/5);
437}
438
88fb2c6e
AD
439static void fbcon_add_cursor_timer(struct fb_info *info)
440{
441 struct fbcon_ops *ops = info->fbcon_par;
442
443 if ((!info->queue.func || info->queue.func == fb_flashcursor) &&
444 !(ops->flags & FBCON_FLAGS_CURSOR_TIMER)) {
445 if (!info->queue.func)
446 INIT_WORK(&info->queue, fb_flashcursor, info);
447
448 init_timer(&ops->cursor_timer);
449 ops->cursor_timer.function = cursor_timer_handler;
450 ops->cursor_timer.expires = jiffies + HZ / 5;
451 ops->cursor_timer.data = (unsigned long ) info;
452 add_timer(&ops->cursor_timer);
453 ops->flags |= FBCON_FLAGS_CURSOR_TIMER;
454 }
455}
456
457static void fbcon_del_cursor_timer(struct fb_info *info)
458{
459 struct fbcon_ops *ops = info->fbcon_par;
460
461 if (info->queue.func == fb_flashcursor &&
462 ops->flags & FBCON_FLAGS_CURSOR_TIMER) {
463 del_timer_sync(&ops->cursor_timer);
464 ops->flags &= ~FBCON_FLAGS_CURSOR_TIMER;
465 }
466}
467
1da177e4
LT
468#ifndef MODULE
469static int __init fb_console_setup(char *this_opt)
470{
471 char *options;
472 int i, j;
473
474 if (!this_opt || !*this_opt)
9b41046c 475 return 1;
1da177e4
LT
476
477 while ((options = strsep(&this_opt, ",")) != NULL) {
478 if (!strncmp(options, "font:", 5))
479 strcpy(fontname, options + 5);
480
481 if (!strncmp(options, "scrollback:", 11)) {
482 options += 11;
483 if (*options) {
484 fbcon_softback_size = simple_strtoul(options, &options, 0);
485 if (*options == 'k' || *options == 'K') {
486 fbcon_softback_size *= 1024;
487 options++;
488 }
489 if (*options != ',')
9b41046c 490 return 1;
1da177e4
LT
491 options++;
492 } else
9b41046c 493 return 1;
1da177e4
LT
494 }
495
496 if (!strncmp(options, "map:", 4)) {
497 options += 4;
498 if (*options)
499 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
500 if (!options[j])
501 j = 0;
502 con2fb_map_boot[i] =
503 (options[j++]-'0') % FB_MAX;
504 }
9b41046c 505 return 1;
1da177e4
LT
506 }
507
508 if (!strncmp(options, "vc:", 3)) {
509 options += 3;
510 if (*options)
511 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
512 if (first_fb_vc < 0)
513 first_fb_vc = 0;
514 if (*options++ == '-')
515 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
516 fbcon_is_default = 0;
517 }
e4fc2761
AD
518
519 if (!strncmp(options, "rotate:", 7)) {
520 options += 7;
521 if (*options)
522 rotate = simple_strtoul(options, &options, 0);
523 if (rotate > 3)
524 rotate = 0;
525 }
1da177e4 526 }
9b41046c 527 return 1;
1da177e4
LT
528}
529
530__setup("fbcon=", fb_console_setup);
531#endif
532
533static int search_fb_in_map(int idx)
534{
535 int i, retval = 0;
536
537 for (i = 0; i < MAX_NR_CONSOLES; i++) {
538 if (con2fb_map[i] == idx)
539 retval = 1;
540 }
541 return retval;
542}
543
544static int search_for_mapped_con(void)
545{
546 int i, retval = 0;
547
548 for (i = 0; i < MAX_NR_CONSOLES; i++) {
549 if (con2fb_map[i] != -1)
550 retval = 1;
551 }
552 return retval;
553}
554
555static int fbcon_takeover(int show_logo)
556{
557 int err, i;
558
559 if (!num_registered_fb)
560 return -ENODEV;
561
562 if (!show_logo)
563 logo_shown = FBCON_LOGO_DONTSHOW;
564
565 for (i = first_fb_vc; i <= last_fb_vc; i++)
566 con2fb_map[i] = info_idx;
567
568 err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
569 fbcon_is_default);
570 if (err) {
571 for (i = first_fb_vc; i <= last_fb_vc; i++) {
572 con2fb_map[i] = -1;
573 }
574 info_idx = -1;
575 }
576
577 return err;
578}
579
580static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
581 int cols, int rows, int new_cols, int new_rows)
582{
583 /* Need to make room for the logo */
9c44e5f6 584 struct fbcon_ops *ops = info->fbcon_par;
1da177e4
LT
585 int cnt, erase = vc->vc_video_erase_char, step;
586 unsigned short *save = NULL, *r, *q;
587
588 /*
589 * remove underline attribute from erase character
590 * if black and white framebuffer.
591 */
b8c90945 592 if (fb_get_color_depth(&info->var, &info->fix) == 1)
1da177e4 593 erase &= ~0x400;
9c44e5f6 594 logo_height = fb_prepare_logo(info, ops->rotate);
1da177e4
LT
595 logo_lines = (logo_height + vc->vc_font.height - 1) /
596 vc->vc_font.height;
597 q = (unsigned short *) (vc->vc_origin +
598 vc->vc_size_row * rows);
599 step = logo_lines * cols;
600 for (r = q - logo_lines * cols; r < q; r++)
601 if (scr_readw(r) != vc->vc_video_erase_char)
602 break;
603 if (r != q && new_rows >= rows + logo_lines) {
604 save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL);
605 if (save) {
606 int i = cols < new_cols ? cols : new_cols;
607 scr_memsetw(save, erase, logo_lines * new_cols * 2);
608 r = q - step;
609 for (cnt = 0; cnt < logo_lines; cnt++, r += i)
610 scr_memcpyw(save + cnt * new_cols, r, 2 * i);
611 r = q;
612 }
613 }
614 if (r == q) {
615 /* We can scroll screen down */
616 r = q - step - cols;
617 for (cnt = rows - logo_lines; cnt > 0; cnt--) {
618 scr_memcpyw(r + step, r, vc->vc_size_row);
619 r -= cols;
620 }
621 if (!save) {
622 vc->vc_y += logo_lines;
623 vc->vc_pos += logo_lines * vc->vc_size_row;
624 }
625 }
626 scr_memsetw((unsigned short *) vc->vc_origin,
627 erase,
628 vc->vc_size_row * logo_lines);
629
630 if (CON_IS_VISIBLE(vc) && vc->vc_mode == KD_TEXT) {
631 fbcon_clear_margins(vc, 0);
632 update_screen(vc);
633 }
634
635 if (save) {
636 q = (unsigned short *) (vc->vc_origin +
637 vc->vc_size_row *
638 rows);
639 scr_memcpyw(q, save, logo_lines * new_cols * 2);
640 vc->vc_y += logo_lines;
641 vc->vc_pos += logo_lines * vc->vc_size_row;
642 kfree(save);
643 }
644
645 if (logo_lines > vc->vc_bottom) {
646 logo_shown = FBCON_LOGO_CANSHOW;
647 printk(KERN_INFO
648 "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
649 } else if (logo_shown != FBCON_LOGO_DONTSHOW) {
650 logo_shown = FBCON_LOGO_DRAW;
651 vc->vc_top = logo_lines;
652 }
653}
654
655#ifdef CONFIG_FB_TILEBLITTING
b73deed3 656static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
1da177e4
LT
657{
658 struct fbcon_ops *ops = info->fbcon_par;
659
b73deed3 660 ops->p = &fb_display[vc->vc_num];
ab767201 661
1da177e4 662 if ((info->flags & FBINFO_MISC_TILEBLITTING))
b73deed3 663 fbcon_set_tileops(vc, info);
e4fc2761 664 else {
b73deed3 665 fbcon_set_rotation(info);
1da177e4 666 fbcon_set_bitops(ops);
e4fc2761 667 }
1da177e4
LT
668}
669#else
b73deed3 670static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
1da177e4
LT
671{
672 struct fbcon_ops *ops = info->fbcon_par;
673
674 info->flags &= ~FBINFO_MISC_TILEBLITTING;
b73deed3
AD
675 ops->p = &fb_display[vc->vc_num];
676 fbcon_set_rotation(info);
1da177e4
LT
677 fbcon_set_bitops(ops);
678}
679#endif /* CONFIG_MISC_TILEBLITTING */
680
681
682static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info,
683 int unit, int oldidx)
684{
685 struct fbcon_ops *ops = NULL;
686 int err = 0;
687
688 if (!try_module_get(info->fbops->owner))
689 err = -ENODEV;
690
691 if (!err && info->fbops->fb_open &&
692 info->fbops->fb_open(info, 0))
693 err = -ENODEV;
694
695 if (!err) {
a39bc34e 696 ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
1da177e4
LT
697 if (!ops)
698 err = -ENOMEM;
699 }
700
701 if (!err) {
1da177e4 702 info->fbcon_par = ops;
b73deed3 703 set_blitting_type(vc, info);
1da177e4
LT
704 }
705
706 if (err) {
707 con2fb_map[unit] = oldidx;
708 module_put(info->fbops->owner);
709 }
710
711 return err;
712}
713
714static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
715 struct fb_info *newinfo, int unit,
716 int oldidx, int found)
717{
718 struct fbcon_ops *ops = oldinfo->fbcon_par;
719 int err = 0;
720
721 if (oldinfo->fbops->fb_release &&
722 oldinfo->fbops->fb_release(oldinfo, 0)) {
723 con2fb_map[unit] = oldidx;
724 if (!found && newinfo->fbops->fb_release)
725 newinfo->fbops->fb_release(newinfo, 0);
726 if (!found)
727 module_put(newinfo->fbops->owner);
728 err = -ENODEV;
729 }
730
731 if (!err) {
88fb2c6e 732 fbcon_del_cursor_timer(oldinfo);
1da177e4
LT
733 kfree(ops->cursor_state.mask);
734 kfree(ops->cursor_data);
e4fc2761 735 kfree(ops->fontbuffer);
1da177e4
LT
736 kfree(oldinfo->fbcon_par);
737 oldinfo->fbcon_par = NULL;
738 module_put(oldinfo->fbops->owner);
dd0314f7
AD
739 /*
740 If oldinfo and newinfo are driving the same hardware,
741 the fb_release() method of oldinfo may attempt to
742 restore the hardware state. This will leave the
743 newinfo in an undefined state. Thus, a call to
744 fb_set_par() may be needed for the newinfo.
745 */
746 if (newinfo->fbops->fb_set_par)
747 newinfo->fbops->fb_set_par(newinfo);
1da177e4
LT
748 }
749
750 return err;
751}
752
1da177e4
LT
753static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
754 int unit, int show_logo)
755{
756 struct fbcon_ops *ops = info->fbcon_par;
757
758 ops->currcon = fg_console;
759
760 if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT))
761 info->fbops->fb_set_par(info);
762
763 ops->flags |= FBCON_FLAGS_INIT;
764 ops->graphics = 0;
765
766 if (vc)
767 fbcon_set_disp(info, &info->var, vc);
768 else
769 fbcon_preset_disp(info, &info->var, unit);
770
771 if (show_logo) {
772 struct vc_data *fg_vc = vc_cons[fg_console].d;
773 struct fb_info *fg_info =
774 registered_fb[con2fb_map[fg_console]];
775
776 fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols,
777 fg_vc->vc_rows, fg_vc->vc_cols,
778 fg_vc->vc_rows);
779 }
780
781 update_screen(vc_cons[fg_console].d);
782}
783
784/**
785 * set_con2fb_map - map console to frame buffer device
786 * @unit: virtual console number to map
787 * @newidx: frame buffer index to map virtual console to
788 * @user: user request
789 *
790 * Maps a virtual console @unit to a frame buffer device
791 * @newidx.
792 */
793static int set_con2fb_map(int unit, int newidx, int user)
794{
795 struct vc_data *vc = vc_cons[unit].d;
796 int oldidx = con2fb_map[unit];
797 struct fb_info *info = registered_fb[newidx];
798 struct fb_info *oldinfo = NULL;
799 int found, err = 0;
800
801 if (oldidx == newidx)
802 return 0;
803
804 if (!info)
805 err = -EINVAL;
806
807 if (!err && !search_for_mapped_con()) {
808 info_idx = newidx;
809 return fbcon_takeover(0);
810 }
811
812 if (oldidx != -1)
813 oldinfo = registered_fb[oldidx];
814
815 found = search_fb_in_map(newidx);
816
817 acquire_console_sem();
818 con2fb_map[unit] = newidx;
819 if (!err && !found)
820 err = con2fb_acquire_newinfo(vc, info, unit, oldidx);
821
822
823 /*
824 * If old fb is not mapped to any of the consoles,
825 * fbcon should release it.
826 */
827 if (!err && oldinfo && !search_fb_in_map(oldidx))
828 err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx,
829 found);
830
831 if (!err) {
832 int show_logo = (fg_console == 0 && !user &&
833 logo_shown != FBCON_LOGO_DONTSHOW);
834
835 if (!found)
88fb2c6e 836 fbcon_add_cursor_timer(info);
1da177e4
LT
837 con2fb_map_boot[unit] = newidx;
838 con2fb_init_display(vc, info, unit, show_logo);
839 }
840
841 release_console_sem();
842 return err;
843}
844
845/*
846 * Low Level Operations
847 */
848/* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
849static int var_to_display(struct display *disp,
850 struct fb_var_screeninfo *var,
851 struct fb_info *info)
852{
853 disp->xres_virtual = var->xres_virtual;
854 disp->yres_virtual = var->yres_virtual;
855 disp->bits_per_pixel = var->bits_per_pixel;
856 disp->grayscale = var->grayscale;
857 disp->nonstd = var->nonstd;
858 disp->accel_flags = var->accel_flags;
859 disp->height = var->height;
860 disp->width = var->width;
861 disp->red = var->red;
862 disp->green = var->green;
863 disp->blue = var->blue;
864 disp->transp = var->transp;
865 disp->rotate = var->rotate;
866 disp->mode = fb_match_mode(var, &info->modelist);
867 if (disp->mode == NULL)
868 /* This should not happen */
869 return -EINVAL;
870 return 0;
871}
872
873static void display_to_var(struct fb_var_screeninfo *var,
874 struct display *disp)
875{
876 fb_videomode_to_var(var, disp->mode);
877 var->xres_virtual = disp->xres_virtual;
878 var->yres_virtual = disp->yres_virtual;
879 var->bits_per_pixel = disp->bits_per_pixel;
880 var->grayscale = disp->grayscale;
881 var->nonstd = disp->nonstd;
882 var->accel_flags = disp->accel_flags;
883 var->height = disp->height;
884 var->width = disp->width;
885 var->red = disp->red;
886 var->green = disp->green;
887 var->blue = disp->blue;
888 var->transp = disp->transp;
889 var->rotate = disp->rotate;
890}
891
892static const char *fbcon_startup(void)
893{
894 const char *display_desc = "frame buffer device";
895 struct display *p = &fb_display[fg_console];
896 struct vc_data *vc = vc_cons[fg_console].d;
2f4516db 897 const struct font_desc *font = NULL;
1da177e4
LT
898 struct module *owner;
899 struct fb_info *info = NULL;
900 struct fbcon_ops *ops;
901 int rows, cols;
902 int irqres;
903
904 irqres = 1;
905 /*
906 * If num_registered_fb is zero, this is a call for the dummy part.
907 * The frame buffer devices weren't initialized yet.
908 */
909 if (!num_registered_fb || info_idx == -1)
910 return display_desc;
911 /*
912 * Instead of blindly using registered_fb[0], we use info_idx, set by
913 * fb_console_init();
914 */
915 info = registered_fb[info_idx];
916 if (!info)
917 return NULL;
918
919 owner = info->fbops->owner;
920 if (!try_module_get(owner))
921 return NULL;
922 if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
923 module_put(owner);
924 return NULL;
925 }
926
a39bc34e 927 ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
1da177e4
LT
928 if (!ops) {
929 module_put(owner);
930 return NULL;
931 }
932
1da177e4
LT
933 ops->currcon = -1;
934 ops->graphics = 1;
e4fc2761 935 ops->cur_rotate = -1;
1da177e4 936 info->fbcon_par = ops;
e4fc2761 937 p->con_rotate = rotate;
b73deed3 938 set_blitting_type(vc, info);
1da177e4
LT
939
940 if (info->fix.type != FB_TYPE_TEXT) {
941 if (fbcon_softback_size) {
942 if (!softback_buf) {
943 softback_buf =
944 (unsigned long)
945 kmalloc(fbcon_softback_size,
946 GFP_KERNEL);
947 if (!softback_buf) {
948 fbcon_softback_size = 0;
949 softback_top = 0;
950 }
951 }
952 } else {
953 if (softback_buf) {
954 kfree((void *) softback_buf);
955 softback_buf = 0;
956 softback_top = 0;
957 }
958 }
959 if (softback_buf)
960 softback_in = softback_top = softback_curr =
961 softback_buf;
962 softback_lines = 0;
963 }
964
965 /* Setup default font */
966 if (!p->fontdata) {
967 if (!fontname[0] || !(font = find_font(fontname)))
968 font = get_default_font(info->var.xres,
969 info->var.yres);
970 vc->vc_font.width = font->width;
971 vc->vc_font.height = font->height;
2f4516db 972 vc->vc_font.data = (void *)(p->fontdata = font->data);
1da177e4
LT
973 vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */
974 }
975
e4fc2761
AD
976 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
977 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
978 cols /= vc->vc_font.width;
979 rows /= vc->vc_font.height;
1da177e4
LT
980 vc_resize(vc, cols, rows);
981
982 DPRINTK("mode: %s\n", info->fix.id);
983 DPRINTK("visual: %d\n", info->fix.visual);
984 DPRINTK("res: %dx%d-%d\n", info->var.xres,
985 info->var.yres,
986 info->var.bits_per_pixel);
987
988#ifdef CONFIG_ATARI
989 if (MACH_IS_ATARI) {
990 cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
991 irqres =
992 request_irq(IRQ_AUTO_4, fb_vbl_handler,
993 IRQ_TYPE_PRIO, "framebuffer vbl",
994 info);
995 }
996#endif /* CONFIG_ATARI */
997
998#ifdef CONFIG_MAC
999 /*
1000 * On a Macintoy, the VBL interrupt may or may not be active.
1001 * As interrupt based cursor is more reliable and race free, we
1002 * probe for VBL interrupts.
1003 */
1004 if (MACH_IS_MAC) {
1005 int ct = 0;
1006 /*
1007 * Probe for VBL: set temp. handler ...
1008 */
1009 irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0,
1010 "framebuffer vbl", info);
1011 vbl_detected = 0;
1012
1013 /*
1014 * ... and spin for 20 ms ...
1015 */
1016 while (!vbl_detected && ++ct < 1000)
1017 udelay(20);
1018
1019 if (ct == 1000)
1020 printk
1021 ("fbcon_startup: No VBL detected, using timer based cursor.\n");
1022
1023 free_irq(IRQ_MAC_VBL, fb_vbl_detect);
1024
1025 if (vbl_detected) {
1026 /*
1027 * interrupt based cursor ok
1028 */
1029 cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
1030 irqres =
1031 request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0,
1032 "framebuffer vbl", info);
1033 } else {
1034 /*
1035 * VBL not detected: fall through, use timer based cursor
1036 */
1037 irqres = 1;
1038 }
1039 }
1040#endif /* CONFIG_MAC */
1041
88fb2c6e 1042 fbcon_add_cursor_timer(info);
1da177e4
LT
1043 return display_desc;
1044}
1045
1046static void fbcon_init(struct vc_data *vc, int init)
1047{
1048 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1049 struct fbcon_ops *ops;
1050 struct vc_data **default_mode = vc->vc_display_fg;
1051 struct vc_data *svc = *default_mode;
1052 struct display *t, *p = &fb_display[vc->vc_num];
1053 int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
306958e8 1054 int cap;
1da177e4
LT
1055
1056 if (info_idx == -1 || info == NULL)
1057 return;
306958e8
AB
1058
1059 cap = info->flags;
1060
1da177e4
LT
1061 if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
1062 (info->fix.type == FB_TYPE_TEXT))
1063 logo = 0;
1064
1da177e4
LT
1065 if (var_to_display(p, &info->var, info))
1066 return;
1067
1068 /* If we are not the first console on this
1069 fb, copy the font from that console */
1070 t = &fb_display[svc->vc_num];
1071 if (!vc->vc_font.data) {
2f4516db 1072 vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
1da177e4
LT
1073 vc->vc_font.width = (*default_mode)->vc_font.width;
1074 vc->vc_font.height = (*default_mode)->vc_font.height;
1075 p->userfont = t->userfont;
1076 if (p->userfont)
1077 REFCOUNT(p->fontdata)++;
1078 }
1079 if (p->userfont)
1080 charcnt = FNTCHARCNT(p->fontdata);
b8c90945 1081 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1da177e4
LT
1082 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1083 if (charcnt == 256) {
1084 vc->vc_hi_font_mask = 0;
1085 } else {
1086 vc->vc_hi_font_mask = 0x100;
1087 if (vc->vc_can_do_color)
1088 vc->vc_complement_mask <<= 1;
1089 }
1090
1091 if (!*svc->vc_uni_pagedir_loc)
1092 con_set_default_unimap(svc);
1093 if (!*vc->vc_uni_pagedir_loc)
1094 con_copy_unimap(vc, svc);
1095
e4fc2761
AD
1096 ops = info->fbcon_par;
1097 p->con_rotate = rotate;
b73deed3 1098 set_blitting_type(vc, info);
e4fc2761 1099
1da177e4
LT
1100 cols = vc->vc_cols;
1101 rows = vc->vc_rows;
e4fc2761
AD
1102 new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1103 new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1104 new_cols /= vc->vc_font.width;
1105 new_rows /= vc->vc_font.height;
1da177e4
LT
1106 vc_resize(vc, new_cols, new_rows);
1107
1da177e4
LT
1108 /*
1109 * We must always set the mode. The mode of the previous console
1110 * driver could be in the same resolution but we are using different
1111 * hardware so we have to initialize the hardware.
1112 *
1113 * We need to do it in fbcon_init() to prevent screen corruption.
1114 */