2 * linux/drivers/video/sbusfb.c -- SBUS or UPA based frame buffer device
4 * Copyright (C) 1998 Jakub Jelinek
6 * This driver is partly based on the Open Firmware console driver
8 * Copyright (C) 1997 Geert Uytterhoeven
10 * and SPARC console subsystem
12 * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
13 * Copyright (C) 1995-1997 David S. Miller (davem@caip.rutgers.edu)
14 * Copyright (C) 1995-1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
15 * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
16 * Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
17 * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
19 * This file is subject to the terms and conditions of the GNU General Public
20 * License. See the file COPYING in the main directory of this archive for
24 #include <linux/config.h>
25 #include <linux/module.h>
26 #include <linux/kernel.h>
27 #include <linux/errno.h>
28 #include <linux/string.h>
30 #include <linux/tty.h>
31 #include <linux/malloc.h>
32 #include <linux/vmalloc.h>
33 #include <linux/delay.h>
34 #include <linux/interrupt.h>
36 #include <linux/selection.h>
37 #include <linux/init.h>
38 #include <linux/console.h>
40 #include <linux/vt_kern.h>
42 #include <asm/uaccess.h>
43 #include <asm/pgtable.h> /* io_remap_page_range() */
45 #include <video/sbusfb.h>
47 #define DEFAULT_CURSOR_BLINK_RATE (2*HZ/5)
49 #define CURSOR_SHAPE 1
50 #define CURSOR_BLINK 2
53 * Interface used by the world
56 int sbusfb_init(void);
57 int sbusfb_setup(char*);
60 static int defx_margin
= -1, defy_margin
= -1;
61 static char fontname
[40] __initdata
= { 0 };
62 static int curblink __initdata
= 1;
66 int x_margin
, y_margin
;
68 { 8, 1280, 1024, 64, 80 },
69 { 8, 1152, 1024, 64, 80 },
70 { 8, 1152, 900, 64, 18 },
71 { 8, 1024, 768, 0, 0 },
72 { 8, 800, 600, 16, 12 },
73 { 8, 640, 480, 0, 0 },
74 { 1, 1152, 900, 8, 18 },
78 static int sbusfb_open(struct fb_info
*info
, int user
);
79 static int sbusfb_release(struct fb_info
*info
, int user
);
80 static int sbusfb_mmap(struct fb_info
*info
, struct file
*file
,
81 struct vm_area_struct
*vma
);
82 static int sbusfb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
83 struct fb_info
*info
);
84 static int sbusfb_get_var(struct fb_var_screeninfo
*var
, int con
,
85 struct fb_info
*info
);
86 static int sbusfb_set_var(struct fb_var_screeninfo
*var
, int con
,
87 struct fb_info
*info
);
88 static int sbusfb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
89 struct fb_info
*info
);
90 static int sbusfb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
91 struct fb_info
*info
);
92 static int sbusfb_ioctl(struct inode
*inode
, struct file
*file
, u_int cmd
,
93 u_long arg
, int con
, struct fb_info
*info
);
94 static void sbusfb_cursor(struct display
*p
, int mode
, int x
, int y
);
95 static void sbusfb_clear_margin(struct display
*p
, int s
);
99 * Interface to the low level console driver
102 static int sbusfbcon_switch(int con
, struct fb_info
*info
);
103 static int sbusfbcon_updatevar(int con
, struct fb_info
*info
);
104 static void sbusfbcon_blank(int blank
, struct fb_info
*info
);
111 static int sbusfb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
112 u_int
*transp
, struct fb_info
*info
);
113 static int sbusfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
114 u_int transp
, struct fb_info
*info
);
115 static void do_install_cmap(int con
, struct fb_info
*info
);
117 static struct fb_ops sbusfb_ops
= {
119 fb_open
: sbusfb_open
,
120 fb_release
: sbusfb_release
,
121 fb_get_fix
: sbusfb_get_fix
,
122 fb_get_var
: sbusfb_get_var
,
123 fb_set_var
: sbusfb_set_var
,
124 fb_get_cmap
: sbusfb_get_cmap
,
125 fb_set_cmap
: sbusfb_set_cmap
,
126 fb_ioctl
: sbusfb_ioctl
,
127 fb_mmap
: sbusfb_mmap
,
131 * Open/Release the frame buffer device
134 static int sbusfb_open(struct fb_info
*info
, int user
)
136 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
149 static int sbusfb_release(struct fb_info
*info
, int user
)
151 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
156 if (fb
->vtconsole
!= -1) {
157 vt_cons
[fb
->vtconsole
]->vc_mode
= KD_TEXT
;
160 sbusfb_clear_margin(&fb_display
[fb
->vtconsole
], 0);
171 static unsigned long sbusfb_mmapsize(struct fb_info_sbusfb
*fb
, long size
)
173 if (size
== SBUS_MMAP_EMPTY
) return 0;
174 if (size
>= 0) return size
;
175 return fb
->type
.fb_size
* (-size
);
178 static int sbusfb_mmap(struct fb_info
*info
, struct file
*file
,
179 struct vm_area_struct
*vma
)
181 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
182 unsigned int size
, page
, r
, map_size
;
183 unsigned long map_offset
= 0;
187 size
= vma
->vm_end
- vma
->vm_start
;
188 if (vma
->vm_pgoff
> (~0UL >> PAGE_SHIFT
))
191 off
= vma
->vm_pgoff
<< PAGE_SHIFT
;
193 /* To stop the swapper from even considering these pages */
194 vma
->vm_flags
|= (VM_SHM
| VM_LOCKED
);
197 /* Align it as much as desirable */
199 unsigned long j
, alignment
, s
= 0;
202 map_offset
= (vma
->vm_pgoff
<< PAGE_SHIFT
) + size
;
203 for (i
= 0; fb
->mmap_map
[i
].size
; i
++) {
204 if (fb
->mmap_map
[i
].voff
< off
)
206 if (fb
->mmap_map
[i
].voff
>= map_offset
)
208 if (max
< 0 || sbusfb_mmapsize(fb
,fb
->mmap_map
[i
].size
) > s
) {
210 s
= sbusfb_mmapsize(fb
,fb
->mmap_map
[max
].size
);
215 if (fb
->mmap_map
[max
].voff
+ j
> map_offset
)
216 j
= map_offset
- fb
->mmap_map
[max
].voff
;
217 for (alignment
= 0x400000; alignment
> PAGE_SIZE
; alignment
>>= 3)
218 if (j
>= alignment
&& !(fb
->mmap_map
[max
].poff
& (alignment
- 1)))
220 if (alignment
> PAGE_SIZE
) {
222 alignment
= j
- ((vma
->vm_start
+ fb
->mmap_map
[max
].voff
- off
) & (j
- 1));
223 if (alignment
!= j
) {
224 struct vm_area_struct
*vmm
= find_vma(current
->mm
, vma
->vm_start
);
225 if (!vmm
|| vmm
->vm_start
>= vma
->vm_end
+ alignment
) {
226 vma
->vm_start
+= alignment
;
227 vma
->vm_end
+= alignment
;
235 /* Each page, see which map applies */
236 for (page
= 0; page
< size
; ){
238 for (i
= 0; fb
->mmap_map
[i
].size
; i
++)
239 if (fb
->mmap_map
[i
].voff
== off
+page
) {
240 map_size
= sbusfb_mmapsize(fb
,fb
->mmap_map
[i
].size
);
242 #define POFF_MASK (PAGE_MASK|0x1UL)
244 #define POFF_MASK (PAGE_MASK)
246 map_offset
= (fb
->physbase
+ fb
->mmap_map
[i
].poff
) & POFF_MASK
;
253 if (page
+ map_size
> size
)
254 map_size
= size
- page
;
255 r
= io_remap_page_range (vma
->vm_start
+page
, map_offset
, map_size
, vma
->vm_page_prot
, fb
->iospace
);
261 vma
->vm_flags
|= VM_IO
;
265 if (info
->display_fg
)
266 lastconsole
= info
->display_fg
->vc_num
;
268 if (fb
->consolecnt
&& fb_display
[lastconsole
].fb_info
== info
) {
269 fb
->vtconsole
= lastconsole
;
271 vt_cons
[lastconsole
]->vc_mode
= KD_GRAPHICS
;
272 vc_cons
[lastconsole
].d
->vc_sw
->con_cursor(vc_cons
[lastconsole
].d
,CM_ERASE
);
273 } else if (fb
->unblank
&& !fb
->blanked
)
279 static void sbusfb_clear_margin(struct display
*p
, int s
)
281 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
283 if (fb
->switch_from_graph
)
284 (*fb
->switch_from_graph
)(fb
);
286 unsigned short rects
[16];
290 rects
[2] = fb
->var
.xres_virtual
;
291 rects
[3] = fb
->y_margin
;
293 rects
[5] = fb
->y_margin
;
294 rects
[6] = fb
->x_margin
;
295 rects
[7] = fb
->var
.yres_virtual
;
296 rects
[8] = fb
->var
.xres_virtual
- fb
->x_margin
;
297 rects
[9] = fb
->y_margin
;
298 rects
[10] = fb
->var
.xres_virtual
;
299 rects
[11] = fb
->var
.yres_virtual
;
300 rects
[12] = fb
->x_margin
;
301 rects
[13] = fb
->var
.yres_virtual
- fb
->y_margin
;
302 rects
[14] = fb
->var
.xres_virtual
- fb
->x_margin
;
303 rects
[15] = fb
->var
.yres_virtual
;
304 (*fb
->fill
)(fb
, p
, s
, 4, rects
);
306 unsigned char *fb_base
= p
->screen_base
, *q
;
307 int skip_bytes
= fb
->y_margin
* fb
->var
.xres_virtual
;
308 int scr_size
= fb
->var
.xres_virtual
* fb
->var
.yres_virtual
;
309 int h
, he
, incr
, size
;
312 if (fb
->var
.bits_per_pixel
== 1) {
313 fb_base
-= (skip_bytes
+ fb
->x_margin
) / 8;
316 fb_memset255 (fb_base
, skip_bytes
- fb
->x_margin
/ 8);
317 fb_memset255 (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
/ 8, skip_bytes
- fb
->x_margin
/ 8);
318 incr
= fb
->var
.xres_virtual
/ 8;
319 size
= fb
->x_margin
/ 8 * 2;
320 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
/ 8, h
= 0;
321 h
<= he
; q
+= incr
, h
++)
322 fb_memset255 (q
, size
);
324 fb_base
-= (skip_bytes
+ fb
->x_margin
);
325 memset (fb_base
, attr_bgcol(p
,s
), skip_bytes
- fb
->x_margin
);
326 memset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
, attr_bgcol(p
,s
), skip_bytes
- fb
->x_margin
);
327 incr
= fb
->var
.xres_virtual
;
328 size
= fb
->x_margin
* 2;
329 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
, h
= 0;
330 h
<= he
; q
+= incr
, h
++)
331 memset (q
, attr_bgcol(p
,s
), size
);
336 static void sbusfb_disp_setup(struct display
*p
)
338 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
342 sbusfb_clear_margin(p
, 0);
346 * Get the Fixed Part of the Display
349 static int sbusfb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
350 struct fb_info
*info
)
352 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
354 memcpy(fix
, &fb
->fix
, sizeof(struct fb_fix_screeninfo
));
359 * Get the User Defined Part of the Display
362 static int sbusfb_get_var(struct fb_var_screeninfo
*var
, int con
,
363 struct fb_info
*info
)
365 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
367 memcpy(var
, &fb
->var
, sizeof(struct fb_var_screeninfo
));
372 * Set the User Defined Part of the Display
375 static int sbusfb_set_var(struct fb_var_screeninfo
*var
, int con
,
376 struct fb_info
*info
)
378 struct display
*display
;
379 int activate
= var
->activate
;
382 display
= &fb_display
[con
];
384 display
= info
->disp
;
386 /* simple check for equality until fully implemented -E */
387 if ((activate
& FB_ACTIVATE_MASK
) == FB_ACTIVATE_NOW
) {
388 if (display
->var
.xres
!= var
->xres
||
389 display
->var
.yres
!= var
->yres
||
390 display
->var
.xres_virtual
!= var
->xres_virtual
||
391 display
->var
.yres_virtual
!= var
->yres_virtual
||
392 display
->var
.bits_per_pixel
!= var
->bits_per_pixel
||
393 display
->var
.accel_flags
!= var
->accel_flags
) {
405 static int sbus_hw_scursor (struct fbcursor
*cursor
, struct fb_info_sbusfb
*fb
)
410 char red
[2], green
[2], blue
[2];
412 if (copy_from_user (&f
, cursor
, sizeof(struct fbcursor
)))
415 if (op
& FB_CUR_SETSHAPE
){
416 if ((u32
) f
.size
.fbx
> fb
->cursor
.hwsize
.fbx
)
418 if ((u32
) f
.size
.fby
> fb
->cursor
.hwsize
.fby
)
421 bytes
= f
.size
.fby
<< 3;
423 bytes
= f
.size
.fby
<< 2;
425 if (op
& FB_CUR_SETCMAP
){
426 if (f
.cmap
.index
|| f
.cmap
.count
!= 2)
428 if (copy_from_user (red
, f
.cmap
.red
, 2) ||
429 copy_from_user (green
, f
.cmap
.green
, 2) ||
430 copy_from_user (blue
, f
.cmap
.blue
, 2))
433 if (op
& FB_CUR_SETCMAP
)
434 (*fb
->setcursormap
) (fb
, red
, green
, blue
);
435 if (op
& FB_CUR_SETSHAPE
){
438 fb
->cursor
.size
= f
.size
;
439 memset ((void *)&fb
->cursor
.bits
, 0, sizeof (fb
->cursor
.bits
));
440 if (copy_from_user (fb
->cursor
.bits
[0], f
.mask
, bytes
) ||
441 copy_from_user (fb
->cursor
.bits
[1], f
.image
, bytes
))
443 if (f
.size
.fbx
<= 32) {
444 u
= 0xffffffff << (32 - f
.size
.fbx
);
445 for (i
= fb
->cursor
.size
.fby
- 1; i
>= 0; i
--) {
446 fb
->cursor
.bits
[0][i
] &= u
;
447 fb
->cursor
.bits
[1][i
] &= fb
->cursor
.bits
[0][i
];
450 u
= 0xffffffff << (64 - f
.size
.fbx
);
451 for (i
= fb
->cursor
.size
.fby
- 1; i
>= 0; i
--) {
452 fb
->cursor
.bits
[0][2*i
+1] &= u
;
453 fb
->cursor
.bits
[1][2*i
] &= fb
->cursor
.bits
[0][2*i
];
454 fb
->cursor
.bits
[1][2*i
+1] &= fb
->cursor
.bits
[0][2*i
+1];
457 (*fb
->setcurshape
) (fb
);
459 if (op
& (FB_CUR_SETCUR
| FB_CUR_SETPOS
| FB_CUR_SETHOT
)){
460 if (op
& FB_CUR_SETCUR
)
461 fb
->cursor
.enable
= f
.enable
;
462 if (op
& FB_CUR_SETPOS
)
463 fb
->cursor
.cpos
= f
.pos
;
464 if (op
& FB_CUR_SETHOT
)
465 fb
->cursor
.chot
= f
.hot
;
466 (*fb
->setcursor
) (fb
);
471 static unsigned char hw_cursor_cmap
[2] = { 0, 0xff };
474 sbusfb_cursor_timer_handler(unsigned long dev_addr
)
476 struct fb_info_sbusfb
*fb
= (struct fb_info_sbusfb
*)dev_addr
;
478 if (!fb
->setcursor
) return;
480 if (fb
->cursor
.mode
& CURSOR_BLINK
) {
481 fb
->cursor
.enable
^= 1;
485 fb
->cursor
.timer
.expires
= jiffies
+ fb
->cursor
.blink_rate
;
486 add_timer(&fb
->cursor
.timer
);
489 static void sbusfb_cursor(struct display
*p
, int mode
, int x
, int y
)
491 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
495 fb
->cursor
.mode
&= ~CURSOR_BLINK
;
496 fb
->cursor
.enable
= 0;
497 (*fb
->setcursor
)(fb
);
502 if (fb
->cursor
.mode
& CURSOR_SHAPE
) {
503 fb
->cursor
.size
.fbx
= fontwidth(p
);
504 fb
->cursor
.size
.fby
= fontheight(p
);
505 fb
->cursor
.chot
.fbx
= 0;
506 fb
->cursor
.chot
.fby
= 0;
507 fb
->cursor
.enable
= 1;
508 memset (fb
->cursor
.bits
, 0, sizeof (fb
->cursor
.bits
));
509 fb
->cursor
.bits
[0][fontheight(p
) - 2] = (0xffffffff << (32 - fontwidth(p
)));
510 fb
->cursor
.bits
[1][fontheight(p
) - 2] = (0xffffffff << (32 - fontwidth(p
)));
511 fb
->cursor
.bits
[0][fontheight(p
) - 1] = (0xffffffff << (32 - fontwidth(p
)));
512 fb
->cursor
.bits
[1][fontheight(p
) - 1] = (0xffffffff << (32 - fontwidth(p
)));
513 (*fb
->setcursormap
) (fb
, hw_cursor_cmap
, hw_cursor_cmap
, hw_cursor_cmap
);
514 (*fb
->setcurshape
) (fb
);
516 fb
->cursor
.mode
= CURSOR_BLINK
;
518 fb
->cursor
.cpos
.fbx
= (x
<< fontwidthlog(p
)) + fb
->x_margin
;
520 fb
->cursor
.cpos
.fbx
= (x
* fontwidth(p
)) + fb
->x_margin
;
521 if (fontheightlog(p
))
522 fb
->cursor
.cpos
.fby
= (y
<< fontheightlog(p
)) + fb
->y_margin
;
524 fb
->cursor
.cpos
.fby
= (y
* fontheight(p
)) + fb
->y_margin
;
525 (*fb
->setcursor
)(fb
);
534 static int sbusfb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
535 struct fb_info
*info
)
537 if (!info
->display_fg
|| con
== info
->display_fg
->vc_num
) /* current console? */
538 return fb_get_cmap(cmap
, kspc
, sbusfb_getcolreg
, info
);
539 else if (fb_display
[con
].cmap
.len
) /* non default colormap? */
540 fb_copy_cmap(&fb_display
[con
].cmap
, cmap
, kspc
? 0 : 2);
542 fb_copy_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
), cmap
, kspc
? 0 : 2);
550 static int sbusfb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
551 struct fb_info
*info
)
554 struct display
*disp
;
557 disp
= &fb_display
[con
];
560 if (!disp
->cmap
.len
) { /* no colormap allocated? */
561 if ((err
= fb_alloc_cmap(&disp
->cmap
, 1<<disp
->var
.bits_per_pixel
, 0)))
564 if (con
== currcon
) { /* current console? */
565 err
= fb_set_cmap(cmap
, kspc
, sbusfb_setcolreg
, info
);
567 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
570 (*fb
->loadcmap
)(fb
, &fb_display
[con
], cmap
->start
, cmap
->len
);
574 fb_copy_cmap(cmap
, &disp
->cmap
, kspc
? 0 : 1);
578 static int sbusfb_ioctl(struct inode
*inode
, struct file
*file
, u_int cmd
,
579 u_long arg
, int con
, struct fb_info
*info
)
581 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
586 case FBIOGTYPE
: /* return frame buffer type */
587 if (copy_to_user((struct fbtype
*)arg
, &fb
->type
, sizeof(struct fbtype
)))
591 struct fbgattr
*fba
= (struct fbgattr
*) arg
;
593 i
= verify_area (VERIFY_WRITE
, (void *) arg
, sizeof (struct fbgattr
));
595 if (__put_user(fb
->emulations
[0], &fba
->real_type
) ||
596 __put_user(0, &fba
->owner
) ||
597 __copy_to_user(&fba
->fbtype
, &fb
->type
,
598 sizeof(struct fbtype
)) ||
599 __put_user(0, &fba
->sattr
.flags
) ||
600 __put_user(fb
->type
.fb_type
, &fba
->sattr
.emu_type
) ||
601 __put_user(-1, &fba
->sattr
.dev_specific
[0]))
603 for (i
= 0; i
< 4; i
++) {
604 if (put_user(fb
->emulations
[i
], &fba
->emu_types
[i
]))
610 i
= verify_area (VERIFY_READ
, (void *) arg
, sizeof (struct fbsattr
));
614 if (fb
->consolecnt
) {
615 lastconsole
= info
->display_fg
->vc_num
;
616 if (vt_cons
[lastconsole
]->vc_mode
== KD_TEXT
)
619 if (get_user(i
, (int *)arg
))
622 if (!fb
->blanked
|| !fb
->unblank
)
624 if (fb
->consolecnt
|| (fb
->open
&& fb
->mmaped
))
628 if (fb
->blanked
|| !fb
->blank
)
635 if (put_user(fb
->blanked
, (int *) arg
))
638 case FBIOGETCMAP_SPARC
: {
640 int end
, count
, index
;
645 i
= verify_area (VERIFY_READ
, (void *) arg
, sizeof (struct fbcmap
));
647 cmap
= (struct fbcmap
*) arg
;
648 if (__get_user(count
, &cmap
->count
) ||
649 __get_user(index
, &cmap
->index
))
651 if ((index
< 0) || (index
> 255))
653 if (index
+ count
> 256)
655 if (__get_user(rp
, &cmap
->red
) ||
656 __get_user(gp
, &cmap
->green
) ||
657 __get_user(bp
, &cmap
->blue
))
659 if (verify_area (VERIFY_WRITE
, rp
, count
))
661 if (verify_area (VERIFY_WRITE
, gp
, count
))
663 if (verify_area (VERIFY_WRITE
, bp
, count
))
666 for (i
= index
; i
< end
; i
++){
667 if (__put_user(fb
->color_map
CM(i
,0), rp
) ||
668 __put_user(fb
->color_map
CM(i
,1), gp
) ||
669 __put_user(fb
->color_map
CM(i
,2), bp
))
673 (*fb
->loadcmap
)(fb
, NULL
, index
, count
);
676 case FBIOPUTCMAP_SPARC
: { /* load color map entries */
678 int end
, count
, index
;
683 i
= verify_area (VERIFY_READ
, (void *) arg
, sizeof (struct fbcmap
));
685 cmap
= (struct fbcmap
*) arg
;
686 if (__get_user(count
, &cmap
->count
) ||
687 __get_user(index
, &cmap
->index
))
689 if ((index
< 0) || (index
> 255))
691 if (index
+ count
> 256)
693 if (__get_user(rp
, &cmap
->red
) ||
694 __get_user(gp
, &cmap
->green
) ||
695 __get_user(bp
, &cmap
->blue
))
697 if (verify_area (VERIFY_READ
, rp
, count
))
699 if (verify_area (VERIFY_READ
, gp
, count
))
701 if (verify_area (VERIFY_READ
, bp
, count
))
705 for (i
= index
; i
< end
; i
++){
706 if (__get_user(fb
->color_map
CM(i
,0), rp
))
708 if (__get_user(fb
->color_map
CM(i
,1), gp
))
710 if (__get_user(fb
->color_map
CM(i
,2), bp
))
714 (*fb
->loadcmap
)(fb
, NULL
, index
, count
);
718 struct fbcurpos
*p
= (struct fbcurpos
*) arg
;
719 if (!fb
->setcursor
) return -EINVAL
;
720 if(verify_area (VERIFY_WRITE
, p
, sizeof (struct fbcurpos
)))
722 if (__put_user(fb
->cursor
.hwsize
.fbx
, &p
->fbx
) ||
723 __put_user(fb
->cursor
.hwsize
.fby
, &p
->fby
))
728 if (!fb
->setcursor
) return -EINVAL
;
729 if (fb
->consolecnt
) {
730 lastconsole
= info
->display_fg
->vc_num
;
731 if (vt_cons
[lastconsole
]->vc_mode
== KD_TEXT
)
732 return -EINVAL
; /* Don't let graphics programs hide our nice text cursor */
733 fb
->cursor
.mode
= CURSOR_SHAPE
; /* Forget state of our text cursor */
735 return sbus_hw_scursor ((struct fbcursor
*) arg
, fb
);
738 if (!fb
->setcursor
) return -EINVAL
;
739 /* Don't let graphics programs move our nice text cursor */
740 if (fb
->consolecnt
) {
741 lastconsole
= info
->display_fg
->vc_num
;
742 if (vt_cons
[lastconsole
]->vc_mode
== KD_TEXT
)
743 return -EINVAL
; /* Don't let graphics programs move our nice text cursor */
745 if (copy_from_user(&fb
->cursor
.cpos
, (void *)arg
, sizeof(struct fbcurpos
)))
747 (*fb
->setcursor
) (fb
);
751 return fb
->ioctl(fb
, cmd
, arg
);
758 * Setup: parse used options
761 int __init
sbusfb_setup(char *options
)
765 for (p
= options
;;) {
766 if (!strncmp(p
, "nomargins", 9)) {
767 defx_margin
= 0; defy_margin
= 0;
768 } else if (!strncmp(p
, "margins=", 8)) {
772 i
= simple_strtoul(p
+8,&q
,10);
773 if (i
>= 0 && *q
== 'x') {
774 j
= simple_strtoul(q
+1,&q
,10);
775 if (j
>= 0 && (*q
== ' ' || !*q
)) {
776 defx_margin
= i
; defy_margin
= j
;
779 } else if (!strncmp(p
, "font=", 5)) {
782 for (i
= 0; i
< sizeof(fontname
) - 1; i
++)
783 if (p
[i
+5] == ' ' || !p
[i
+5])
785 memcpy(fontname
, p
+5, i
);
787 } else if (!strncmp(p
, "noblink", 7))
789 while (*p
&& *p
!= ' ' && *p
!= ',') p
++;
790 if (*p
!= ',') break;
796 static int sbusfbcon_switch(int con
, struct fb_info
*info
)
798 int x_margin
, y_margin
;
799 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
802 /* Do we have to save the colormap? */
803 if (fb_display
[currcon
].cmap
.len
)
804 fb_get_cmap(&fb_display
[currcon
].cmap
, 1, sbusfb_getcolreg
, info
);
806 if (info
->display_fg
) {
807 lastconsole
= info
->display_fg
->vc_num
;
808 if (lastconsole
!= con
&&
809 (fontwidth(&fb_display
[lastconsole
]) != fontwidth(&fb_display
[con
]) ||
810 fontheight(&fb_display
[lastconsole
]) != fontheight(&fb_display
[con
])))
811 fb
->cursor
.mode
|= CURSOR_SHAPE
;
813 x_margin
= (fb_display
[con
].var
.xres_virtual
- fb_display
[con
].var
.xres
) / 2;
814 y_margin
= (fb_display
[con
].var
.yres_virtual
- fb_display
[con
].var
.yres
) / 2;
816 fb
->margins(fb
, &fb_display
[con
], x_margin
, y_margin
);
817 if (fb
->graphmode
|| fb
->x_margin
!= x_margin
|| fb
->y_margin
!= y_margin
) {
818 fb
->x_margin
= x_margin
; fb
->y_margin
= y_margin
;
819 sbusfb_clear_margin(&fb_display
[con
], 0);
822 /* Install new colormap */
823 do_install_cmap(con
, info
);
828 * Update the `var' structure (called by fbcon.c)
831 static int sbusfbcon_updatevar(int con
, struct fb_info
*info
)
841 static void sbusfbcon_blank(int blank
, struct fb_info
*info
)
843 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
845 if (blank
&& fb
->blank
)
846 return fb
->blank(fb
);
847 else if (!blank
&& fb
->unblank
)
848 return fb
->unblank(fb
);
852 * Read a single color register and split it into
853 * colors/transparent. Return != 0 for invalid regno.
856 static int sbusfb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
857 u_int
*transp
, struct fb_info
*info
)
859 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
861 if (!fb
->color_map
|| regno
> 255)
863 *red
= (fb
->color_map
CM(regno
, 0)<<8) | fb
->color_map
CM(regno
, 0);
864 *green
= (fb
->color_map
CM(regno
, 1)<<8) | fb
->color_map
CM(regno
, 1);
865 *blue
= (fb
->color_map
CM(regno
, 2)<<8) | fb
->color_map
CM(regno
, 2);
872 * Set a single color register. The values supplied are already
873 * rounded down to the hardware's capabilities (according to the
874 * entries in the var structure). Return != 0 for invalid regno.
877 static int sbusfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
878 u_int transp
, struct fb_info
*info
)
880 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
882 if (!fb
->color_map
|| regno
> 255)
887 fb
->color_map
CM(regno
, 0) = red
;
888 fb
->color_map
CM(regno
, 1) = green
;
889 fb
->color_map
CM(regno
, 2) = blue
;
894 static void do_install_cmap(int con
, struct fb_info
*info
)
896 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
900 if (fb_display
[con
].cmap
.len
)
901 fb_set_cmap(&fb_display
[con
].cmap
, 1, sbusfb_setcolreg
, info
);
903 fb_set_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
),
904 1, sbusfb_setcolreg
, info
);
906 (*fb
->loadcmap
)(fb
, &fb_display
[con
], 0, 256);
909 static int sbusfb_set_font(struct display
*p
, int width
, int height
)
912 int w
= p
->var
.xres_virtual
, h
= p
->var
.yres_virtual
;
913 int depth
= p
->var
.bits_per_pixel
;
914 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
915 int x_margin
, y_margin
;
917 if (depth
> 8) depth
= 8;
920 if (defx_margin
< 0 || defy_margin
< 0) {
921 for (margin
= 0; def_margins
[margin
].depth
; margin
++)
922 if (w
== def_margins
[margin
].xres
&&
923 h
== def_margins
[margin
].yres
&&
924 depth
== def_margins
[margin
].depth
) {
925 x_margin
= def_margins
[margin
].x_margin
;
926 y_margin
= def_margins
[margin
].y_margin
;
930 x_margin
= defx_margin
;
931 y_margin
= defy_margin
;
933 x_margin
+= ((w
- 2*x_margin
) % width
) / 2;
934 y_margin
+= ((h
- 2*y_margin
) % height
) / 2;
936 p
->var
.xres
= w
- 2*x_margin
;
937 p
->var
.yres
= h
- 2*y_margin
;
939 fb
->cursor
.mode
|= CURSOR_SHAPE
;
942 fb
->margins(fb
, p
, x_margin
, y_margin
);
943 if (fb
->x_margin
!= x_margin
|| fb
->y_margin
!= y_margin
) {
944 fb
->x_margin
= x_margin
; fb
->y_margin
= y_margin
;
945 sbusfb_clear_margin(p
, 0);
951 void sbusfb_palette(int enter
)
956 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
958 if (p
->dispsw
&& p
->dispsw
->setup
== sbusfb_disp_setup
&&
959 p
->fb_info
->display_fg
&&
960 p
->fb_info
->display_fg
->vc_num
== i
) {
961 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
963 if (fb
->restore_palette
) {
965 fb
->restore_palette(fb
);
966 else if (vt_cons
[i
]->vc_mode
!= KD_GRAPHICS
)
967 vc_cons
[i
].d
->vc_sw
->con_set_palette(vc_cons
[i
].d
, color_table
);
977 extern void (*prom_palette
)(int);
979 static void __init
sbusfb_init_fb(int node
, int parent
, int fbtype
,
980 struct sbus_dev
*sbdp
)
982 struct fb_fix_screeninfo
*fix
;
983 struct fb_var_screeninfo
*var
;
984 struct display
*disp
;
985 struct fb_info_sbusfb
*fb
;
987 int linebytes
, w
, h
, depth
;
991 fb
= kmalloc(sizeof(struct fb_info_sbusfb
), GFP_ATOMIC
);
993 prom_printf("Could not allocate sbusfb structure\n");
998 prom_palette
= sbusfb_palette
;
1000 memset(fb
, 0, sizeof(struct fb_info_sbusfb
));
1006 spin_lock_init(&fb
->lock
);
1007 fb
->prom_node
= node
;
1008 fb
->prom_parent
= parent
;
1011 fb
->iospace
= sbdp
->reg_addrs
[0].which_io
;
1013 type
->fb_type
= fbtype
;
1014 memset(&fb
->emulations
, 0xff, sizeof(fb
->emulations
));
1015 fb
->emulations
[0] = fbtype
;
1017 #ifndef __sparc_v9__
1018 disp
->screen_base
= (unsigned char *)prom_getintdefault(node
, "address", 0);
1021 type
->fb_height
= h
= prom_getintdefault(node
, "height", 900);
1022 type
->fb_width
= w
= prom_getintdefault(node
, "width", 1152);
1024 type
->fb_depth
= depth
= (fbtype
== FBTYPE_SUN2BW
) ? 1 : 8;
1025 linebytes
= prom_getintdefault(node
, "linebytes", w
* depth
/ 8);
1026 type
->fb_size
= PAGE_ALIGN((linebytes
) * h
);
1028 if (defx_margin
< 0 || defy_margin
< 0) {
1029 for (margin
= 0; def_margins
[margin
].depth
; margin
++)
1030 if (w
== def_margins
[margin
].xres
&&
1031 h
== def_margins
[margin
].yres
&&
1032 depth
== def_margins
[margin
].depth
) {
1033 fb
->x_margin
= def_margins
[margin
].x_margin
;
1034 fb
->y_margin
= def_margins
[margin
].y_margin
;
1038 fb
->x_margin
= defx_margin
;
1039 fb
->y_margin
= defy_margin
;
1041 fb
->x_margin
+= ((w
- 2*fb
->x_margin
) & 7) / 2;
1042 fb
->y_margin
+= ((h
- 2*fb
->y_margin
) & 15) / 2;
1044 var
->xres_virtual
= w
;
1045 var
->yres_virtual
= h
;
1046 var
->xres
= w
- 2*fb
->x_margin
;
1047 var
->yres
= h
- 2*fb
->y_margin
;
1049 var
->bits_per_pixel
= depth
;
1050 var
->height
= var
->width
= -1;
1051 var
->pixclock
= 10000;
1052 var
->vmode
= FB_VMODE_NONINTERLACED
;
1053 var
->red
.length
= var
->green
.length
= var
->blue
.length
= 8;
1055 fix
->line_length
= linebytes
;
1056 fix
->smem_len
= type
->fb_size
;
1057 fix
->type
= FB_TYPE_PACKED_PIXELS
;
1058 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
1061 fb
->info
.fbops
= &sbusfb_ops
;
1062 fb
->info
.disp
= disp
;
1063 strcpy(fb
->info
.fontname
, fontname
);
1064 fb
->info
.changevar
= NULL
;
1065 fb
->info
.switch_con
= &sbusfbcon_switch
;
1066 fb
->info
.updatevar
= &sbusfbcon_updatevar
;
1067 fb
->info
.blank
= &sbusfbcon_blank
;
1068 fb
->info
.flags
= FBINFO_FLAG_DEFAULT
;
1070 fb
->cursor
.hwsize
.fbx
= 32;
1071 fb
->cursor
.hwsize
.fby
= 32;
1073 if (depth
> 1 && !fb
->color_map
)
1074 fb
->color_map
= kmalloc(256 * 3, GFP_ATOMIC
);
1077 #ifdef CONFIG_FB_CREATOR
1078 case FBTYPE_CREATOR
:
1079 p
= creatorfb_init(fb
); break;
1081 #ifdef CONFIG_FB_CGSIX
1082 case FBTYPE_SUNFAST_COLOR
:
1083 p
= cgsixfb_init(fb
); break;
1085 #ifdef CONFIG_FB_CGTHREE
1086 case FBTYPE_SUN3COLOR
:
1087 p
= cgthreefb_init(fb
); break;
1089 #ifdef CONFIG_FB_TCX
1090 case FBTYPE_TCXCOLOR
:
1091 p
= tcxfb_init(fb
); break;
1093 #ifdef CONFIG_FB_LEO
1095 p
= leofb_init(fb
); break;
1097 #ifdef CONFIG_FB_BWTWO
1099 p
= bwtwofb_init(fb
); break;
1101 #ifdef CONFIG_FB_CGFOURTEEN
1102 case FBTYPE_MDICOLOR
:
1103 p
= cgfourteenfb_init(fb
); break;
1105 #ifdef CONFIG_FB_P9100
1106 case FBTYPE_P9100COLOR
:
1107 /* Temporary crock. For now we are a cg3 */
1108 p
= p9100fb_init(fb
); type
->fb_type
= FBTYPE_SUN3COLOR
; break;
1117 if (p
== SBUSFBINIT_SIZECHANGE
)
1120 disp
->dispsw
= &fb
->dispsw
;
1121 if (fb
->setcursor
) {
1122 fb
->dispsw
.cursor
= sbusfb_cursor
;
1124 fb
->cursor
.blink_rate
= DEFAULT_CURSOR_BLINK_RATE
;
1125 init_timer(&fb
->cursor
.timer
);
1126 fb
->cursor
.timer
.expires
= jiffies
+ fb
->cursor
.blink_rate
;
1127 fb
->cursor
.timer
.data
= (unsigned long)fb
;
1128 fb
->cursor
.timer
.function
= sbusfb_cursor_timer_handler
;
1129 add_timer(&fb
->cursor
.timer
);
1132 fb
->cursor
.mode
= CURSOR_SHAPE
;
1133 fb
->dispsw
.set_font
= sbusfb_set_font
;
1134 fb
->setup
= fb
->dispsw
.setup
;
1135 fb
->dispsw
.setup
= sbusfb_disp_setup
;
1136 fb
->dispsw
.clear_margins
= NULL
;
1139 disp
->visual
= fix
->visual
;
1140 disp
->type
= fix
->type
;
1141 disp
->type_aux
= fix
->type_aux
;
1142 disp
->line_length
= fix
->line_length
;
1145 disp
->can_soft_blank
= 1;
1147 sbusfb_set_var(var
, -1, &fb
->info
);
1149 if (register_framebuffer(&fb
->info
) < 0) {
1153 printk(KERN_INFO
"fb%d: %s\n", GET_FB_IDX(fb
->info
.node
), p
);
1156 static inline int known_card(char *name
)
1159 for (p
= name
; *p
&& *p
!= ','; p
++);
1160 if (*p
== ',') name
= p
+ 1;
1161 if (!strcmp(name
, "cgsix") || !strcmp(name
, "cgthree+"))
1162 return FBTYPE_SUNFAST_COLOR
;
1163 if (!strcmp(name
, "cgthree") || !strcmp(name
, "cgRDI"))
1164 return FBTYPE_SUN3COLOR
;
1165 if (!strcmp(name
, "cgfourteen"))
1166 return FBTYPE_MDICOLOR
;
1167 if (!strcmp(name
, "leo"))
1168 return FBTYPE_SUNLEO
;
1169 if (!strcmp(name
, "bwtwo"))
1170 return FBTYPE_SUN2BW
;
1171 if (!strcmp(name
, "tcx"))
1172 return FBTYPE_TCXCOLOR
;
1173 if (!strcmp(name
, "p9100"))
1174 return FBTYPE_P9100COLOR
;
1175 return FBTYPE_NOTYPE
;
1178 int __init
sbusfb_init(void)
1181 struct sbus_dev
*sbdp
;
1182 struct sbus_bus
*sbus
;
1184 extern int con_is_present(void);
1186 if (!con_is_present()) return -ENXIO
;
1188 #ifdef CONFIG_FB_CREATOR
1191 root
= prom_getchild(prom_root_node
);
1192 for (node
= prom_searchsiblings(root
, "SUNW,ffb"); node
;
1193 node
= prom_searchsiblings(prom_getsibling(node
), "SUNW,ffb"))
1194 sbusfb_init_fb(node
, prom_root_node
, FBTYPE_CREATOR
, NULL
);
1195 for (node
= prom_searchsiblings(root
, "SUNW,afb"); node
;
1196 node
= prom_searchsiblings(prom_getsibling(node
), "SUNW,afb"))
1197 sbusfb_init_fb(node
, prom_root_node
, FBTYPE_CREATOR
, NULL
);
1201 sbusfb_init_fb(0, 0, FBTYPE_SUN2BW
, NULL
);
1203 #if defined(CONFIG_FB_CGFOURTEEN) && !defined(__sparc_v9__)
1206 root
= prom_getchild(prom_root_node
);
1207 root
= prom_searchsiblings(root
, "obio");
1209 (node
= prom_searchsiblings(prom_getchild(root
), "cgfourteen"))) {
1210 sbusfb_init_fb(node
, root
, FBTYPE_MDICOLOR
, NULL
);
1214 if (sbus_root
== NULL
)
1216 for_all_sbusdev(sbdp
, sbus
) {
1217 type
= known_card(sbdp
->prom_name
);
1218 if (type
== FBTYPE_NOTYPE
)
1220 if (prom_getproperty(sbdp
->prom_node
, "emulation",
1221 prom_name
, sizeof(prom_name
)) > 0) {
1222 type
= known_card(prom_name
);
1223 if (type
== FBTYPE_NOTYPE
)
1224 type
= known_card(sbdp
->prom_name
);
1226 sbusfb_init_fb(sbdp
->prom_node
, sbdp
->bus
->prom_node
, type
, sbdp
);