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/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/slab.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>
51 #include <asm/machines.h>
52 #include <asm/idprom.h>
54 #define CGFOUR_OBMEM_ADDR 0x1f300000
55 #define BWTWO_OBMEM_ADDR 0x1f000000
56 #define BWTWO_OBMEM50_ADDR 0x00100000
60 #include <asm/sun3x.h>
62 #include <video/sbusfb.h>
64 #define DEFAULT_CURSOR_BLINK_RATE (2*HZ/5)
66 #define CURSOR_SHAPE 1
67 #define CURSOR_BLINK 2
69 #define mymemset(x,y) memset(x,0,y)
72 * Interface used by the world
75 int sun3fb_init(void);
76 void sun3fb_setup(char *options
);
78 static char fontname
[40] __initdata
= { 0 };
79 static int curblink __initdata
= 1;
81 static int sun3fb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
82 struct fb_info
*info
);
83 static int sun3fb_get_var(struct fb_var_screeninfo
*var
, int con
,
84 struct fb_info
*info
);
85 static int sun3fb_set_var(struct fb_var_screeninfo
*var
, int con
,
86 struct fb_info
*info
);
87 static int sun3fb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
88 struct fb_info
*info
);
89 static int sun3fb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
90 struct fb_info
*info
);
91 static int sun3fb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
92 u_int transp
, struct fb_info
*info
);
93 static int sun3fb_blank(int blank
, struct fb_info
*info
);
94 static void sun3fb_cursor(struct display
*p
, int mode
, int x
, int y
);
95 static void sun3fb_clear_margin(struct display
*p
, int s
);
98 * Interface to the low level console driver
101 static int sun3fbcon_switch(int con
, struct fb_info
*info
);
102 static int sun3fbcon_updatevar(int con
, struct fb_info
*info
);
108 static int sun3fb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
109 u_int
*transp
, struct fb_info
*info
);
111 static struct fb_ops sun3fb_ops
= {
112 .owner
= THIS_MODULE
,
113 .fb_get_fix
= sun3fb_get_fix
,
114 .fb_get_var
= sun3fb_get_var
,
115 .fb_set_var
= sun3fb_set_var
,
116 .fb_get_cmap
= sun3fb_get_cmap
,
117 .fb_set_cmap
= sun3fb_set_cmap
,
118 .fb_setcolreg
= sun3fb_setcolreg
,
119 .fb_blank
= sun3fb_blank
,
122 static void sun3fb_clear_margin(struct display
*p
, int s
)
124 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
128 if (fb
->switch_from_graph
)
129 (*fb
->switch_from_graph
)(fb
);
131 unsigned short rects
[16];
135 rects
[2] = fb
->var
.xres_virtual
;
136 rects
[3] = fb
->y_margin
;
138 rects
[5] = fb
->y_margin
;
139 rects
[6] = fb
->x_margin
;
140 rects
[7] = fb
->var
.yres_virtual
;
141 rects
[8] = fb
->var
.xres_virtual
- fb
->x_margin
;
142 rects
[9] = fb
->y_margin
;
143 rects
[10] = fb
->var
.xres_virtual
;
144 rects
[11] = fb
->var
.yres_virtual
;
145 rects
[12] = fb
->x_margin
;
146 rects
[13] = fb
->var
.yres_virtual
- fb
->y_margin
;
147 rects
[14] = fb
->var
.xres_virtual
- fb
->x_margin
;
148 rects
[15] = fb
->var
.yres_virtual
;
149 (*fb
->fill
)(fb
, p
, s
, 4, rects
);
151 unsigned char *fb_base
= fb
->info
.screen_base
, *q
;
152 int skip_bytes
= fb
->y_margin
* fb
->var
.xres_virtual
;
153 int scr_size
= fb
->var
.xres_virtual
* fb
->var
.yres_virtual
;
154 int h
, he
, incr
, size
;
157 if (fb
->var
.bits_per_pixel
== 1) {
158 fb_base
-= (skip_bytes
+ fb
->x_margin
) / 8;
161 mymemset (fb_base
, skip_bytes
- fb
->x_margin
/ 8);
162 mymemset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
/ 8, skip_bytes
- fb
->x_margin
/ 8);
163 incr
= fb
->var
.xres_virtual
/ 8;
164 size
= fb
->x_margin
/ 8 * 2;
165 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
/ 8, h
= 0;
166 h
<= he
; q
+= incr
, h
++)
169 fb_base
-= (skip_bytes
+ fb
->x_margin
);
170 memset (fb_base
, attr_bgcol(p
,s
), skip_bytes
- fb
->x_margin
);
171 memset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
, attr_bgcol(p
,s
), skip_bytes
- fb
->x_margin
);
172 incr
= fb
->var
.xres_virtual
;
173 size
= fb
->x_margin
* 2;
174 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
, h
= 0;
175 h
<= he
; q
+= incr
, h
++)
176 memset (q
, attr_bgcol(p
,s
), size
);
181 static void sun3fb_disp_setup(struct display
*p
)
183 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
187 sun3fb_clear_margin(p
, 0);
191 * Get the Fixed Part of the Display
194 static int sun3fb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
195 struct fb_info
*info
)
197 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
199 memcpy(fix
, &fb
->fix
, sizeof(struct fb_fix_screeninfo
));
204 * Get the User Defined Part of the Display
207 static int sun3fb_get_var(struct fb_var_screeninfo
*var
, int con
,
208 struct fb_info
*info
)
210 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
212 memcpy(var
, &fb
->var
, sizeof(struct fb_var_screeninfo
));
217 * Set the User Defined Part of the Display
220 static int sun3fb_set_var(struct fb_var_screeninfo
*var
, int con
,
221 struct fb_info
*info
)
223 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
225 if (var
->xres
> fb
->var
.xres
|| var
->yres
> fb
->var
.yres
||
226 var
->xres_virtual
> fb
->var
.xres_virtual
||
227 var
->yres_virtual
> fb
->var
.yres_virtual
||
228 var
->bits_per_pixel
!= fb
->var
.bits_per_pixel
||
230 (var
->vmode
& FB_VMODE_MASK
) != FB_VMODE_NONINTERLACED
)
232 memcpy(var
, &fb
->var
, sizeof(struct fb_var_screeninfo
));
240 static unsigned char hw_cursor_cmap
[2] = { 0, 0xff };
243 sun3fb_cursor_timer_handler(unsigned long dev_addr
)
245 struct fb_info_sbusfb
*fb
= (struct fb_info_sbusfb
*)dev_addr
;
247 if (!fb
->setcursor
) return;
249 if (fb
->cursor
.mode
& CURSOR_BLINK
) {
250 fb
->cursor
.enable
^= 1;
254 fb
->cursor
.timer
.expires
= jiffies
+ fb
->cursor
.blink_rate
;
255 add_timer(&fb
->cursor
.timer
);
258 static void sun3fb_cursor(struct display
*p
, int mode
, int x
, int y
)
260 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
264 fb
->cursor
.mode
&= ~CURSOR_BLINK
;
265 fb
->cursor
.enable
= 0;
266 (*fb
->setcursor
)(fb
);
271 if (fb
->cursor
.mode
& CURSOR_SHAPE
) {
272 fb
->cursor
.size
.fbx
= fontwidth(p
);
273 fb
->cursor
.size
.fby
= fontheight(p
);
274 fb
->cursor
.chot
.fbx
= 0;
275 fb
->cursor
.chot
.fby
= 0;
276 fb
->cursor
.enable
= 1;
277 memset (fb
->cursor
.bits
, 0, sizeof (fb
->cursor
.bits
));
278 fb
->cursor
.bits
[0][fontheight(p
) - 2] = (0xffffffff << (32 - fontwidth(p
)));
279 fb
->cursor
.bits
[1][fontheight(p
) - 2] = (0xffffffff << (32 - fontwidth(p
)));
280 fb
->cursor
.bits
[0][fontheight(p
) - 1] = (0xffffffff << (32 - fontwidth(p
)));
281 fb
->cursor
.bits
[1][fontheight(p
) - 1] = (0xffffffff << (32 - fontwidth(p
)));
282 (*fb
->setcursormap
) (fb
, hw_cursor_cmap
, hw_cursor_cmap
, hw_cursor_cmap
);
283 (*fb
->setcurshape
) (fb
);
285 fb
->cursor
.mode
= CURSOR_BLINK
;
287 fb
->cursor
.cpos
.fbx
= (x
<< fontwidthlog(p
)) + fb
->x_margin
;
289 fb
->cursor
.cpos
.fbx
= (x
* fontwidth(p
)) + fb
->x_margin
;
290 if (fontheightlog(p
))
291 fb
->cursor
.cpos
.fby
= (y
<< fontheightlog(p
)) + fb
->y_margin
;
293 fb
->cursor
.cpos
.fby
= (y
* fontheight(p
)) + fb
->y_margin
;
294 (*fb
->setcursor
)(fb
);
303 static int sun3fb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
304 struct fb_info
*info
)
306 if (con
== info
->currcon
) /* current console? */
307 return fb_get_cmap(cmap
, kspc
, sun3fb_getcolreg
, info
);
308 else if (fb_display
[con
].cmap
.len
) /* non default colormap? */
309 fb_copy_cmap(&fb_display
[con
].cmap
, cmap
, kspc
? 0 : 2);
311 fb_copy_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
), cmap
, kspc
? 0 : 2);
319 static int sun3fb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
320 struct fb_info
*info
)
324 if (!fb_display
[con
].cmap
.len
) { /* no colormap allocated? */
325 if ((err
= fb_alloc_cmap(&fb_display
[con
].cmap
, 1<<fb_display
[con
].var
.bits_per_pixel
, 0)))
328 if (con
== info
->currcon
) { /* current console? */
329 err
= fb_set_cmap(cmap
, kspc
, info
);
331 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
334 (*fb
->loadcmap
)(fb
, &fb_display
[con
], cmap
->start
, cmap
->len
);
338 fb_copy_cmap(cmap
, &fb_display
[con
].cmap
, kspc
? 0 : 1);
343 * Setup: parse used options
346 void __init
sun3fb_setup(char *options
)
350 for (p
= options
;;) {
351 if (!strncmp(p
, "font=", 5)) {
354 for (i
= 0; i
< sizeof(fontname
) - 1; i
++)
355 if (p
[i
+5] == ' ' || !p
[i
+5])
357 memcpy(fontname
, p
+5, i
);
359 } else if (!strncmp(p
, "noblink", 7))
361 while (*p
&& *p
!= ' ' && *p
!= ',') p
++;
362 if (*p
!= ',') break;
369 static int sun3fbcon_switch(int con
, struct fb_info
*info
)
371 int x_margin
, y_margin
;
372 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
375 /* Do we have to save the colormap? */
376 if (fb_display
[info
->currcon
].cmap
.len
)
377 fb_get_cmap(&fb_display
[info
->currcon
].cmap
, 1, sun3fb_getcolreg
, info
);
379 if (info
->display_fg
) {
380 lastconsole
= info
->display_fg
->vc_num
;
381 if (lastconsole
!= con
&&
382 (fontwidth(&fb_display
[lastconsole
]) != fontwidth(&fb_display
[con
]) ||
383 fontheight(&fb_display
[lastconsole
]) != fontheight(&fb_display
[con
])))
384 fb
->cursor
.mode
|= CURSOR_SHAPE
;
386 x_margin
= (fb_display
[con
].var
.xres_virtual
- fb_display
[con
].var
.xres
) / 2;
387 y_margin
= (fb_display
[con
].var
.yres_virtual
- fb_display
[con
].var
.yres
) / 2;
389 fb
->margins(fb
, &fb_display
[con
], x_margin
, y_margin
);
390 if (fb
->graphmode
|| fb
->x_margin
!= x_margin
|| fb
->y_margin
!= y_margin
) {
391 fb
->x_margin
= x_margin
; fb
->y_margin
= y_margin
;
392 sun3fb_clear_margin(&fb_display
[con
], 0);
395 /* Install new colormap */
396 do_install_cmap(con
, info
);
401 * Update the `var' structure (called by fbcon.c)
404 static int sun3fbcon_updatevar(int con
, struct fb_info
*info
)
414 static int sun3fb_blank(int blank
, struct fb_info
*info
)
416 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
418 if (blank
&& fb
->blank
)
419 return fb
->blank(fb
);
420 else if (!blank
&& fb
->unblank
)
421 return fb
->unblank(fb
);
426 * Read a single color register and split it into
427 * colors/transparent. Return != 0 for invalid regno.
430 static int sun3fb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
431 u_int
*transp
, struct fb_info
*info
)
433 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
435 if (!fb
->color_map
|| regno
> 255)
437 *red
= (fb
->color_map
CM(regno
, 0)<<8) | fb
->color_map
CM(regno
, 0);
438 *green
= (fb
->color_map
CM(regno
, 1)<<8) | fb
->color_map
CM(regno
, 1);
439 *blue
= (fb
->color_map
CM(regno
, 2)<<8) | fb
->color_map
CM(regno
, 2);
446 * Set a single color register. The values supplied are already
447 * rounded down to the hardware's capabilities (according to the
448 * entries in the var structure). Return != 0 for invalid regno.
451 static int sun3fb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
452 u_int transp
, struct fb_info
*info
)
454 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
456 if (!fb
->color_map
|| regno
> 255)
461 fb
->color_map
CM(regno
, 0) = red
;
462 fb
->color_map
CM(regno
, 1) = green
;
463 fb
->color_map
CM(regno
, 2) = blue
;
467 static int sun3fb_set_font(struct display
*p
, int width
, int height
)
469 int w
= p
->var
.xres_virtual
, h
= p
->var
.yres_virtual
;
470 int depth
= p
->var
.bits_per_pixel
;
471 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
472 int x_margin
, y_margin
;
474 if (depth
> 8) depth
= 8;
475 x_margin
= (w
% width
) / 2;
476 y_margin
= (h
% height
) / 2;
478 p
->var
.xres
= w
- 2*x_margin
;
479 p
->var
.yres
= h
- 2*y_margin
;
481 fb
->cursor
.mode
|= CURSOR_SHAPE
;
484 fb
->margins(fb
, p
, x_margin
, y_margin
);
485 if (fb
->x_margin
!= x_margin
|| fb
->y_margin
!= y_margin
) {
486 fb
->x_margin
= x_margin
; fb
->y_margin
= y_margin
;
487 sun3fb_clear_margin(p
, 0);
493 void sun3fb_palette(int enter
)
498 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
500 if (p
->dispsw
&& p
->dispsw
->setup
== sun3fb_disp_setup
&&
501 p
->fb_info
->display_fg
&&
502 p
->fb_info
->display_fg
->vc_num
== i
) {
503 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
505 if (fb
->restore_palette
) {
507 fb
->restore_palette(fb
);
508 else if (vt_cons
[i
]->vc_mode
!= KD_GRAPHICS
)
509 vc_cons
[i
].d
->vc_sw
->con_set_palette(vc_cons
[i
].d
, color_table
);
518 static int __init
sun3fb_init_fb(int fbtype
, unsigned long addr
)
520 static struct sbus_dev sdb
;
521 struct fb_fix_screeninfo
*fix
;
522 struct fb_var_screeninfo
*var
;
523 struct display
*disp
;
524 struct fb_info_sbusfb
*fb
;
526 int linebytes
, w
, h
, depth
;
529 fb
= kmalloc(sizeof(struct fb_info_sbusfb
), GFP_ATOMIC
);
533 memset(fb
, 0, sizeof(struct fb_info_sbusfb
));
539 sdb
.reg_addrs
[0].phys_addr
= addr
;
542 type
->fb_type
= fbtype
;
544 type
->fb_height
= h
= 900;
545 type
->fb_width
= w
= 1152;
547 type
->fb_depth
= depth
= (fbtype
== FBTYPE_SUN2BW
) ? 1 : 8;
548 linebytes
= w
* depth
/ 8;
549 type
->fb_size
= PAGE_ALIGN((linebytes
) * h
);
551 fb->x_margin = (w & 7) / 2;
552 fb->y_margin = (h & 15) / 2;
554 fb
->x_margin
= fb
->y_margin
= 0;
556 var
->xres_virtual
= w
;
557 var
->yres_virtual
= h
;
558 var
->xres
= w
- 2*fb
->x_margin
;
559 var
->yres
= h
- 2*fb
->y_margin
;
561 var
->bits_per_pixel
= depth
;
562 var
->height
= var
->width
= -1;
563 var
->pixclock
= 10000;
564 var
->vmode
= FB_VMODE_NONINTERLACED
;
565 var
->red
.length
= var
->green
.length
= var
->blue
.length
= 8;
567 fix
->line_length
= linebytes
;
568 fix
->smem_len
= type
->fb_size
;
569 fix
->type
= FB_TYPE_PACKED_PIXELS
;
570 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
572 fb
->info
.fbops
= &sun3fb_ops
;
573 fb
->info
.disp
= disp
;
574 fb
->info
.currcon
= -1;
575 strcpy(fb
->info
.fontname
, fontname
);
576 fb
->info
.changevar
= NULL
;
577 fb
->info
.switch_con
= &sun3fbcon_switch
;
578 fb
->info
.updatevar
= &sun3fbcon_updatevar
;
579 fb
->info
.flags
= FBINFO_FLAG_DEFAULT
;
581 fb
->cursor
.hwsize
.fbx
= 32;
582 fb
->cursor
.hwsize
.fby
= 32;
584 if (depth
> 1 && !fb
->color_map
) {
585 if((fb
->color_map
= kmalloc(256 * 3, GFP_ATOMIC
))==NULL
)
590 #ifdef CONFIG_FB_CGSIX
591 case FBTYPE_SUNFAST_COLOR
:
592 p
= cgsixfb_init(fb
); break;
594 #ifdef CONFIG_FB_BWTWO
596 p
= bwtwofb_init(fb
); break;
598 #ifdef CONFIG_FB_CGTHREE
599 case FBTYPE_SUN4COLOR
:
600 case FBTYPE_SUN3COLOR
:
601 type
->fb_size
= 0x100000;
602 p
= cgthreefb_init(fb
); break;
605 fix
->smem_start
= (unsigned long)fb
->info
.screen_base
; // FIXME
612 if (p
== SBUSFBINIT_SIZECHANGE
)
615 disp
->dispsw
= &fb
->dispsw
;
617 fb
->dispsw
.cursor
= sun3fb_cursor
;
619 fb
->cursor
.blink_rate
= DEFAULT_CURSOR_BLINK_RATE
;
620 init_timer(&fb
->cursor
.timer
);
621 fb
->cursor
.timer
.expires
= jiffies
+ fb
->cursor
.blink_rate
;
622 fb
->cursor
.timer
.data
= (unsigned long)fb
;
623 fb
->cursor
.timer
.function
= sun3fb_cursor_timer_handler
;
624 add_timer(&fb
->cursor
.timer
);
627 fb
->cursor
.mode
= CURSOR_SHAPE
;
628 fb
->dispsw
.set_font
= sun3fb_set_font
;
629 fb
->setup
= fb
->dispsw
.setup
;
630 fb
->dispsw
.setup
= sun3fb_disp_setup
;
631 fb
->dispsw
.clear_margins
= NULL
;
634 disp
->visual
= fix
->visual
;
635 disp
->type
= fix
->type
;
636 disp
->type_aux
= fix
->type_aux
;
637 disp
->line_length
= fix
->line_length
;
640 disp
->can_soft_blank
= 1;
642 sun3fb_set_var(var
, -1, &fb
->info
);
644 if (register_framebuffer(&fb
->info
) < 0) {
648 printk("fb%d: %s\n", fb
->info
.node
, p
);
654 int __init
sun3fb_init(void)
656 extern int con_is_present(void);
660 if (!con_is_present()) return -ENODEV
;
662 switch(*(romvec
->pv_fbtype
))
666 return sun3fb_init_fb(FBTYPE_SUN2BW
, addr
);
667 case FBTYPE_SUN3COLOR
:
668 case FBTYPE_SUN4COLOR
:
669 if(idprom
->id_machtype
!= (SM_SUN3
|SM_3_60
)) {
670 printk("sun3fb: cgthree/four only supported on 3/60\n");
674 addr
= CGFOUR_OBMEM_ADDR
;
675 return sun3fb_init_fb(*(romvec
->pv_fbtype
), addr
);
677 printk("sun3fb: unsupported framebuffer\n");
681 addr
= SUN3X_VIDEO_BASE
;
682 p4id
= *(char *)SUN3X_VIDEO_P4ID
;
684 p4id
= (p4id
== 0x45) ? p4id
: (p4id
& 0xf0);
687 return sun3fb_init_fb(FBTYPE_SUN2BW
, addr
);
690 return sun3fb_init_fb(FBTYPE_SUN4COLOR
, addr
);
693 return sun3fb_init_fb(FBTYPE_SUN8COLOR
, addr
);
697 return sun3fb_init_fb(FBTYPE_SUNFAST_COLOR
, addr
);
704 MODULE_LICENSE("GPL");