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 void sbusfb_init(void);
57 void sbusfb_setup(char *options
, int *ints
);
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_pan_display(struct fb_var_screeninfo
*var
, int con
,
89 struct fb_info
*info
);
90 static int sbusfb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
91 struct fb_info
*info
);
92 static int sbusfb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
93 struct fb_info
*info
);
94 static int sbusfb_ioctl(struct inode
*inode
, struct file
*file
, u_int cmd
,
95 u_long arg
, int con
, struct fb_info
*info
);
96 static void sbusfb_cursor(struct display
*p
, int mode
, int x
, int y
);
97 static void sbusfb_clear_margin(struct display
*p
, int s
);
101 * Interface to the low level console driver
104 static int sbusfbcon_switch(int con
, struct fb_info
*info
);
105 static int sbusfbcon_updatevar(int con
, struct fb_info
*info
);
106 static void sbusfbcon_blank(int blank
, struct fb_info
*info
);
113 static int sbusfb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
114 u_int
*transp
, struct fb_info
*info
);
115 static int sbusfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
116 u_int transp
, struct fb_info
*info
);
117 static void do_install_cmap(int con
, struct fb_info
*info
);
119 static struct fb_ops sbusfb_ops
= {
120 sbusfb_open
, sbusfb_release
, sbusfb_get_fix
, sbusfb_get_var
, sbusfb_set_var
,
121 sbusfb_get_cmap
, sbusfb_set_cmap
, sbusfb_pan_display
, sbusfb_ioctl
, sbusfb_mmap
126 * Open/Release the frame buffer device
129 static int sbusfb_open(struct fb_info
*info
, int user
)
131 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
134 if (fb
->open
) return -EBUSY
;
144 static int sbusfb_release(struct fb_info
*info
, int user
)
146 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
149 if (fb
->vtconsole
!= -1) {
150 vt_cons
[fb
->vtconsole
]->vc_mode
= KD_TEXT
;
153 sbusfb_clear_margin(&fb_display
[fb
->vtconsole
], 0);
165 static unsigned long sbusfb_mmapsize(struct fb_info_sbusfb
*fb
, long size
)
167 if (size
== SBUS_MMAP_EMPTY
) return 0;
168 if (size
>= 0) return size
;
169 return fb
->type
.fb_size
* (-size
);
172 static int sbusfb_mmap(struct fb_info
*info
, struct file
*file
,
173 struct vm_area_struct
*vma
)
175 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
176 unsigned int size
, page
, r
, map_size
;
177 unsigned long map_offset
= 0;
180 size
= vma
->vm_end
- vma
->vm_start
;
181 if (vma
->vm_offset
& ~PAGE_MASK
)
184 /* To stop the swapper from even considering these pages */
185 vma
->vm_flags
|= (VM_SHM
| VM_LOCKED
);
188 /* Align it as much as desirable */
190 unsigned long j
, alignment
, s
= 0;
193 map_offset
= vma
->vm_offset
+size
;
194 for (i
= 0; fb
->mmap_map
[i
].size
; i
++) {
195 if (fb
->mmap_map
[i
].voff
< vma
->vm_offset
)
197 if (fb
->mmap_map
[i
].voff
>= map_offset
)
199 if (max
< 0 || sbusfb_mmapsize(fb
,fb
->mmap_map
[i
].size
) > s
) {
201 s
= sbusfb_mmapsize(fb
,fb
->mmap_map
[max
].size
);
206 if (fb
->mmap_map
[max
].voff
+ j
> map_offset
)
207 j
= map_offset
- fb
->mmap_map
[max
].voff
;
208 for (alignment
= 0x400000; alignment
> PAGE_SIZE
; alignment
>>= 3)
209 if (j
>= alignment
&& !(fb
->mmap_map
[max
].poff
& (alignment
- 1)))
211 if (alignment
> PAGE_SIZE
) {
213 alignment
= j
- ((vma
->vm_start
+ fb
->mmap_map
[max
].voff
- vma
->vm_offset
) & (j
- 1));
214 if (alignment
!= j
) {
215 struct vm_area_struct
*vmm
= find_vma(current
->mm
, vma
->vm_start
);
216 if (!vmm
|| vmm
->vm_start
>= vma
->vm_end
+ alignment
) {
217 vma
->vm_start
+= alignment
;
218 vma
->vm_end
+= alignment
;
226 /* Each page, see which map applies */
227 for (page
= 0; page
< size
; ){
229 for (i
= 0; fb
->mmap_map
[i
].size
; i
++)
230 if (fb
->mmap_map
[i
].voff
== vma
->vm_offset
+page
) {
231 map_size
= sbusfb_mmapsize(fb
,fb
->mmap_map
[i
].size
);
233 #define POFF_MASK (PAGE_MASK|0x1UL)
235 #define POFF_MASK (PAGE_MASK)
237 map_offset
= (fb
->physbase
+ fb
->mmap_map
[i
].poff
) & POFF_MASK
;
244 if (page
+ map_size
> size
)
245 map_size
= size
- page
;
246 r
= io_remap_page_range (vma
->vm_start
+page
, map_offset
, map_size
, vma
->vm_page_prot
, fb
->iospace
);
252 vma
->vm_flags
|= VM_IO
;
256 if (info
->display_fg
)
257 lastconsole
= info
->display_fg
->vc_num
;
259 if (fb
->consolecnt
&& fb_display
[lastconsole
].fb_info
== info
) {
260 fb
->vtconsole
= lastconsole
;
262 vt_cons
[lastconsole
]->vc_mode
= KD_GRAPHICS
;
263 vc_cons
[lastconsole
].d
->vc_sw
->con_cursor(vc_cons
[lastconsole
].d
,CM_ERASE
);
264 } else if (fb
->unblank
&& !fb
->blanked
)
270 static void sbusfb_clear_margin(struct display
*p
, int s
)
272 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
274 if (fb
->switch_from_graph
)
275 (*fb
->switch_from_graph
)(fb
);
277 unsigned short rects
[16];
281 rects
[2] = fb
->var
.xres_virtual
;
282 rects
[3] = fb
->y_margin
;
284 rects
[5] = fb
->y_margin
;
285 rects
[6] = fb
->x_margin
;
286 rects
[7] = fb
->var
.yres_virtual
;
287 rects
[8] = fb
->var
.xres_virtual
- fb
->x_margin
;
288 rects
[9] = fb
->y_margin
;
289 rects
[10] = fb
->var
.xres_virtual
;
290 rects
[11] = fb
->var
.yres_virtual
;
291 rects
[12] = fb
->x_margin
;
292 rects
[13] = fb
->var
.yres_virtual
- fb
->y_margin
;
293 rects
[14] = fb
->var
.xres_virtual
- fb
->x_margin
;
294 rects
[15] = fb
->var
.yres_virtual
;
295 (*fb
->fill
)(fb
, p
, s
, 4, rects
);
297 unsigned char *fb_base
= p
->screen_base
, *q
;
298 int skip_bytes
= fb
->y_margin
* fb
->var
.xres_virtual
;
299 int scr_size
= fb
->var
.xres_virtual
* fb
->var
.yres_virtual
;
300 int h
, he
, incr
, size
;
303 if (fb
->var
.bits_per_pixel
== 1) {
304 fb_base
-= (skip_bytes
+ fb
->x_margin
) / 8;
307 mymemset (fb_base
, skip_bytes
- fb
->x_margin
/ 8);
308 mymemset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
/ 8, skip_bytes
- fb
->x_margin
/ 8);
309 incr
= fb
->var
.xres_virtual
/ 8;
310 size
= fb
->x_margin
/ 8 * 2;
311 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
/ 8, h
= 0;
312 h
<= he
; q
+= incr
, h
++)
315 fb_base
-= (skip_bytes
+ fb
->x_margin
);
316 memset (fb_base
, attr_bgcol(p
,s
), skip_bytes
- fb
->x_margin
);
317 memset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
, attr_bgcol(p
,s
), skip_bytes
- fb
->x_margin
);
318 incr
= fb
->var
.xres_virtual
;
319 size
= fb
->x_margin
* 2;
320 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
, h
= 0;
321 h
<= he
; q
+= incr
, h
++)
322 memset (q
, attr_bgcol(p
,s
), size
);
327 static void sbusfb_disp_setup(struct display
*p
)
329 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
333 sbusfb_clear_margin(p
, 0);
337 * Get the Fixed Part of the Display
340 static int sbusfb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
341 struct fb_info
*info
)
343 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
345 memcpy(fix
, &fb
->fix
, sizeof(struct fb_fix_screeninfo
));
350 * Get the User Defined Part of the Display
353 static int sbusfb_get_var(struct fb_var_screeninfo
*var
, int con
,
354 struct fb_info
*info
)
356 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
358 memcpy(var
, &fb
->var
, sizeof(struct fb_var_screeninfo
));
363 * Set the User Defined Part of the Display
366 static int sbusfb_set_var(struct fb_var_screeninfo
*var
, int con
,
367 struct fb_info
*info
)
369 struct display
*display
;
370 int activate
= var
->activate
;
373 display
= &fb_display
[con
];
375 display
= info
->disp
;
377 /* simple check for equality until fully implemented -E */
378 if ((activate
& FB_ACTIVATE_MASK
) == FB_ACTIVATE_NOW
) {
379 if (display
->var
.xres
!= var
->xres
||
380 display
->var
.yres
!= var
->yres
||
381 display
->var
.xres_virtual
!= var
->xres_virtual
||
382 display
->var
.yres_virtual
!= var
->yres_virtual
||
383 display
->var
.bits_per_pixel
!= var
->bits_per_pixel
||
384 display
->var
.accel_flags
!= var
->accel_flags
) {
393 * Pan or Wrap the Display
395 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
398 static int sbusfb_pan_display(struct fb_var_screeninfo
*var
, int con
,
399 struct fb_info
*info
)
401 if (var
->xoffset
|| var
->yoffset
)
411 static int sbus_hw_scursor (struct fbcursor
*cursor
, struct fb_info_sbusfb
*fb
)
416 char red
[2], green
[2], blue
[2];
418 if (copy_from_user (&f
, cursor
, sizeof(struct fbcursor
)))
421 if (op
& FB_CUR_SETSHAPE
){
422 if ((u32
) f
.size
.fbx
> fb
->cursor
.hwsize
.fbx
)
424 if ((u32
) f
.size
.fby
> fb
->cursor
.hwsize
.fby
)
427 bytes
= f
.size
.fby
<< 3;
429 bytes
= f
.size
.fby
<< 2;
431 if (op
& FB_CUR_SETCMAP
){
432 if (f
.cmap
.index
|| f
.cmap
.count
!= 2)
434 if (copy_from_user (red
, f
.cmap
.red
, 2) ||
435 copy_from_user (green
, f
.cmap
.green
, 2) ||
436 copy_from_user (blue
, f
.cmap
.blue
, 2))
439 if (op
& FB_CUR_SETCMAP
)
440 (*fb
->setcursormap
) (fb
, red
, green
, blue
);
441 if (op
& FB_CUR_SETSHAPE
){
444 fb
->cursor
.size
= f
.size
;
445 memset ((void *)&fb
->cursor
.bits
, 0, sizeof (fb
->cursor
.bits
));
446 if (copy_from_user (fb
->cursor
.bits
[0], f
.mask
, bytes
) ||
447 copy_from_user (fb
->cursor
.bits
[1], f
.image
, bytes
))
449 if (f
.size
.fbx
<= 32) {
450 u
= 0xffffffff << (32 - f
.size
.fbx
);
451 for (i
= fb
->cursor
.size
.fby
- 1; i
>= 0; i
--) {
452 fb
->cursor
.bits
[0][i
] &= u
;
453 fb
->cursor
.bits
[1][i
] &= fb
->cursor
.bits
[0][i
];
456 u
= 0xffffffff << (64 - f
.size
.fbx
);
457 for (i
= fb
->cursor
.size
.fby
- 1; i
>= 0; i
--) {
458 fb
->cursor
.bits
[0][2*i
+1] &= u
;
459 fb
->cursor
.bits
[1][2*i
] &= fb
->cursor
.bits
[0][2*i
];
460 fb
->cursor
.bits
[1][2*i
+1] &= fb
->cursor
.bits
[0][2*i
+1];
463 (*fb
->setcurshape
) (fb
);
465 if (op
& (FB_CUR_SETCUR
| FB_CUR_SETPOS
| FB_CUR_SETHOT
)){
466 if (op
& FB_CUR_SETCUR
)
467 fb
->cursor
.enable
= f
.enable
;
468 if (op
& FB_CUR_SETPOS
)
469 fb
->cursor
.cpos
= f
.pos
;
470 if (op
& FB_CUR_SETHOT
)
471 fb
->cursor
.chot
= f
.hot
;
472 (*fb
->setcursor
) (fb
);
477 static unsigned char hw_cursor_cmap
[2] = { 0, 0xff };
480 sbusfb_cursor_timer_handler(unsigned long dev_addr
)
482 struct fb_info_sbusfb
*fb
= (struct fb_info_sbusfb
*)dev_addr
;
484 if (!fb
->setcursor
) return;
486 if (fb
->cursor
.mode
& CURSOR_BLINK
) {
487 fb
->cursor
.enable
^= 1;
491 fb
->cursor
.timer
.expires
= jiffies
+ fb
->cursor
.blink_rate
;
492 add_timer(&fb
->cursor
.timer
);
495 static void sbusfb_cursor(struct display
*p
, int mode
, int x
, int y
)
497 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
501 fb
->cursor
.mode
&= ~CURSOR_BLINK
;
502 fb
->cursor
.enable
= 0;
503 (*fb
->setcursor
)(fb
);
508 if (fb
->cursor
.mode
& CURSOR_SHAPE
) {
509 fb
->cursor
.size
.fbx
= fontwidth(p
);
510 fb
->cursor
.size
.fby
= fontheight(p
);
511 fb
->cursor
.chot
.fbx
= 0;
512 fb
->cursor
.chot
.fby
= 0;
513 fb
->cursor
.enable
= 1;
514 memset (fb
->cursor
.bits
, 0, sizeof (fb
->cursor
.bits
));
515 fb
->cursor
.bits
[0][fontheight(p
) - 2] = (0xffffffff << (32 - fontwidth(p
)));
516 fb
->cursor
.bits
[1][fontheight(p
) - 2] = (0xffffffff << (32 - fontwidth(p
)));
517 fb
->cursor
.bits
[0][fontheight(p
) - 1] = (0xffffffff << (32 - fontwidth(p
)));
518 fb
->cursor
.bits
[1][fontheight(p
) - 1] = (0xffffffff << (32 - fontwidth(p
)));
519 (*fb
->setcursormap
) (fb
, hw_cursor_cmap
, hw_cursor_cmap
, hw_cursor_cmap
);
520 (*fb
->setcurshape
) (fb
);
522 fb
->cursor
.mode
= CURSOR_BLINK
;
524 fb
->cursor
.cpos
.fbx
= (x
<< fontwidthlog(p
)) + fb
->x_margin
;
526 fb
->cursor
.cpos
.fbx
= (x
* fontwidth(p
)) + fb
->x_margin
;
527 if (fontheightlog(p
))
528 fb
->cursor
.cpos
.fby
= (y
<< fontheightlog(p
)) + fb
->y_margin
;
530 fb
->cursor
.cpos
.fby
= (y
* fontheight(p
)) + fb
->y_margin
;
531 (*fb
->setcursor
)(fb
);
540 static int sbusfb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
541 struct fb_info
*info
)
543 if (!info
->display_fg
|| con
== info
->display_fg
->vc_num
) /* current console? */
544 return fb_get_cmap(cmap
, kspc
, sbusfb_getcolreg
, info
);
545 else if (fb_display
[con
].cmap
.len
) /* non default colormap? */
546 fb_copy_cmap(&fb_display
[con
].cmap
, cmap
, kspc
? 0 : 2);
548 fb_copy_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
), cmap
, kspc
? 0 : 2);
556 static int sbusfb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
557 struct fb_info
*info
)
560 struct display
*disp
;
563 disp
= &fb_display
[con
];
566 if (!disp
->cmap
.len
) { /* no colormap allocated? */
567 if ((err
= fb_alloc_cmap(&disp
->cmap
, 1<<disp
->var
.bits_per_pixel
, 0)))
570 if (con
== currcon
) { /* current console? */
571 err
= fb_set_cmap(cmap
, kspc
, sbusfb_setcolreg
, info
);
573 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
576 (*fb
->loadcmap
)(fb
, &fb_display
[con
], cmap
->start
, cmap
->len
);
580 fb_copy_cmap(cmap
, &disp
->cmap
, kspc
? 0 : 1);
584 static int sbusfb_ioctl(struct inode
*inode
, struct file
*file
, u_int cmd
,
585 u_long arg
, int con
, struct fb_info
*info
)
587 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
592 case FBIOGTYPE
: /* return frame buffer type */
593 copy_to_user_ret((struct fbtype
*)arg
, &fb
->type
, sizeof(struct fbtype
), -EFAULT
);
596 struct fbgattr
*fba
= (struct fbgattr
*) arg
;
598 i
= verify_area (VERIFY_WRITE
, (void *) arg
, sizeof (struct fbgattr
));
600 __put_user_ret(fb
->emulations
[0], &fba
->real_type
, -EFAULT
);
601 __put_user_ret(0, &fba
->owner
, -EFAULT
);
602 __copy_to_user_ret(&fba
->fbtype
, &fb
->type
,
603 sizeof(struct fbtype
), -EFAULT
);
604 __put_user_ret(0, &fba
->sattr
.flags
, -EFAULT
);
605 __put_user_ret(fb
->type
.fb_type
, &fba
->sattr
.emu_type
, -EFAULT
);
606 __put_user_ret(-1, &fba
->sattr
.dev_specific
[0], -EFAULT
);
607 for (i
= 0; i
< 4; i
++)
608 put_user_ret(fb
->emulations
[i
], &fba
->emu_types
[i
], -EFAULT
);
612 i
= verify_area (VERIFY_READ
, (void *) arg
, sizeof (struct fbsattr
));
616 if (fb
->consolecnt
) {
617 lastconsole
= info
->display_fg
->vc_num
;
618 if (vt_cons
[lastconsole
]->vc_mode
== KD_TEXT
)
621 get_user_ret(i
, (int *)arg
, -EFAULT
);
623 if (!fb
->blanked
|| !fb
->unblank
)
625 if (fb
->consolecnt
|| (fb
->open
&& fb
->mmaped
))
629 if (fb
->blanked
|| !fb
->blank
)
636 put_user_ret(fb
->blanked
, (int *) arg
, -EFAULT
);
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 __get_user_ret(count
, &cmap
->count
, -EFAULT
);
649 __get_user_ret(index
, &cmap
->index
, -EFAULT
);
650 if ((index
< 0) || (index
> 255))
652 if (index
+ count
> 256)
654 __get_user_ret(rp
, &cmap
->red
, -EFAULT
);
655 __get_user_ret(gp
, &cmap
->green
, -EFAULT
);
656 __get_user_ret(bp
, &cmap
->blue
, -EFAULT
);
657 if(verify_area (VERIFY_WRITE
, rp
, count
)) return -EFAULT
;
658 if(verify_area (VERIFY_WRITE
, gp
, count
)) return -EFAULT
;
659 if(verify_area (VERIFY_WRITE
, bp
, count
)) return -EFAULT
;
661 for (i
= index
; i
< end
; i
++){
662 __put_user_ret(fb
->color_map
CM(i
,0), rp
, -EFAULT
);
663 __put_user_ret(fb
->color_map
CM(i
,1), gp
, -EFAULT
);
664 __put_user_ret(fb
->color_map
CM(i
,2), bp
, -EFAULT
);
667 (*fb
->loadcmap
)(fb
, NULL
, index
, count
);
670 case FBIOPUTCMAP_SPARC
: { /* load color map entries */
672 int end
, count
, index
;
677 i
= verify_area (VERIFY_READ
, (void *) arg
, sizeof (struct fbcmap
));
679 cmap
= (struct fbcmap
*) arg
;
680 __get_user_ret(count
, &cmap
->count
, -EFAULT
);
681 __get_user_ret(index
, &cmap
->index
, -EFAULT
);
682 if ((index
< 0) || (index
> 255))
684 if (index
+ count
> 256)
686 __get_user_ret(rp
, &cmap
->red
, -EFAULT
);
687 __get_user_ret(gp
, &cmap
->green
, -EFAULT
);
688 __get_user_ret(bp
, &cmap
->blue
, -EFAULT
);
689 if(verify_area (VERIFY_READ
, rp
, count
)) return -EFAULT
;
690 if(verify_area (VERIFY_READ
, gp
, count
)) return -EFAULT
;
691 if(verify_area (VERIFY_READ
, bp
, count
)) return -EFAULT
;
694 for (i
= index
; i
< end
; i
++){
695 __get_user_ret(fb
->color_map
CM(i
,0), rp
, -EFAULT
);
696 __get_user_ret(fb
->color_map
CM(i
,1), gp
, -EFAULT
);
697 __get_user_ret(fb
->color_map
CM(i
,2), bp
, -EFAULT
);
700 (*fb
->loadcmap
)(fb
, NULL
, index
, count
);
704 struct fbcurpos
*p
= (struct fbcurpos
*) arg
;
705 if (!fb
->setcursor
) return -EINVAL
;
706 if(verify_area (VERIFY_WRITE
, p
, sizeof (struct fbcurpos
)))
708 __put_user_ret(fb
->cursor
.hwsize
.fbx
, &p
->fbx
, -EFAULT
);
709 __put_user_ret(fb
->cursor
.hwsize
.fby
, &p
->fby
, -EFAULT
);
713 if (!fb
->setcursor
) return -EINVAL
;
714 if (fb
->consolecnt
) {
715 lastconsole
= info
->display_fg
->vc_num
;
716 if (vt_cons
[lastconsole
]->vc_mode
== KD_TEXT
)
717 return -EINVAL
; /* Don't let graphics programs hide our nice text cursor */
718 fb
->cursor
.mode
= CURSOR_SHAPE
; /* Forget state of our text cursor */
720 return sbus_hw_scursor ((struct fbcursor
*) arg
, fb
);
723 if (!fb
->setcursor
) return -EINVAL
;
724 /* Don't let graphics programs move our nice text cursor */
725 if (fb
->consolecnt
) {
726 lastconsole
= info
->display_fg
->vc_num
;
727 if (vt_cons
[lastconsole
]->vc_mode
== KD_TEXT
)
728 return -EINVAL
; /* Don't let graphics programs move our nice text cursor */
730 if (copy_from_user(&fb
->cursor
.cpos
, (void *)arg
, sizeof(struct fbcurpos
)))
732 (*fb
->setcursor
) (fb
);
736 return fb
->ioctl(fb
, cmd
, arg
);
743 * Setup: parse used options
746 void __init
sbusfb_setup(char *options
, int *ints
)
750 for (p
= options
;;) {
751 if (!strncmp(p
, "nomargins", 9)) {
752 defx_margin
= 0; defy_margin
= 0;
753 } else if (!strncmp(p
, "margins=", 8)) {
757 i
= simple_strtoul(p
+8,&q
,10);
758 if (i
>= 0 && *q
== 'x') {
759 j
= simple_strtoul(q
+1,&q
,10);
760 if (j
>= 0 && (*q
== ' ' || !*q
)) {
761 defx_margin
= i
; defy_margin
= j
;
764 } else if (!strncmp(p
, "font=", 5)) {
767 for (i
= 0; i
< sizeof(fontname
) - 1; i
++)
768 if (p
[i
+5] == ' ' || !p
[i
+5])
770 memcpy(fontname
, p
+5, i
);
772 } else if (!strncmp(p
, "noblink", 7))
774 while (*p
&& *p
!= ' ' && *p
!= ',') p
++;
775 if (*p
!= ',') break;
780 static int sbusfbcon_switch(int con
, struct fb_info
*info
)
782 int x_margin
, y_margin
;
783 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
786 /* Do we have to save the colormap? */
787 if (fb_display
[currcon
].cmap
.len
)
788 fb_get_cmap(&fb_display
[currcon
].cmap
, 1, sbusfb_getcolreg
, info
);
790 if (info
->display_fg
) {
791 lastconsole
= info
->display_fg
->vc_num
;
792 if (lastconsole
!= con
&&
793 (fontwidth(&fb_display
[lastconsole
]) != fontwidth(&fb_display
[con
]) ||
794 fontheight(&fb_display
[lastconsole
]) != fontheight(&fb_display
[con
])))
795 fb
->cursor
.mode
|= CURSOR_SHAPE
;
797 x_margin
= (fb_display
[con
].var
.xres_virtual
- fb_display
[con
].var
.xres
) / 2;
798 y_margin
= (fb_display
[con
].var
.yres_virtual
- fb_display
[con
].var
.yres
) / 2;
800 fb
->margins(fb
, &fb_display
[con
], x_margin
, y_margin
);
801 if (fb
->graphmode
|| fb
->x_margin
!= x_margin
|| fb
->y_margin
!= y_margin
) {
802 fb
->x_margin
= x_margin
; fb
->y_margin
= y_margin
;
803 sbusfb_clear_margin(&fb_display
[con
], 0);
806 /* Install new colormap */
807 do_install_cmap(con
, info
);
812 * Update the `var' structure (called by fbcon.c)
815 static int sbusfbcon_updatevar(int con
, struct fb_info
*info
)
825 static void sbusfbcon_blank(int blank
, struct fb_info
*info
)
827 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
829 if (blank
&& fb
->blank
)
830 return fb
->blank(fb
);
831 else if (!blank
&& fb
->unblank
)
832 return fb
->unblank(fb
);
836 * Read a single color register and split it into
837 * colors/transparent. Return != 0 for invalid regno.
840 static int sbusfb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
841 u_int
*transp
, struct fb_info
*info
)
843 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
845 if (!fb
->color_map
|| regno
> 255)
847 *red
= (fb
->color_map
CM(regno
, 0)<<8) | fb
->color_map
CM(regno
, 0);
848 *green
= (fb
->color_map
CM(regno
, 1)<<8) | fb
->color_map
CM(regno
, 1);
849 *blue
= (fb
->color_map
CM(regno
, 2)<<8) | fb
->color_map
CM(regno
, 2);
856 * Set a single color register. The values supplied are already
857 * rounded down to the hardware's capabilities (according to the
858 * entries in the var structure). Return != 0 for invalid regno.
861 static int sbusfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
862 u_int transp
, struct fb_info
*info
)
864 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
866 if (!fb
->color_map
|| regno
> 255)
871 fb
->color_map
CM(regno
, 0) = red
;
872 fb
->color_map
CM(regno
, 1) = green
;
873 fb
->color_map
CM(regno
, 2) = blue
;
878 static void do_install_cmap(int con
, struct fb_info
*info
)
880 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
884 if (fb_display
[con
].cmap
.len
)
885 fb_set_cmap(&fb_display
[con
].cmap
, 1, sbusfb_setcolreg
, info
);
887 fb_set_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
),
888 1, sbusfb_setcolreg
, info
);
890 (*fb
->loadcmap
)(fb
, &fb_display
[con
], 0, 256);
893 static int sbusfb_set_font(struct display
*p
, int width
, int height
)
896 int w
= p
->var
.xres_virtual
, h
= p
->var
.yres_virtual
;
897 int depth
= p
->var
.bits_per_pixel
;
898 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
899 int x_margin
, y_margin
;
901 if (depth
> 8) depth
= 8;
904 if (defx_margin
< 0 || defy_margin
< 0) {
905 for (margin
= 0; def_margins
[margin
].depth
; margin
++)
906 if (w
== def_margins
[margin
].xres
&&
907 h
== def_margins
[margin
].yres
&&
908 depth
== def_margins
[margin
].depth
) {
909 x_margin
= def_margins
[margin
].x_margin
;
910 y_margin
= def_margins
[margin
].y_margin
;
914 x_margin
= defx_margin
;
915 y_margin
= defy_margin
;
917 x_margin
+= ((w
- 2*x_margin
) % width
) / 2;
918 y_margin
+= ((h
- 2*y_margin
) % height
) / 2;
920 p
->var
.xres
= w
- 2*x_margin
;
921 p
->var
.yres
= h
- 2*y_margin
;
923 fb
->cursor
.mode
|= CURSOR_SHAPE
;
926 fb
->margins(fb
, p
, x_margin
, y_margin
);
927 if (fb
->x_margin
!= x_margin
|| fb
->y_margin
!= y_margin
) {
928 fb
->x_margin
= x_margin
; fb
->y_margin
= y_margin
;
929 sbusfb_clear_margin(p
, 0);
935 void sbusfb_palette(int enter
)
940 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
942 if (p
->dispsw
&& p
->dispsw
->setup
== sbusfb_disp_setup
&&
943 p
->fb_info
->display_fg
&&
944 p
->fb_info
->display_fg
->vc_num
== i
) {
945 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
947 if (fb
->restore_palette
) {
949 fb
->restore_palette(fb
);
950 else if (vt_cons
[i
]->vc_mode
!= KD_GRAPHICS
)
951 vc_cons
[i
].d
->vc_sw
->con_set_palette(vc_cons
[i
].d
, color_table
);
961 extern void (*prom_palette
)(int);
963 static void __init
sbusfb_init_fb(int node
, int parent
, int fbtype
,
964 struct linux_sbus_device
*sbdp
)
966 struct fb_fix_screeninfo
*fix
;
967 struct fb_var_screeninfo
*var
;
968 struct display
*disp
;
969 struct fb_info_sbusfb
*fb
;
971 int linebytes
, w
, h
, depth
;
975 fb
= kmalloc(sizeof(struct fb_info_sbusfb
), GFP_ATOMIC
);
977 prom_printf("Could not allocate sbusfb structure\n");
982 prom_palette
= sbusfb_palette
;
984 memset(fb
, 0, sizeof(struct fb_info_sbusfb
));
990 fb
->prom_node
= node
;
991 fb
->prom_parent
= parent
;
994 fb
->iospace
= sbdp
->reg_addrs
[0].which_io
;
996 type
->fb_type
= fbtype
;
997 memset(&fb
->emulations
, 0xff, sizeof(fb
->emulations
));
998 fb
->emulations
[0] = fbtype
;
1000 #ifndef __sparc_v9__
1001 disp
->screen_base
= (unsigned char *)prom_getintdefault(node
, "address", 0);
1004 type
->fb_height
= h
= prom_getintdefault(node
, "height", 900);
1005 type
->fb_width
= w
= prom_getintdefault(node
, "width", 1152);
1007 type
->fb_depth
= depth
= (fbtype
== FBTYPE_SUN2BW
) ? 1 : 8;
1008 linebytes
= prom_getintdefault(node
, "linebytes", w
* depth
/ 8);
1009 type
->fb_size
= PAGE_ALIGN((linebytes
) * h
);
1011 if (defx_margin
< 0 || defy_margin
< 0) {
1012 for (margin
= 0; def_margins
[margin
].depth
; margin
++)
1013 if (w
== def_margins
[margin
].xres
&&
1014 h
== def_margins
[margin
].yres
&&
1015 depth
== def_margins
[margin
].depth
) {
1016 fb
->x_margin
= def_margins
[margin
].x_margin
;
1017 fb
->y_margin
= def_margins
[margin
].y_margin
;
1021 fb
->x_margin
= defx_margin
;
1022 fb
->y_margin
= defy_margin
;
1024 fb
->x_margin
+= ((w
- 2*fb
->x_margin
) & 7) / 2;
1025 fb
->y_margin
+= ((h
- 2*fb
->y_margin
) & 15) / 2;
1027 var
->xres_virtual
= w
;
1028 var
->yres_virtual
= h
;
1029 var
->xres
= w
- 2*fb
->x_margin
;
1030 var
->yres
= h
- 2*fb
->y_margin
;
1032 var
->bits_per_pixel
= depth
;
1033 var
->height
= var
->width
= -1;
1034 var
->pixclock
= 10000;
1035 var
->vmode
= FB_VMODE_NONINTERLACED
;
1036 var
->red
.length
= var
->green
.length
= var
->blue
.length
= 8;
1038 fix
->line_length
= linebytes
;
1039 fix
->smem_len
= type
->fb_size
;
1040 fix
->type
= FB_TYPE_PACKED_PIXELS
;
1041 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
1044 fb
->info
.fbops
= &sbusfb_ops
;
1045 fb
->info
.disp
= disp
;
1046 strcpy(fb
->info
.fontname
, fontname
);
1047 fb
->info
.changevar
= NULL
;
1048 fb
->info
.switch_con
= &sbusfbcon_switch
;
1049 fb
->info
.updatevar
= &sbusfbcon_updatevar
;
1050 fb
->info
.blank
= &sbusfbcon_blank
;
1051 fb
->info
.flags
= FBINFO_FLAG_DEFAULT
;
1053 fb
->cursor
.hwsize
.fbx
= 32;
1054 fb
->cursor
.hwsize
.fby
= 32;
1056 if (depth
> 1 && !fb
->color_map
)
1057 fb
->color_map
= kmalloc(256 * 3, GFP_ATOMIC
);
1060 #ifdef CONFIG_FB_CREATOR
1061 case FBTYPE_CREATOR
:
1062 p
= creatorfb_init(fb
); break;
1064 #ifdef CONFIG_FB_CGSIX
1065 case FBTYPE_SUNFAST_COLOR
:
1066 p
= cgsixfb_init(fb
); break;
1068 #ifdef CONFIG_FB_CGTHREE
1069 case FBTYPE_SUN3COLOR
:
1070 p
= cgthreefb_init(fb
); break;
1072 #ifdef CONFIG_FB_TCX
1073 case FBTYPE_TCXCOLOR
:
1074 p
= tcxfb_init(fb
); break;
1076 #ifdef CONFIG_FB_LEO
1078 p
= leofb_init(fb
); break;
1080 #ifdef CONFIG_FB_BWTWO
1082 p
= bwtwofb_init(fb
); break;
1084 #ifdef CONFIG_FB_CGFOURTEEN
1085 case FBTYPE_MDICOLOR
:
1086 p
= cgfourteenfb_init(fb
); break;
1095 if (p
== SBUSFBINIT_SIZECHANGE
)
1098 disp
->dispsw
= &fb
->dispsw
;
1099 if (fb
->setcursor
) {
1100 fb
->dispsw
.cursor
= sbusfb_cursor
;
1102 fb
->cursor
.blink_rate
= DEFAULT_CURSOR_BLINK_RATE
;
1103 init_timer(&fb
->cursor
.timer
);
1104 fb
->cursor
.timer
.expires
= jiffies
+ fb
->cursor
.blink_rate
;
1105 fb
->cursor
.timer
.data
= (unsigned long)fb
;
1106 fb
->cursor
.timer
.function
= sbusfb_cursor_timer_handler
;
1107 add_timer(&fb
->cursor
.timer
);
1110 fb
->cursor
.mode
= CURSOR_SHAPE
;
1111 fb
->dispsw
.set_font
= sbusfb_set_font
;
1112 fb
->setup
= fb
->dispsw
.setup
;
1113 fb
->dispsw
.setup
= sbusfb_disp_setup
;
1114 fb
->dispsw
.clear_margins
= NULL
;
1117 disp
->visual
= fix
->visual
;
1118 disp
->type
= fix
->type
;
1119 disp
->type_aux
= fix
->type_aux
;
1120 disp
->line_length
= fix
->line_length
;
1123 disp
->can_soft_blank
= 1;
1125 sbusfb_set_var(var
, -1, &fb
->info
);
1127 if (register_framebuffer(&fb
->info
) < 0) {
1131 printk("fb%d: %s\n", GET_FB_IDX(fb
->info
.node
), p
);
1134 static inline int known_card(char *name
)
1137 for (p
= name
; *p
&& *p
!= ','; p
++);
1138 if (*p
== ',') name
= p
+ 1;
1139 if (!strcmp(name
, "cgsix") || !strcmp(name
, "cgthree+"))
1140 return FBTYPE_SUNFAST_COLOR
;
1141 if (!strcmp(name
, "cgthree") || !strcmp(name
, "cgRDI"))
1142 return FBTYPE_SUN3COLOR
;
1143 if (!strcmp(name
, "cgfourteen"))
1144 return FBTYPE_MDICOLOR
;
1145 if (!strcmp(name
, "leo"))
1146 return FBTYPE_SUNLEO
;
1147 if (!strcmp(name
, "bwtwo"))
1148 return FBTYPE_SUN2BW
;
1149 if (!strcmp(name
, "tcx"))
1150 return FBTYPE_TCXCOLOR
;
1151 return FBTYPE_NOTYPE
;
1154 void __init
sbusfb_init(void)
1157 struct linux_sbus_device
*sbdp
;
1158 struct linux_sbus
*sbus
;
1160 extern int con_is_present(void);
1162 if (!con_is_present()) return;
1164 #ifdef CONFIG_FB_CREATOR
1167 root
= prom_getchild(prom_root_node
);
1168 for (node
= prom_searchsiblings(root
, "SUNW,ffb"); node
;
1169 node
= prom_searchsiblings(prom_getsibling(node
), "SUNW,ffb"))
1170 sbusfb_init_fb(node
, prom_root_node
, FBTYPE_CREATOR
, NULL
);
1171 for (node
= prom_searchsiblings(root
, "SUNW,afb"); node
;
1172 node
= prom_searchsiblings(prom_getsibling(node
), "SUNW,afb"))
1173 sbusfb_init_fb(node
, prom_root_node
, FBTYPE_CREATOR
, NULL
);
1177 sbusfb_init_fb(0, 0, FBTYPE_SUN2BW
, NULL
);
1179 #if defined(CONFIG_FB_CGFOURTEEN) && !defined(__sparc_v9__)
1182 root
= prom_getchild(prom_root_node
);
1183 root
= prom_searchsiblings(root
, "obio");
1185 (node
= prom_searchsiblings(prom_getchild(root
), "cgfourteen"))) {
1186 sbusfb_init_fb(node
, root
, FBTYPE_MDICOLOR
, NULL
);
1190 if (!SBus_chain
) return;
1191 for_all_sbusdev(sbdp
, sbus
) {
1192 type
= known_card(sbdp
->prom_name
);
1193 if (type
== FBTYPE_NOTYPE
) continue;
1194 if (prom_getproperty(sbdp
->prom_node
, "emulation", prom_name
, sizeof(prom_name
)) > 0) {
1195 type
= known_card(prom_name
);
1196 if (type
== FBTYPE_NOTYPE
) type
= known_card(sbdp
->prom_name
);
1198 prom_apply_sbus_ranges(sbdp
->my_bus
, &sbdp
->reg_addrs
[0],
1199 sbdp
->num_registers
, sbdp
);
1200 sbusfb_init_fb(sbdp
->prom_node
, sbdp
->my_bus
->prom_node
, type
, sbdp
);