2 * linux/drivers/video/sun3fb.c -- Frame buffer driver for Sun3
4 * (C) 1998 Thomas Bogendoerfer
6 * This driver is bases on sbusfb.c, which is
8 * Copyright (C) 1998 Jakub Jelinek
10 * This driver is partly based on the Open Firmware console driver
12 * Copyright (C) 1997 Geert Uytterhoeven
14 * and SPARC console subsystem
16 * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
17 * Copyright (C) 1995-1997 David S. Miller (davem@caip.rutgers.edu)
18 * Copyright (C) 1995-1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
19 * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
20 * Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
21 * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
23 * This file is subject to the terms and conditions of the GNU General Public
24 * License. See the file COPYING in the main directory of this archive for
28 #include <linux/config.h>
29 #include <linux/module.h>
30 #include <linux/kernel.h>
31 #include <linux/errno.h>
32 #include <linux/string.h>
34 #include <linux/tty.h>
35 #include <linux/malloc.h>
36 #include <linux/vmalloc.h>
37 #include <linux/delay.h>
38 #include <linux/interrupt.h>
40 #include <linux/selection.h>
41 #include <linux/init.h>
42 #include <linux/console.h>
44 #include <linux/vt_kern.h>
46 #include <asm/uaccess.h>
47 #include <asm/pgtable.h> /* io_remap_page_range() */
50 #include <asm/oplib.h>
53 #include <asm/sun3x.h>
55 #include <video/sbusfb.h>
57 #define DEFAULT_CURSOR_BLINK_RATE (2*HZ/5)
59 #define CURSOR_SHAPE 1
60 #define CURSOR_BLINK 2
63 * Interface used by the world
66 int sun3fb_init(void);
67 int sun3fb_setup(char *options
);
70 static char fontname
[40] __initdata
= { 0 };
71 static int curblink __initdata
= 1;
73 static int sun3fb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
74 struct fb_info
*info
);
75 static int sun3fb_get_var(struct fb_var_screeninfo
*var
, int con
,
76 struct fb_info
*info
);
77 static int sun3fb_set_var(struct fb_var_screeninfo
*var
, int con
,
78 struct fb_info
*info
);
79 static int sun3fb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
80 struct fb_info
*info
);
81 static int sun3fb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
82 struct fb_info
*info
);
83 static void sun3fb_cursor(struct display
*p
, int mode
, int x
, int y
);
84 static void sun3fb_clear_margin(struct display
*p
, int s
);
88 * Interface to the low level console driver
91 static int sun3fbcon_switch(int con
, struct fb_info
*info
);
92 static int sun3fbcon_updatevar(int con
, struct fb_info
*info
);
93 static void sun3fbcon_blank(int blank
, struct fb_info
*info
);
100 static int sun3fb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
101 u_int
*transp
, struct fb_info
*info
);
102 static int sun3fb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
103 u_int transp
, struct fb_info
*info
);
104 static void do_install_cmap(int con
, struct fb_info
*info
);
106 static struct fb_ops sun3fb_ops
= {
108 fb_get_fix
: sun3fb_get_fix
,
109 fb_get_var
: sun3fb_get_var
,
110 fb_set_var
: sun3fb_set_var
,
111 fb_get_cmap
: sun3fb_get_cmap
,
112 fb_set_cmap
: sun3fb_set_cmap
,
115 static void sun3fb_clear_margin(struct display
*p
, int s
)
117 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
119 if (fb
->switch_from_graph
)
120 (*fb
->switch_from_graph
)(fb
);
122 unsigned short rects
[16];
126 rects
[2] = fb
->var
.xres_virtual
;
127 rects
[3] = fb
->y_margin
;
129 rects
[5] = fb
->y_margin
;
130 rects
[6] = fb
->x_margin
;
131 rects
[7] = fb
->var
.yres_virtual
;
132 rects
[8] = fb
->var
.xres_virtual
- fb
->x_margin
;
133 rects
[9] = fb
->y_margin
;
134 rects
[10] = fb
->var
.xres_virtual
;
135 rects
[11] = fb
->var
.yres_virtual
;
136 rects
[12] = fb
->x_margin
;
137 rects
[13] = fb
->var
.yres_virtual
- fb
->y_margin
;
138 rects
[14] = fb
->var
.xres_virtual
- fb
->x_margin
;
139 rects
[15] = fb
->var
.yres_virtual
;
140 (*fb
->fill
)(fb
, p
, s
, 4, rects
);
142 unsigned char *fb_base
= p
->screen_base
, *q
;
143 int skip_bytes
= fb
->y_margin
* fb
->var
.xres_virtual
;
144 int scr_size
= fb
->var
.xres_virtual
* fb
->var
.yres_virtual
;
145 int h
, he
, incr
, size
;
148 if (fb
->var
.bits_per_pixel
== 1) {
149 fb_base
-= (skip_bytes
+ fb
->x_margin
) / 8;
152 mymemset (fb_base
, skip_bytes
- fb
->x_margin
/ 8);
153 mymemset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
/ 8, skip_bytes
- fb
->x_margin
/ 8);
154 incr
= fb
->var
.xres_virtual
/ 8;
155 size
= fb
->x_margin
/ 8 * 2;
156 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
/ 8, h
= 0;
157 h
<= he
; q
+= incr
, h
++)
160 fb_base
-= (skip_bytes
+ fb
->x_margin
);
161 memset (fb_base
, attr_bgcol(p
,s
), skip_bytes
- fb
->x_margin
);
162 memset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
, attr_bgcol(p
,s
), skip_bytes
- fb
->x_margin
);
163 incr
= fb
->var
.xres_virtual
;
164 size
= fb
->x_margin
* 2;
165 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
, h
= 0;
166 h
<= he
; q
+= incr
, h
++)
167 memset (q
, attr_bgcol(p
,s
), size
);
172 static void sun3fb_disp_setup(struct display
*p
)
174 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
178 sun3fb_clear_margin(p
, 0);
182 * Get the Fixed Part of the Display
185 static int sun3fb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
186 struct fb_info
*info
)
188 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
190 memcpy(fix
, &fb
->fix
, sizeof(struct fb_fix_screeninfo
));
195 * Get the User Defined Part of the Display
198 static int sun3fb_get_var(struct fb_var_screeninfo
*var
, int con
,
199 struct fb_info
*info
)
201 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
203 memcpy(var
, &fb
->var
, sizeof(struct fb_var_screeninfo
));
208 * Set the User Defined Part of the Display
211 static int sun3fb_set_var(struct fb_var_screeninfo
*var
, int con
,
212 struct fb_info
*info
)
214 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
216 if (var
->xres
> fb
->var
.xres
|| var
->yres
> fb
->var
.yres
||
217 var
->xres_virtual
> fb
->var
.xres_virtual
||
218 var
->yres_virtual
> fb
->var
.yres_virtual
||
219 var
->bits_per_pixel
!= fb
->var
.bits_per_pixel
||
221 (var
->vmode
& FB_VMODE_MASK
) != FB_VMODE_NONINTERLACED
)
223 memcpy(var
, &fb
->var
, sizeof(struct fb_var_screeninfo
));
231 static unsigned char hw_cursor_cmap
[2] = { 0, 0xff };
234 sun3fb_cursor_timer_handler(unsigned long dev_addr
)
236 struct fb_info_sbusfb
*fb
= (struct fb_info_sbusfb
*)dev_addr
;
238 if (!fb
->setcursor
) return;
240 if (fb
->cursor
.mode
& CURSOR_BLINK
) {
241 fb
->cursor
.enable
^= 1;
245 fb
->cursor
.timer
.expires
= jiffies
+ fb
->cursor
.blink_rate
;
246 add_timer(&fb
->cursor
.timer
);
249 static void sun3fb_cursor(struct display
*p
, int mode
, int x
, int y
)
251 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
255 fb
->cursor
.mode
&= ~CURSOR_BLINK
;
256 fb
->cursor
.enable
= 0;
257 (*fb
->setcursor
)(fb
);
262 if (fb
->cursor
.mode
& CURSOR_SHAPE
) {
263 fb
->cursor
.size
.fbx
= fontwidth(p
);
264 fb
->cursor
.size
.fby
= fontheight(p
);
265 fb
->cursor
.chot
.fbx
= 0;
266 fb
->cursor
.chot
.fby
= 0;
267 fb
->cursor
.enable
= 1;
268 memset (fb
->cursor
.bits
, 0, sizeof (fb
->cursor
.bits
));
269 fb
->cursor
.bits
[0][fontheight(p
) - 2] = (0xffffffff << (32 - fontwidth(p
)));
270 fb
->cursor
.bits
[1][fontheight(p
) - 2] = (0xffffffff << (32 - fontwidth(p
)));
271 fb
->cursor
.bits
[0][fontheight(p
) - 1] = (0xffffffff << (32 - fontwidth(p
)));
272 fb
->cursor
.bits
[1][fontheight(p
) - 1] = (0xffffffff << (32 - fontwidth(p
)));
273 (*fb
->setcursormap
) (fb
, hw_cursor_cmap
, hw_cursor_cmap
, hw_cursor_cmap
);
274 (*fb
->setcurshape
) (fb
);
276 fb
->cursor
.mode
= CURSOR_BLINK
;
278 fb
->cursor
.cpos
.fbx
= (x
<< fontwidthlog(p
)) + fb
->x_margin
;
280 fb
->cursor
.cpos
.fbx
= (x
* fontwidth(p
)) + fb
->x_margin
;
281 if (fontheightlog(p
))
282 fb
->cursor
.cpos
.fby
= (y
<< fontheightlog(p
)) + fb
->y_margin
;
284 fb
->cursor
.cpos
.fby
= (y
* fontheight(p
)) + fb
->y_margin
;
285 (*fb
->setcursor
)(fb
);
294 static int sun3fb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
295 struct fb_info
*info
)
297 if (con
== currcon
) /* current console? */
298 return fb_get_cmap(cmap
, kspc
, sun3fb_getcolreg
, info
);
299 else if (fb_display
[con
].cmap
.len
) /* non default colormap? */
300 fb_copy_cmap(&fb_display
[con
].cmap
, cmap
, kspc
? 0 : 2);
302 fb_copy_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
), cmap
, kspc
? 0 : 2);
310 static int sun3fb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
311 struct fb_info
*info
)
315 if (!fb_display
[con
].cmap
.len
) { /* no colormap allocated? */
316 if ((err
= fb_alloc_cmap(&fb_display
[con
].cmap
, 1<<fb_display
[con
].var
.bits_per_pixel
, 0)))
319 if (con
== currcon
) { /* current console? */
320 err
= fb_set_cmap(cmap
, kspc
, sun3fb_setcolreg
, info
);
322 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
325 (*fb
->loadcmap
)(fb
, &fb_display
[con
], cmap
->start
, cmap
->len
);
329 fb_copy_cmap(cmap
, &fb_display
[con
].cmap
, kspc
? 0 : 1);
334 * Setup: parse used options
337 __initfunc(void sun3fb_setup(char *options
))
341 for (p
= options
;;) {
342 if (!strncmp(p
, "font=", 5)) {
345 for (i
= 0; i
< sizeof(fontname
) - 1; i
++)
346 if (p
[i
+5] == ' ' || !p
[i
+5])
348 memcpy(fontname
, p
+5, i
);
350 } else if (!strncmp(p
, "noblink", 7))
352 while (*p
&& *p
!= ' ' && *p
!= ',') p
++;
353 if (*p
!= ',') break;
360 static int sun3fbcon_switch(int con
, struct fb_info
*info
)
362 int x_margin
, y_margin
;
363 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
366 /* Do we have to save the colormap? */
367 if (fb_display
[currcon
].cmap
.len
)
368 fb_get_cmap(&fb_display
[currcon
].cmap
, 1, sun3fb_getcolreg
, info
);
370 if (info
->display_fg
) {
371 lastconsole
= info
->display_fg
->vc_num
;
372 if (lastconsole
!= con
&&
373 (fontwidth(&fb_display
[lastconsole
]) != fontwidth(&fb_display
[con
]) ||
374 fontheight(&fb_display
[lastconsole
]) != fontheight(&fb_display
[con
])))
375 fb
->cursor
.mode
|= CURSOR_SHAPE
;
377 x_margin
= (fb_display
[con
].var
.xres_virtual
- fb_display
[con
].var
.xres
) / 2;
378 y_margin
= (fb_display
[con
].var
.yres_virtual
- fb_display
[con
].var
.yres
) / 2;
380 fb
->margins(fb
, &fb_display
[con
], x_margin
, y_margin
);
381 if (fb
->graphmode
|| fb
->x_margin
!= x_margin
|| fb
->y_margin
!= y_margin
) {
382 fb
->x_margin
= x_margin
; fb
->y_margin
= y_margin
;
383 sun3fb_clear_margin(&fb_display
[con
], 0);
386 /* Install new colormap */
387 do_install_cmap(con
, info
);
392 * Update the `var' structure (called by fbcon.c)
395 static int sun3fbcon_updatevar(int con
, struct fb_info
*info
)
405 static void sun3fbcon_blank(int blank
, struct fb_info
*info
)
407 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
409 if (blank
&& fb
->blank
)
410 return fb
->blank(fb
);
411 else if (!blank
&& fb
->unblank
)
412 return fb
->unblank(fb
);
416 * Read a single color register and split it into
417 * colors/transparent. Return != 0 for invalid regno.
420 static int sun3fb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
421 u_int
*transp
, struct fb_info
*info
)
423 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
425 if (!fb
->color_map
|| regno
> 255)
427 *red
= (fb
->color_map
CM(regno
, 0)<<8) | fb
->color_map
CM(regno
, 0);
428 *green
= (fb
->color_map
CM(regno
, 1)<<8) | fb
->color_map
CM(regno
, 1);
429 *blue
= (fb
->color_map
CM(regno
, 2)<<8) | fb
->color_map
CM(regno
, 2);
436 * Set a single color register. The values supplied are already
437 * rounded down to the hardware's capabilities (according to the
438 * entries in the var structure). Return != 0 for invalid regno.
441 static int sun3fb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
442 u_int transp
, struct fb_info
*info
)
444 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
446 if (!fb
->color_map
|| regno
> 255)
451 fb
->color_map
CM(regno
, 0) = red
;
452 fb
->color_map
CM(regno
, 1) = green
;
453 fb
->color_map
CM(regno
, 2) = blue
;
458 static void do_install_cmap(int con
, struct fb_info
*info
)
460 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
464 if (fb_display
[con
].cmap
.len
)
465 fb_set_cmap(&fb_display
[con
].cmap
, 1, sun3fb_setcolreg
, info
);
467 fb_set_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
),
468 1, sun3fb_setcolreg
, info
);
470 (*fb
->loadcmap
)(fb
, &fb_display
[con
], 0, 256);
473 static int sun3fb_set_font(struct display
*p
, int width
, int height
)
475 int w
= p
->var
.xres_virtual
, h
= p
->var
.yres_virtual
;
476 int depth
= p
->var
.bits_per_pixel
;
477 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
478 int x_margin
, y_margin
;
480 if (depth
> 8) depth
= 8;
481 x_margin
= (w
% width
) / 2;
482 y_margin
= (h
% height
) / 2;
484 p
->var
.xres
= w
- 2*x_margin
;
485 p
->var
.yres
= h
- 2*y_margin
;
487 fb
->cursor
.mode
|= CURSOR_SHAPE
;
490 fb
->margins(fb
, p
, x_margin
, y_margin
);
491 if (fb
->x_margin
!= x_margin
|| fb
->y_margin
!= y_margin
) {
492 fb
->x_margin
= x_margin
; fb
->y_margin
= y_margin
;
493 sun3fb_clear_margin(p
, 0);
499 void sun3fb_palette(int enter
)
504 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
506 if (p
->dispsw
&& p
->dispsw
->setup
== sun3fb_disp_setup
&&
507 p
->fb_info
->display_fg
&&
508 p
->fb_info
->display_fg
->vc_num
== i
) {
509 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
511 if (fb
->restore_palette
) {
513 fb
->restore_palette(fb
);
514 else if (vt_cons
[i
]->vc_mode
!= KD_GRAPHICS
)
515 vc_cons
[i
].d
->vc_sw
->con_set_palette(vc_cons
[i
].d
, color_table
);
524 __initfunc(static void sun3fb_init_fb(int fbtype
, unsigned long addr
))
526 static struct linux_sbus_device sdb
;
527 struct fb_fix_screeninfo
*fix
;
528 struct fb_var_screeninfo
*var
;
529 struct display
*disp
;
530 struct fb_info_sbusfb
*fb
;
532 int linebytes
, w
, h
, depth
;
535 fb
= kmalloc(sizeof(struct fb_info_sbusfb
), GFP_ATOMIC
);
539 memset(fb
, 0, sizeof(struct fb_info_sbusfb
));
545 sdb
.reg_addrs
[0].phys_addr
= addr
;
548 type
->fb_type
= fbtype
;
550 type
->fb_height
= h
= 900;
551 type
->fb_width
= w
= 1152;
553 type
->fb_depth
= depth
= (fbtype
== FBTYPE_SUN2BW
) ? 1 : 8;
554 linebytes
= w
* depth
/ 8;
555 type
->fb_size
= PAGE_ALIGN((linebytes
) * h
);
557 fb
->x_margin
= (w
& 7) / 2;
558 fb
->y_margin
= (h
& 15) / 2;
560 var
->xres_virtual
= w
;
561 var
->yres_virtual
= h
;
562 var
->xres
= w
- 2*fb
->x_margin
;
563 var
->yres
= h
- 2*fb
->y_margin
;
565 var
->bits_per_pixel
= depth
;
566 var
->height
= var
->width
= -1;
567 var
->pixclock
= 10000;
568 var
->vmode
= FB_VMODE_NONINTERLACED
;
569 var
->red
.length
= var
->green
.length
= var
->blue
.length
= 8;
571 fix
->line_length
= linebytes
;
572 fix
->smem_len
= type
->fb_size
;
573 fix
->type
= FB_TYPE_PACKED_PIXELS
;
574 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
577 fb
->info
.fbops
= &sun3fb_ops
;
578 fb
->info
.disp
= disp
;
579 strcpy(fb
->info
.fontname
, fontname
);
580 fb
->info
.changevar
= NULL
;
581 fb
->info
.switch_con
= &sun3fbcon_switch
;
582 fb
->info
.updatevar
= &sun3fbcon_updatevar
;
583 fb
->info
.blank
= &sun3fbcon_blank
;
584 fb
->info
.flags
= FBINFO_FLAG_DEFAULT
;
586 fb
->cursor
.hwsize
.fbx
= 32;
587 fb
->cursor
.hwsize
.fby
= 32;
589 if (depth
> 1 && !fb
->color_map
)
590 fb
->color_map
= kmalloc(256 * 3, GFP_ATOMIC
);
593 #ifdef CONFIG_FB_CGSIX
594 case FBTYPE_SUNFAST_COLOR
:
595 p
= cgsixfb_init(fb
); break;
597 #ifdef CONFIG_FB_BWTWO
599 p
= bwtwofb_init(fb
); break;
602 fix
->smem_start
= fb
->disp
.screen_base
;
609 if (p
== SBUSFBINIT_SIZECHANGE
)
612 disp
->dispsw
= &fb
->dispsw
;
614 fb
->dispsw
.cursor
= sun3fb_cursor
;
616 fb
->cursor
.blink_rate
= DEFAULT_CURSOR_BLINK_RATE
;
617 init_timer(&fb
->cursor
.timer
);
618 fb
->cursor
.timer
.expires
= jiffies
+ fb
->cursor
.blink_rate
;
619 fb
->cursor
.timer
.data
= (unsigned long)fb
;
620 fb
->cursor
.timer
.function
= sun3fb_cursor_timer_handler
;
621 add_timer(&fb
->cursor
.timer
);
624 fb
->cursor
.mode
= CURSOR_SHAPE
;
625 fb
->dispsw
.set_font
= sun3fb_set_font
;
626 fb
->setup
= fb
->dispsw
.setup
;
627 fb
->dispsw
.setup
= sun3fb_disp_setup
;
628 fb
->dispsw
.clear_margins
= NULL
;
631 disp
->visual
= fix
->visual
;
632 disp
->type
= fix
->type
;
633 disp
->type_aux
= fix
->type_aux
;
634 disp
->line_length
= fix
->line_length
;
637 disp
->can_soft_blank
= 1;
639 sun3fb_set_var(var
, -1, &fb
->info
);
641 if (register_framebuffer(&fb
->info
) < 0) {
645 printk("fb%d: %s\n", GET_FB_IDX(fb
->info
.node
), p
);
651 __initfunc(int sun3fb_init(void))
653 extern int con_is_present(void);
657 if (!con_is_present()) return;
658 printk("sun3fb_init()\n");
661 switch(*(romvec
->pv_fbtype
))
664 return sun3fb_init_fb(FBTYPE_SUN2BW
, addr
);
665 case FBTYPE_SUN3COLOR
:
666 printk("cg3 detected but not supported\n");
670 addr
= SUN3X_VIDEO_BASE
;
671 p4id
= *(char *)SUN3X_VIDEO_P4ID
;
673 p4id
= (p4id
== 0x45) ? p4id
: (p4id
& 0xf0);
676 return sun3fb_init_fb(FBTYPE_SUN2BW
, addr
);
679 sun3fb_init_fb(FBTYPE_SUN4COLOR
, addr
);
682 sun3fb_init_fb(FBTYPE_SUN8COLOR
, addr
);
686 return sun3fb_init_fb(FBTYPE_SUNFAST_COLOR
, addr
);