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@yahoo.com)
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/module.h>
29 #include <linux/kernel.h>
30 #include <linux/errno.h>
31 #include <linux/string.h>
33 #include <linux/slab.h>
34 #include <linux/vmalloc.h>
35 #include <linux/delay.h>
36 #include <linux/interrupt.h>
38 #include <linux/selection.h>
39 #include <linux/init.h>
40 #include <linux/console.h>
42 #include <linux/vt_kern.h>
44 #include <asm/uaccess.h>
45 #include <asm/pgtable.h> /* io_remap_page_range() */
48 #include <asm/oplib.h>
49 #include <asm/machines.h>
50 #include <asm/idprom.h>
52 #define CGFOUR_OBMEM_ADDR 0x1f300000
53 #define BWTWO_OBMEM_ADDR 0x1f000000
54 #define BWTWO_OBMEM50_ADDR 0x00100000
58 #include <asm/sun3x.h>
60 #include <video/sbusfb.h>
62 #define DEFAULT_CURSOR_BLINK_RATE (2*HZ/5)
64 #define CURSOR_SHAPE 1
65 #define CURSOR_BLINK 2
67 #define mymemset(x,y) memset(x,0,y)
70 * Interface used by the world
73 int sun3fb_init(void);
74 void sun3fb_setup(char *options
);
76 static char fontname
[40] __initdata
= { 0 };
77 static int curblink __initdata
= 1;
79 static int sun3fb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
80 struct fb_info
*info
);
81 static int sun3fb_get_var(struct fb_var_screeninfo
*var
, int con
,
82 struct fb_info
*info
);
83 static int sun3fb_set_var(struct fb_var_screeninfo
*var
, int con
,
84 struct fb_info
*info
);
85 static int sun3fb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
86 struct fb_info
*info
);
87 static int sun3fb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
88 struct fb_info
*info
);
89 static int sun3fb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
90 u_int transp
, struct fb_info
*info
);
91 static int sun3fb_blank(int blank
, struct fb_info
*info
);
92 static void sun3fb_cursor(struct display
*p
, int mode
, int x
, int y
);
93 static void sun3fb_clear_margin(struct display
*p
, int s
);
96 * Interface to the low level console driver
99 static int sun3fbcon_switch(int con
, struct fb_info
*info
);
100 static int sun3fbcon_updatevar(int con
, struct fb_info
*info
);
106 static int sun3fb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
107 u_int
*transp
, struct fb_info
*info
);
109 static struct fb_ops sun3fb_ops
= {
110 .owner
= THIS_MODULE
,
111 .fb_get_fix
= sun3fb_get_fix
,
112 .fb_get_var
= sun3fb_get_var
,
113 .fb_set_var
= sun3fb_set_var
,
114 .fb_get_cmap
= sun3fb_get_cmap
,
115 .fb_set_cmap
= sun3fb_set_cmap
,
116 .fb_setcolreg
= sun3fb_setcolreg
,
117 .fb_blank
= sun3fb_blank
,
120 static void sun3fb_clear_margin(struct display
*p
, int s
)
122 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
126 if (fb
->switch_from_graph
)
127 (*fb
->switch_from_graph
)(fb
);
129 unsigned short rects
[16];
133 rects
[2] = fb
->var
.xres_virtual
;
134 rects
[3] = fb
->y_margin
;
136 rects
[5] = fb
->y_margin
;
137 rects
[6] = fb
->x_margin
;
138 rects
[7] = fb
->var
.yres_virtual
;
139 rects
[8] = fb
->var
.xres_virtual
- fb
->x_margin
;
140 rects
[9] = fb
->y_margin
;
141 rects
[10] = fb
->var
.xres_virtual
;
142 rects
[11] = fb
->var
.yres_virtual
;
143 rects
[12] = fb
->x_margin
;
144 rects
[13] = fb
->var
.yres_virtual
- fb
->y_margin
;
145 rects
[14] = fb
->var
.xres_virtual
- fb
->x_margin
;
146 rects
[15] = fb
->var
.yres_virtual
;
147 (*fb
->fill
)(fb
, p
, s
, 4, rects
);
149 unsigned char *fb_base
= fb
->info
.screen_base
, *q
;
150 int skip_bytes
= fb
->y_margin
* fb
->var
.xres_virtual
;
151 int scr_size
= fb
->var
.xres_virtual
* fb
->var
.yres_virtual
;
152 int h
, he
, incr
, size
;
155 if (fb
->var
.bits_per_pixel
== 1) {
156 fb_base
-= (skip_bytes
+ fb
->x_margin
) / 8;
159 mymemset (fb_base
, skip_bytes
- fb
->x_margin
/ 8);
160 mymemset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
/ 8, skip_bytes
- fb
->x_margin
/ 8);
161 incr
= fb
->var
.xres_virtual
/ 8;
162 size
= fb
->x_margin
/ 8 * 2;
163 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
/ 8, h
= 0;
164 h
<= he
; q
+= incr
, h
++)
167 fb_base
-= (skip_bytes
+ fb
->x_margin
);
168 memset (fb_base
, attr_bgcol(p
,s
), skip_bytes
- fb
->x_margin
);
169 memset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
, attr_bgcol(p
,s
), skip_bytes
- fb
->x_margin
);
170 incr
= fb
->var
.xres_virtual
;
171 size
= fb
->x_margin
* 2;
172 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
, h
= 0;
173 h
<= he
; q
+= incr
, h
++)
174 memset (q
, attr_bgcol(p
,s
), size
);
179 static void sun3fb_disp_setup(struct display
*p
)
181 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
185 sun3fb_clear_margin(p
, 0);
189 * Get the Fixed Part of the Display
192 static int sun3fb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
193 struct fb_info
*info
)
195 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
197 memcpy(fix
, &fb
->fix
, sizeof(struct fb_fix_screeninfo
));
202 * Get the User Defined Part of the Display
205 static int sun3fb_get_var(struct fb_var_screeninfo
*var
, int con
,
206 struct fb_info
*info
)
208 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
210 memcpy(var
, &fb
->var
, sizeof(struct fb_var_screeninfo
));
215 * Set the User Defined Part of the Display
218 static int sun3fb_set_var(struct fb_var_screeninfo
*var
, int con
,
219 struct fb_info
*info
)
221 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
223 if (var
->xres
> fb
->var
.xres
|| var
->yres
> fb
->var
.yres
||
224 var
->xres_virtual
> fb
->var
.xres_virtual
||
225 var
->yres_virtual
> fb
->var
.yres_virtual
||
226 var
->bits_per_pixel
!= fb
->var
.bits_per_pixel
||
228 (var
->vmode
& FB_VMODE_MASK
) != FB_VMODE_NONINTERLACED
)
230 memcpy(var
, &fb
->var
, sizeof(struct fb_var_screeninfo
));
238 static unsigned char hw_cursor_cmap
[2] = { 0, 0xff };
241 sun3fb_cursor_timer_handler(unsigned long dev_addr
)
243 struct fb_info_sbusfb
*fb
= (struct fb_info_sbusfb
*)dev_addr
;
245 if (!fb
->setcursor
) return;
247 if (fb
->cursor
.mode
& CURSOR_BLINK
) {
248 fb
->cursor
.enable
^= 1;
252 fb
->cursor
.timer
.expires
= jiffies
+ fb
->cursor
.blink_rate
;
253 add_timer(&fb
->cursor
.timer
);
256 static void sun3fb_cursor(struct display
*p
, int mode
, int x
, int y
)
258 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
262 fb
->cursor
.mode
&= ~CURSOR_BLINK
;
263 fb
->cursor
.enable
= 0;
264 (*fb
->setcursor
)(fb
);
269 if (fb
->cursor
.mode
& CURSOR_SHAPE
) {
270 fb
->cursor
.size
.fbx
= fontwidth(p
);
271 fb
->cursor
.size
.fby
= fontheight(p
);
272 fb
->cursor
.chot
.fbx
= 0;
273 fb
->cursor
.chot
.fby
= 0;
274 fb
->cursor
.enable
= 1;
275 memset (fb
->cursor
.bits
, 0, sizeof (fb
->cursor
.bits
));
276 fb
->cursor
.bits
[0][fontheight(p
) - 2] = (0xffffffff << (32 - fontwidth(p
)));
277 fb
->cursor
.bits
[1][fontheight(p
) - 2] = (0xffffffff << (32 - fontwidth(p
)));
278 fb
->cursor
.bits
[0][fontheight(p
) - 1] = (0xffffffff << (32 - fontwidth(p
)));
279 fb
->cursor
.bits
[1][fontheight(p
) - 1] = (0xffffffff << (32 - fontwidth(p
)));
280 (*fb
->setcursormap
) (fb
, hw_cursor_cmap
, hw_cursor_cmap
, hw_cursor_cmap
);
281 (*fb
->setcurshape
) (fb
);
283 fb
->cursor
.mode
= CURSOR_BLINK
;
285 fb
->cursor
.cpos
.fbx
= (x
<< fontwidthlog(p
)) + fb
->x_margin
;
287 fb
->cursor
.cpos
.fbx
= (x
* fontwidth(p
)) + fb
->x_margin
;
288 if (fontheightlog(p
))
289 fb
->cursor
.cpos
.fby
= (y
<< fontheightlog(p
)) + fb
->y_margin
;
291 fb
->cursor
.cpos
.fby
= (y
* fontheight(p
)) + fb
->y_margin
;
292 (*fb
->setcursor
)(fb
);
301 static int sun3fb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
302 struct fb_info
*info
)
304 if (con
== info
->currcon
) /* current console? */
305 return fb_get_cmap(cmap
, kspc
, sun3fb_getcolreg
, info
);
306 else if (fb_display
[con
].cmap
.len
) /* non default colormap? */
307 fb_copy_cmap(&fb_display
[con
].cmap
, cmap
, kspc
? 0 : 2);
309 fb_copy_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
), cmap
, kspc
? 0 : 2);
317 static int sun3fb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
318 struct fb_info
*info
)
322 if (!fb_display
[con
].cmap
.len
) { /* no colormap allocated? */
323 if ((err
= fb_alloc_cmap(&fb_display
[con
].cmap
, 1<<fb_display
[con
].var
.bits_per_pixel
, 0)))
326 if (con
== info
->currcon
) { /* current console? */
327 err
= fb_set_cmap(cmap
, kspc
, info
);
329 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
332 (*fb
->loadcmap
)(fb
, &fb_display
[con
], cmap
->start
, cmap
->len
);
336 fb_copy_cmap(cmap
, &fb_display
[con
].cmap
, kspc
? 0 : 1);
341 * Setup: parse used options
344 void __init
sun3fb_setup(char *options
)
348 for (p
= options
;;) {
349 if (!strncmp(p
, "font=", 5)) {
352 for (i
= 0; i
< sizeof(fontname
) - 1; i
++)
353 if (p
[i
+5] == ' ' || !p
[i
+5])
355 memcpy(fontname
, p
+5, i
);
357 } else if (!strncmp(p
, "noblink", 7))
359 while (*p
&& *p
!= ' ' && *p
!= ',') p
++;
360 if (*p
!= ',') break;
367 static int sun3fbcon_switch(int con
, struct fb_info
*info
)
369 int x_margin
, y_margin
;
370 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
373 /* Do we have to save the colormap? */
374 if (fb_display
[info
->currcon
].cmap
.len
)
375 fb_get_cmap(&fb_display
[info
->currcon
].cmap
, 1, sun3fb_getcolreg
, info
);
377 if (info
->display_fg
) {
378 lastconsole
= info
->display_fg
->vc_num
;
379 if (lastconsole
!= con
&&
380 (fontwidth(&fb_display
[lastconsole
]) != fontwidth(&fb_display
[con
]) ||
381 fontheight(&fb_display
[lastconsole
]) != fontheight(&fb_display
[con
])))
382 fb
->cursor
.mode
|= CURSOR_SHAPE
;
384 x_margin
= (fb_display
[con
].var
.xres_virtual
- fb_display
[con
].var
.xres
) / 2;
385 y_margin
= (fb_display
[con
].var
.yres_virtual
- fb_display
[con
].var
.yres
) / 2;
387 fb
->margins(fb
, &fb_display
[con
], x_margin
, y_margin
);
388 if (fb
->graphmode
|| fb
->x_margin
!= x_margin
|| fb
->y_margin
!= y_margin
) {
389 fb
->x_margin
= x_margin
; fb
->y_margin
= y_margin
;
390 sun3fb_clear_margin(&fb_display
[con
], 0);
393 /* Install new colormap */
394 do_install_cmap(con
, info
);
399 * Update the `var' structure (called by fbcon.c)
402 static int sun3fbcon_updatevar(int con
, struct fb_info
*info
)
412 static int sun3fb_blank(int blank
, struct fb_info
*info
)
414 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
416 if (blank
&& fb
->blank
)
417 return fb
->blank(fb
);
418 else if (!blank
&& fb
->unblank
)
419 return fb
->unblank(fb
);
424 * Read a single color register and split it into
425 * colors/transparent. Return != 0 for invalid regno.
428 static int sun3fb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
429 u_int
*transp
, struct fb_info
*info
)
431 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
433 if (!fb
->color_map
|| regno
> 255)
435 *red
= (fb
->color_map
CM(regno
, 0)<<8) | fb
->color_map
CM(regno
, 0);
436 *green
= (fb
->color_map
CM(regno
, 1)<<8) | fb
->color_map
CM(regno
, 1);
437 *blue
= (fb
->color_map
CM(regno
, 2)<<8) | fb
->color_map
CM(regno
, 2);
444 * Set a single color register. The values supplied are already
445 * rounded down to the hardware's capabilities (according to the
446 * entries in the var structure). Return != 0 for invalid regno.
449 static int sun3fb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
450 u_int transp
, struct fb_info
*info
)
452 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
454 if (!fb
->color_map
|| regno
> 255)
459 fb
->color_map
CM(regno
, 0) = red
;
460 fb
->color_map
CM(regno
, 1) = green
;
461 fb
->color_map
CM(regno
, 2) = blue
;
465 static int sun3fb_set_font(struct display
*p
, int width
, int height
)
467 int w
= p
->var
.xres_virtual
, h
= p
->var
.yres_virtual
;
468 int depth
= p
->var
.bits_per_pixel
;
469 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
470 int x_margin
, y_margin
;
472 if (depth
> 8) depth
= 8;
473 x_margin
= (w
% width
) / 2;
474 y_margin
= (h
% height
) / 2;
476 p
->var
.xres
= w
- 2*x_margin
;
477 p
->var
.yres
= h
- 2*y_margin
;
479 fb
->cursor
.mode
|= CURSOR_SHAPE
;
482 fb
->margins(fb
, p
, x_margin
, y_margin
);
483 if (fb
->x_margin
!= x_margin
|| fb
->y_margin
!= y_margin
) {
484 fb
->x_margin
= x_margin
; fb
->y_margin
= y_margin
;
485 sun3fb_clear_margin(p
, 0);
491 void sun3fb_palette(int enter
)
496 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
498 if (p
->dispsw
&& p
->dispsw
->setup
== sun3fb_disp_setup
&&
499 p
->fb_info
->display_fg
&&
500 p
->fb_info
->display_fg
->vc_num
== i
) {
501 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
503 if (fb
->restore_palette
) {
505 fb
->restore_palette(fb
);
506 else if (vc_cons
[i
].d
->vc_mode
!= KD_GRAPHICS
)
507 vc_cons
[i
].d
->vc_sw
->con_set_palette(vc_cons
[i
].d
, color_table
);
516 static int __init
sun3fb_init_fb(int fbtype
, unsigned long addr
)
518 static struct sbus_dev sdb
;
519 struct fb_fix_screeninfo
*fix
;
520 struct fb_var_screeninfo
*var
;
521 struct display
*disp
;
522 struct fb_info_sbusfb
*fb
;
524 int linebytes
, w
, h
, depth
;
527 fb
= kmalloc(sizeof(struct fb_info_sbusfb
), GFP_ATOMIC
);
531 memset(fb
, 0, sizeof(struct fb_info_sbusfb
));
537 sdb
.reg_addrs
[0].phys_addr
= addr
;
540 type
->fb_type
= fbtype
;
542 type
->fb_height
= h
= 900;
543 type
->fb_width
= w
= 1152;
545 type
->fb_depth
= depth
= (fbtype
== FBTYPE_SUN2BW
) ? 1 : 8;
546 linebytes
= w
* depth
/ 8;
547 type
->fb_size
= PAGE_ALIGN((linebytes
) * h
);
549 fb->x_margin = (w & 7) / 2;
550 fb->y_margin = (h & 15) / 2;
552 fb
->x_margin
= fb
->y_margin
= 0;
554 var
->xres_virtual
= w
;
555 var
->yres_virtual
= h
;
556 var
->xres
= w
- 2*fb
->x_margin
;
557 var
->yres
= h
- 2*fb
->y_margin
;
559 var
->bits_per_pixel
= depth
;
560 var
->height
= var
->width
= -1;
561 var
->pixclock
= 10000;
562 var
->vmode
= FB_VMODE_NONINTERLACED
;
563 var
->red
.length
= var
->green
.length
= var
->blue
.length
= 8;
565 fix
->line_length
= linebytes
;
566 fix
->smem_len
= type
->fb_size
;
567 fix
->type
= FB_TYPE_PACKED_PIXELS
;
568 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
570 fb
->info
.fbops
= &sun3fb_ops
;
571 fb
->info
.disp
= disp
;
572 fb
->info
.currcon
= -1;
573 strcpy(fb
->info
.fontname
, fontname
);
574 fb
->info
.changevar
= NULL
;
575 fb
->info
.switch_con
= &sun3fbcon_switch
;
576 fb
->info
.updatevar
= &sun3fbcon_updatevar
;
577 fb
->info
.flags
= FBINFO_FLAG_DEFAULT
;
579 fb
->cursor
.hwsize
.fbx
= 32;
580 fb
->cursor
.hwsize
.fby
= 32;
582 if (depth
> 1 && !fb
->color_map
) {
583 if((fb
->color_map
= kmalloc(256 * 3, GFP_ATOMIC
))==NULL
)
588 #ifdef CONFIG_FB_CGSIX
589 case FBTYPE_SUNFAST_COLOR
:
590 p
= cgsixfb_init(fb
); break;
592 #ifdef CONFIG_FB_BWTWO
594 p
= bwtwofb_init(fb
); break;
596 #ifdef CONFIG_FB_CGTHREE
597 case FBTYPE_SUN4COLOR
:
598 case FBTYPE_SUN3COLOR
:
599 type
->fb_size
= 0x100000;
600 p
= cgthreefb_init(fb
); break;
603 fix
->smem_start
= (unsigned long)fb
->info
.screen_base
; // FIXME
610 if (p
== SBUSFBINIT_SIZECHANGE
)
613 disp
->dispsw
= &fb
->dispsw
;
615 fb
->dispsw
.cursor
= sun3fb_cursor
;
617 fb
->cursor
.blink_rate
= DEFAULT_CURSOR_BLINK_RATE
;
618 init_timer(&fb
->cursor
.timer
);
619 fb
->cursor
.timer
.expires
= jiffies
+ fb
->cursor
.blink_rate
;
620 fb
->cursor
.timer
.data
= (unsigned long)fb
;
621 fb
->cursor
.timer
.function
= sun3fb_cursor_timer_handler
;
622 add_timer(&fb
->cursor
.timer
);
625 fb
->cursor
.mode
= CURSOR_SHAPE
;
626 fb
->dispsw
.set_font
= sun3fb_set_font
;
627 fb
->setup
= fb
->dispsw
.setup
;
628 fb
->dispsw
.setup
= sun3fb_disp_setup
;
629 fb
->dispsw
.clear_margins
= NULL
;
632 disp
->visual
= fix
->visual
;
633 disp
->type
= fix
->type
;
634 disp
->type_aux
= fix
->type_aux
;
635 disp
->line_length
= fix
->line_length
;
638 disp
->can_soft_blank
= 1;
640 sun3fb_set_var(var
, -1, &fb
->info
);
642 if (register_framebuffer(&fb
->info
) < 0) {
646 printk("fb%d: %s\n", fb
->info
.node
, p
);
652 int __init
sun3fb_init(void)
654 extern int con_is_present(void);
658 if (!con_is_present()) return -ENODEV
;
660 switch(*(romvec
->pv_fbtype
))
664 return sun3fb_init_fb(FBTYPE_SUN2BW
, addr
);
665 case FBTYPE_SUN3COLOR
:
666 case FBTYPE_SUN4COLOR
:
667 if(idprom
->id_machtype
!= (SM_SUN3
|SM_3_60
)) {
668 printk("sun3fb: cgthree/four only supported on 3/60\n");
672 addr
= CGFOUR_OBMEM_ADDR
;
673 return sun3fb_init_fb(*(romvec
->pv_fbtype
), addr
);
675 printk("sun3fb: unsupported framebuffer\n");
679 addr
= SUN3X_VIDEO_BASE
;
680 p4id
= *(char *)SUN3X_VIDEO_P4ID
;
682 p4id
= (p4id
== 0x45) ? p4id
: (p4id
& 0xf0);
685 return sun3fb_init_fb(FBTYPE_SUN2BW
, addr
);
688 return sun3fb_init_fb(FBTYPE_SUN4COLOR
, addr
);
691 return sun3fb_init_fb(FBTYPE_SUN8COLOR
, addr
);
695 return sun3fb_init_fb(FBTYPE_SUNFAST_COLOR
, addr
);
702 MODULE_LICENSE("GPL");