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>
47 * Interface used by the world
50 void sbusfb_init(void);
51 void sbusfb_setup(char *options
, int *ints
);
54 static int nomargins __initdata
= 0;
58 int x_margin
, y_margin
;
59 } def_margins
[] __initdata
= {
60 { 8, 1280, 1024, 64, 80 },
61 { 8, 1152, 1024, 64, 80 },
62 { 8, 1152, 900, 64, 18 },
63 { 8, 1024, 768, 0, 0 },
64 { 8, 800, 600, 16, 12 },
65 { 8, 640, 480, 0, 0 },
66 { 1, 1152, 900, 8, 18 },
70 static int sbusfb_open(struct fb_info
*info
, int user
);
71 static int sbusfb_release(struct fb_info
*info
, int user
);
72 static int sbusfb_mmap(struct fb_info
*info
, struct file
*file
,
73 struct vm_area_struct
*vma
);
74 static int sbusfb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
75 struct fb_info
*info
);
76 static int sbusfb_get_var(struct fb_var_screeninfo
*var
, int con
,
77 struct fb_info
*info
);
78 static int sbusfb_set_var(struct fb_var_screeninfo
*var
, int con
,
79 struct fb_info
*info
);
80 static int sbusfb_pan_display(struct fb_var_screeninfo
*var
, int con
,
81 struct fb_info
*info
);
82 static int sbusfb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
83 struct fb_info
*info
);
84 static int sbusfb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
85 struct fb_info
*info
);
86 static int sbusfb_ioctl(struct inode
*inode
, struct file
*file
, u_int cmd
,
87 u_long arg
, int con
, struct fb_info
*info
);
88 static void sbusfb_cursor(struct display
*p
, int mode
, int x
, int y
);
89 static void sbusfb_clear_margin(struct display
*p
, int s
);
90 extern int io_remap_page_range(unsigned long from
, unsigned long offset
,
91 unsigned long size
, pgprot_t prot
, int space
);
95 * Interface to the low level console driver
98 static int sbusfbcon_switch(int con
, struct fb_info
*info
);
99 static int sbusfbcon_updatevar(int con
, struct fb_info
*info
);
100 static void sbusfbcon_blank(int blank
, struct fb_info
*info
);
107 static int sbusfb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
108 u_int
*transp
, struct fb_info
*info
);
109 static int sbusfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
110 u_int transp
, struct fb_info
*info
);
111 static void do_install_cmap(int con
, struct fb_info
*info
);
113 static struct fb_ops sbusfb_ops
= {
114 sbusfb_open
, sbusfb_release
, sbusfb_get_fix
, sbusfb_get_var
, sbusfb_set_var
,
115 sbusfb_get_cmap
, sbusfb_set_cmap
, sbusfb_pan_display
, sbusfb_ioctl
, sbusfb_mmap
120 * Open/Release the frame buffer device
123 static int sbusfb_open(struct fb_info
*info
, int user
)
125 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
128 if (fb
->open
) return -EBUSY
;
138 static int sbusfb_release(struct fb_info
*info
, int user
)
140 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
143 if (fb
->vtconsole
!= -1) {
144 vt_cons
[fb
->vtconsole
]->vc_mode
= KD_TEXT
;
146 sbusfb_clear_margin(&fb_display
[fb
->vtconsole
], 0);
155 static int sbusfb_mmap(struct fb_info
*info
, struct file
*file
,
156 struct vm_area_struct
*vma
)
158 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
159 unsigned int size
, page
, r
, map_size
;
160 unsigned long map_offset
= 0;
163 size
= vma
->vm_end
- vma
->vm_start
;
164 if (vma
->vm_offset
& ~PAGE_MASK
)
167 /* To stop the swapper from even considering these pages */
168 vma
->vm_flags
|= (VM_SHM
| VM_LOCKED
);
171 /* Align it as much as desirable */
173 int j
, max
= -1, alignment
;
175 map_offset
= vma
->vm_offset
+size
;
176 for (i
= 0; fb
->mmap_map
[i
].size
; i
++) {
177 if (fb
->mmap_map
[i
].voff
< vma
->vm_offset
)
179 if (fb
->mmap_map
[i
].voff
>= map_offset
)
181 if (max
< 0 || fb
->mmap_map
[i
].size
> fb
->mmap_map
[max
].size
)
185 j
= fb
->mmap_map
[max
].size
;
186 if (fb
->mmap_map
[max
].voff
+ j
> map_offset
)
187 j
= map_offset
- fb
->mmap_map
[max
].voff
;
188 for (alignment
= 0x400000; alignment
> PAGE_SIZE
; alignment
>>= 3)
189 if (j
>= alignment
&& !(fb
->mmap_map
[max
].poff
& (alignment
- 1)))
191 if (alignment
> PAGE_SIZE
) {
193 alignment
= j
- ((vma
->vm_start
+ fb
->mmap_map
[max
].voff
- vma
->vm_offset
) & (j
- 1));
194 if (alignment
!= j
) {
195 struct vm_area_struct
*vmm
= find_vma(current
->mm
, vma
->vm_start
);
196 if (!vmm
|| vmm
->vm_start
>= vma
->vm_end
+ alignment
) {
197 vma
->vm_start
+= alignment
;
198 vma
->vm_end
+= alignment
;
206 /* Each page, see which map applies */
207 for (page
= 0; page
< size
; ){
209 for (i
= 0; fb
->mmap_map
[i
].size
; i
++)
210 if (fb
->mmap_map
[i
].voff
== vma
->vm_offset
+page
) {
211 map_size
= fb
->mmap_map
[i
].size
;
212 map_offset
= (fb
->physbase
+ fb
->mmap_map
[i
].poff
) & _PAGE_PADDR
;
219 if (page
+ map_size
> size
)
220 map_size
= size
- page
;
221 r
= io_remap_page_range (vma
->vm_start
+page
, map_offset
, map_size
, vma
->vm_page_prot
, fb
->iospace
);
229 vma
->vm_flags
|= VM_IO
;
232 if (fb
->consolecnt
&& fb_display
[fb
->lastconsole
].fb_info
== info
) {
233 fb
->vtconsole
= fb
->lastconsole
;
234 vt_cons
[fb
->lastconsole
]->vc_mode
= KD_GRAPHICS
;
235 } else if (fb
->unblank
&& !fb
->blanked
)
241 static void sbusfb_clear_margin(struct display
*p
, int s
)
243 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
246 unsigned short rects
[16];
250 rects
[2] = fb
->var
.xres_virtual
;
251 rects
[3] = fb
->y_margin
;
253 rects
[5] = fb
->y_margin
;
254 rects
[6] = fb
->x_margin
;
255 rects
[7] = fb
->var
.yres_virtual
;
256 rects
[8] = fb
->var
.xres_virtual
- fb
->x_margin
;
257 rects
[9] = fb
->y_margin
;
258 rects
[10] = fb
->var
.xres_virtual
;
259 rects
[11] = fb
->var
.yres_virtual
;
260 rects
[12] = fb
->x_margin
;
261 rects
[13] = fb
->var
.yres_virtual
- fb
->y_margin
;
262 rects
[14] = fb
->var
.xres_virtual
- fb
->x_margin
;
263 rects
[15] = fb
->var
.yres_virtual
;
264 (*fb
->fill
)(fb
, s
, 4, rects
);
266 unsigned char *fb_base
= p
->screen_base
, *q
;
267 int skip_bytes
= fb
->y_margin
* fb
->var
.xres_virtual
;
268 int scr_size
= fb
->var
.xres_virtual
* fb
->var
.yres_virtual
;
269 int h
, he
, incr
, size
;
272 if (fb
->var
.bits_per_pixel
== 1) {
273 fb_base
-= (skip_bytes
+ fb
->x_margin
) / 8;
276 memset (fb_base
, ~0, skip_bytes
- fb
->x_margin
/ 8);
277 memset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
/ 8, ~0, skip_bytes
- fb
->x_margin
/ 8);
278 incr
= fb
->var
.xres_virtual
/ 8;
279 size
= fb
->x_margin
/ 8 * 2;
280 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
/ 8, h
= 0;
281 h
<= he
; q
+= incr
, h
++)
282 memset (q
, ~0, size
);
284 fb_base
-= (skip_bytes
+ fb
->x_margin
);
285 memset (fb_base
, attr_bg_col(s
), skip_bytes
- fb
->x_margin
);
286 memset (fb_base
+ scr_size
- skip_bytes
+ fb
->x_margin
, attr_bg_col(s
), skip_bytes
- fb
->x_margin
);
287 incr
= fb
->var
.xres_virtual
;
288 size
= fb
->x_margin
* 2;
289 for (q
= fb_base
+ skip_bytes
- fb
->x_margin
, h
= 0;
290 h
<= he
; q
+= incr
, h
++)
291 memset (q
, attr_bg_col(s
), size
);
294 if (fb
->switch_from_graph
)
295 (*fb
->switch_from_graph
)(fb
);
298 static void sbusfb_disp_setup(struct display
*p
)
300 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
304 sbusfb_clear_margin(p
, 0);
308 * Get the Fixed Part of the Display
311 static int sbusfb_get_fix(struct fb_fix_screeninfo
*fix
, int con
,
312 struct fb_info
*info
)
314 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
316 memcpy(fix
, &fb
->fix
, sizeof(struct fb_fix_screeninfo
));
321 * Get the User Defined Part of the Display
324 static int sbusfb_get_var(struct fb_var_screeninfo
*var
, int con
,
325 struct fb_info
*info
)
327 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
329 memcpy(var
, &fb
->var
, sizeof(struct fb_var_screeninfo
));
334 * Set the User Defined Part of the Display
337 static int sbusfb_set_var(struct fb_var_screeninfo
*var
, int con
,
338 struct fb_info
*info
)
343 struct display
*display
;
344 int oldbpp
= -1, err
;
345 int activate
= var
->activate
;
346 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
349 display
= &fb_display
[con
];
351 display
= &fb
->disp
; /* used during initialization */
353 if (var
->xres
> fb
->var
.xres
|| var
->yres
> fb
->var
.yres
||
354 var
->xres_virtual
> fb
->var
.xres_virtual
||
355 var
->yres_virtual
> fb
->var
.yres_virtual
||
356 var
->bits_per_pixel
> fb
->var
.bits_per_pixel
||
358 (var
->vmode
& FB_VMODE_MASK
) != FB_VMODE_NONINTERLACED
)
360 memcpy(var
, &fb
->var
, sizeof(struct fb_var_screeninfo
));
362 if ((activate
& FB_ACTIVATE_MASK
) == FB_ACTIVATE_NOW
) {
363 oldbpp
= display
->var
.bits_per_pixel
;
366 if (oldbpp
!= var
->bits_per_pixel
) {
367 if ((err
= fb_alloc_cmap(&display
->cmap
, 0, 0)))
369 do_install_cmap(con
, info
);
376 * Pan or Wrap the Display
378 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
381 static int sbusfb_pan_display(struct fb_var_screeninfo
*var
, int con
,
382 struct fb_info
*info
)
384 if (var
->xoffset
|| var
->yoffset
)
394 static int sbus_hw_scursor (struct fbcursor
*cursor
, struct fb_info_sbusfb
*fb
)
399 char red
[2], green
[2], blue
[2];
401 if (copy_from_user (&f
, cursor
, sizeof(struct fbcursor
)))
404 if (op
& FB_CUR_SETSHAPE
){
405 if ((u32
) f
.size
.fbx
> fb
->cursor
.hwsize
.fbx
)
407 if ((u32
) f
.size
.fby
> fb
->cursor
.hwsize
.fby
)
410 bytes
= f
.size
.fby
<< 3;
412 bytes
= f
.size
.fby
<< 2;
414 if (op
& FB_CUR_SETCMAP
){
415 if (f
.cmap
.index
|| f
.cmap
.count
!= 2)
417 if (copy_from_user (red
, f
.cmap
.red
, 2) ||
418 copy_from_user (green
, f
.cmap
.green
, 2) ||
419 copy_from_user (blue
, f
.cmap
.blue
, 2))
422 if (op
& FB_CUR_SETCMAP
)
423 (*fb
->setcursormap
) (fb
, red
, green
, blue
);
424 if (op
& FB_CUR_SETSHAPE
){
427 fb
->cursor
.size
= f
.size
;
428 memset ((void *)&fb
->cursor
.bits
, 0, sizeof (fb
->cursor
.bits
));
429 if (copy_from_user (fb
->cursor
.bits
[0], f
.mask
, bytes
) ||
430 copy_from_user (fb
->cursor
.bits
[1], f
.image
, bytes
))
432 if (f
.size
.fbx
<= 32) {
433 u
= ~(0xffffffff >> f
.size
.fbx
);
434 for (i
= fb
->cursor
.size
.fby
- 1; i
>= 0; i
--) {
435 fb
->cursor
.bits
[0][i
] &= u
;
436 fb
->cursor
.bits
[1][i
] &= fb
->cursor
.bits
[0][i
];
439 u
= ~(0xffffffff >> (f
.size
.fbx
- 32));
440 for (i
= fb
->cursor
.size
.fby
- 1; i
>= 0; i
--) {
441 fb
->cursor
.bits
[0][2*i
+1] &= u
;
442 fb
->cursor
.bits
[1][2*i
] &= fb
->cursor
.bits
[0][2*i
];
443 fb
->cursor
.bits
[1][2*i
+1] &= fb
->cursor
.bits
[0][2*i
+1];
446 (*fb
->setcurshape
) (fb
);
448 if (op
& (FB_CUR_SETCUR
| FB_CUR_SETPOS
| FB_CUR_SETHOT
)){
449 if (op
& FB_CUR_SETCUR
)
450 fb
->cursor
.enable
= f
.enable
;
451 if (op
& FB_CUR_SETPOS
)
452 fb
->cursor
.cpos
= f
.pos
;
453 if (op
& FB_CUR_SETHOT
)
454 fb
->cursor
.chot
= f
.hot
;
455 (*fb
->setcursor
) (fb
);
460 static unsigned char hw_cursor_cmap
[2] = { 0, 0xff };
462 static void sbusfb_cursor(struct display
*p
, int mode
, int x
, int y
)
464 struct fb_info_sbusfb
*fb
= sbusfbinfod(p
);
468 fb
->cursor
.enable
= 0;
469 (*fb
->setcursor
)(fb
);
470 fb
->hw_cursor_shown
= 0;
475 if (!fb
->hw_cursor_shown
) {
476 fb
->cursor
.size
.fbx
= p
->fontwidth
;
477 fb
->cursor
.size
.fby
= p
->fontheight
;
478 fb
->cursor
.chot
.fbx
= 0;
479 fb
->cursor
.chot
.fby
= 0;
480 fb
->cursor
.enable
= 1;
481 memset (fb
->cursor
.bits
, 0, sizeof (fb
->cursor
.bits
));
482 fb
->cursor
.bits
[0][p
->fontheight
- 2] = (0xffffffff << (32 - p
->fontwidth
));
483 fb
->cursor
.bits
[1][p
->fontheight
- 2] = (0xffffffff << (32 - p
->fontwidth
));
484 fb
->cursor
.bits
[0][p
->fontheight
- 1] = (0xffffffff << (32 - p
->fontwidth
));
485 fb
->cursor
.bits
[1][p
->fontheight
- 1] = (0xffffffff << (32 - p
->fontwidth
));
486 (*fb
->setcursormap
) (fb
, hw_cursor_cmap
, hw_cursor_cmap
, hw_cursor_cmap
);
487 (*fb
->setcurshape
) (fb
);
488 fb
->hw_cursor_shown
= 1;
490 fb
->cursor
.cpos
.fbx
= (x
<< 3) + fb
->x_margin
; /* x * p->fontwidth */
491 if (p
->fontheight
== 16)
492 fb
->cursor
.cpos
.fby
= (y
<< 4) + fb
->y_margin
;
494 fb
->cursor
.cpos
.fby
= (y
* p
->fontheight
) + fb
->y_margin
;
495 (*fb
->setcursor
)(fb
);
504 static int sbusfb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
505 struct fb_info
*info
)
507 if (con
== currcon
) /* current console? */
508 return fb_get_cmap(cmap
, &fb_display
[con
].var
, kspc
, sbusfb_getcolreg
, info
);
509 else if (fb_display
[con
].cmap
.len
) /* non default colormap? */
510 fb_copy_cmap(&fb_display
[con
].cmap
, cmap
, kspc
? 0 : 2);
512 fb_copy_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
), cmap
, kspc
? 0 : 2);
520 static int sbusfb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
521 struct fb_info
*info
)
525 if (!fb_display
[con
].cmap
.len
) { /* no colormap allocated? */
526 if ((err
= fb_alloc_cmap(&fb_display
[con
].cmap
, 1<<fb_display
[con
].var
.bits_per_pixel
, 0)))
529 if (con
== currcon
) { /* current console? */
530 err
= fb_set_cmap(cmap
, &fb_display
[con
].var
, kspc
, sbusfb_setcolreg
, info
);
532 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
535 (*fb
->loadcmap
)(fb
, cmap
->start
, cmap
->len
);
539 fb_copy_cmap(cmap
, &fb_display
[con
].cmap
, kspc
? 0 : 1);
543 static int sbusfb_ioctl(struct inode
*inode
, struct file
*file
, u_int cmd
,
544 u_long arg
, int con
, struct fb_info
*info
)
546 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
550 case FBIOGTYPE
: /* return frame buffer type */
551 copy_to_user_ret((struct fbtype
*)arg
, &fb
->type
, sizeof(struct fbtype
), -EFAULT
);
554 struct fbgattr
*fba
= (struct fbgattr
*) arg
;
556 i
= verify_area (VERIFY_WRITE
, (void *) arg
, sizeof (struct fbgattr
));
558 __put_user_ret(fb
->emulations
[0], &fba
->real_type
, -EFAULT
);
559 __put_user_ret(0, &fba
->owner
, -EFAULT
);
560 __copy_to_user_ret(&fba
->fbtype
, &fb
->type
,
561 sizeof(struct fbtype
), -EFAULT
);
562 __put_user_ret(0, &fba
->sattr
.flags
, -EFAULT
);
563 __put_user_ret(fb
->type
.fb_type
, &fba
->sattr
.emu_type
, -EFAULT
);
564 __put_user_ret(-1, &fba
->sattr
.dev_specific
[0], -EFAULT
);
565 for (i
= 0; i
< 4; i
++)
566 put_user_ret(fb
->emulations
[i
], &fba
->emu_types
[i
], -EFAULT
);
570 i
= verify_area (VERIFY_READ
, (void *) arg
, sizeof (struct fbsattr
));
574 if (fb
->consolecnt
&&
575 vt_cons
[fb
->lastconsole
]->vc_mode
== KD_TEXT
)
577 get_user_ret(i
, (int *)arg
, -EFAULT
);
579 if (!fb
->blanked
|| !fb
->unblank
)
581 if (fb
->consolecnt
|| (fb
->open
&& fb
->mmaped
))
585 if (fb
->blanked
|| !fb
->blank
)
592 put_user_ret(fb
->blanked
, (int *) arg
, -EFAULT
);
596 int end
, count
, index
;
601 i
= verify_area (VERIFY_READ
, (void *) arg
, sizeof (struct fbcmap
));
603 cmap
= (struct fbcmap
*) arg
;
604 __get_user_ret(count
, &cmap
->count
, -EFAULT
);
605 __get_user_ret(index
, &cmap
->index
, -EFAULT
);
606 if ((index
< 0) || (index
> 255))
608 if (index
+ count
> 256)
610 __get_user_ret(rp
, &cmap
->red
, -EFAULT
);
611 __get_user_ret(gp
, &cmap
->green
, -EFAULT
);
612 __get_user_ret(bp
, &cmap
->blue
, -EFAULT
);
613 if(verify_area (VERIFY_WRITE
, rp
, count
)) return -EFAULT
;
614 if(verify_area (VERIFY_WRITE
, gp
, count
)) return -EFAULT
;
615 if(verify_area (VERIFY_WRITE
, bp
, count
)) return -EFAULT
;
617 for (i
= index
; i
< end
; i
++){
618 __put_user_ret(fb
->color_map
CM(i
,0), rp
, -EFAULT
);
619 __put_user_ret(fb
->color_map
CM(i
,1), gp
, -EFAULT
);
620 __put_user_ret(fb
->color_map
CM(i
,2), bp
, -EFAULT
);
623 (*fb
->loadcmap
)(fb
, index
, count
);
626 case FBIOPUTCMAP
: { /* load color map entries */
628 int end
, count
, index
;
633 i
= verify_area (VERIFY_READ
, (void *) arg
, sizeof (struct fbcmap
));
635 cmap
= (struct fbcmap
*) arg
;
636 __get_user_ret(count
, &cmap
->count
, -EFAULT
);
637 __get_user_ret(index
, &cmap
->index
, -EFAULT
);
638 if ((index
< 0) || (index
> 255))
640 if (index
+ count
> 256)
642 __get_user_ret(rp
, &cmap
->red
, -EFAULT
);
643 __get_user_ret(gp
, &cmap
->green
, -EFAULT
);
644 __get_user_ret(bp
, &cmap
->blue
, -EFAULT
);
645 if(verify_area (VERIFY_READ
, rp
, count
)) return -EFAULT
;
646 if(verify_area (VERIFY_READ
, gp
, count
)) return -EFAULT
;
647 if(verify_area (VERIFY_READ
, bp
, count
)) return -EFAULT
;
650 for (i
= index
; i
< end
; i
++){
651 __get_user_ret(fb
->color_map
CM(i
,0), rp
, -EFAULT
);
652 __get_user_ret(fb
->color_map
CM(i
,1), gp
, -EFAULT
);
653 __get_user_ret(fb
->color_map
CM(i
,2), bp
, -EFAULT
);
656 (*fb
->loadcmap
)(fb
, index
, count
);
660 struct fbcurpos
*p
= (struct fbcurpos
*) arg
;
661 if (!fb
->setcursor
) return -EINVAL
;
662 if(verify_area (VERIFY_WRITE
, p
, sizeof (struct fbcurpos
)))
664 __put_user_ret(fb
->cursor
.hwsize
.fbx
, &p
->fbx
, -EFAULT
);
665 __put_user_ret(fb
->cursor
.hwsize
.fby
, &p
->fby
, -EFAULT
);
669 if (!fb
->setcursor
) return -EINVAL
;
670 if (fb
->consolecnt
) {
671 if (vt_cons
[fb
->lastconsole
]->vc_mode
== KD_TEXT
)
672 return -EINVAL
; /* Don't let graphics programs hide our nice text cursor */
673 fb
->hw_cursor_shown
= 0; /* Forget state of our text cursor */
675 return sbus_hw_scursor ((struct fbcursor
*) arg
, fb
);
678 if (!fb
->setcursor
) return -EINVAL
;
679 /* Don't let graphics programs move our nice text cursor */
680 if (fb
->consolecnt
) {
681 if (vt_cons
[fb
->lastconsole
]->vc_mode
== KD_TEXT
)
682 return -EINVAL
; /* Don't let graphics programs move our nice text cursor */
684 if (copy_from_user(&fb
->cursor
.cpos
, (void *)arg
, sizeof(struct fbcurpos
)))
686 (*fb
->setcursor
) (fb
);
689 /* FIXME: Call here possible fb specific ioctl */
696 * Setup: parse used options
699 void sbusfb_setup(char *options
, int *ints
)
701 if (!strncmp(options
, "nomargins", 9))
705 static int sbusfbcon_switch(int con
, struct fb_info
*info
)
707 /* Do we have to save the colormap? */
708 if (fb_display
[currcon
].cmap
.len
)
709 fb_get_cmap(&fb_display
[currcon
].cmap
, &fb_display
[currcon
].var
, 1, sbusfb_getcolreg
, info
);
711 sbusfbinfo(info
)->lastconsole
= con
;
713 /* Install new colormap */
714 do_install_cmap(con
, info
);
719 * Update the `var' structure (called by fbcon.c)
722 static int sbusfbcon_updatevar(int con
, struct fb_info
*info
)
732 static void sbusfbcon_blank(int blank
, struct fb_info
*info
)
735 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
742 for (i
= 0; i
< 256; i
++) {
744 for (j
= 0; j
< 3; j
++) {
749 do_install_cmap(currcon
, info
);
754 * Read a single color register and split it into
755 * colors/transparent. Return != 0 for invalid regno.
758 static int sbusfb_getcolreg(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
,
759 u_int
*transp
, struct fb_info
*info
)
761 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
763 if (!fb
->color_map
|| regno
> 255)
765 *red
= fb
->color_map
CM(regno
, 0);
766 *green
= fb
->color_map
CM(regno
, 1);
767 *blue
= fb
->color_map
CM(regno
, 2);
773 * Set a single color register. The values supplied are already
774 * rounded down to the hardware's capabilities (according to the
775 * entries in the var structure). Return != 0 for invalid regno.
778 static int sbusfb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
779 u_int transp
, struct fb_info
*info
)
781 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
783 if (!fb
->color_map
|| regno
> 255)
785 fb
->color_map
CM(regno
, 0) = red
;
786 fb
->color_map
CM(regno
, 1) = green
;
787 fb
->color_map
CM(regno
, 2) = blue
;
792 static void do_install_cmap(int con
, struct fb_info
*info
)
794 struct fb_info_sbusfb
*fb
= sbusfbinfo(info
);
798 if (fb_display
[con
].cmap
.len
)
799 fb_set_cmap(&fb_display
[con
].cmap
, &fb_display
[con
].var
, 1,
800 sbusfb_setcolreg
, info
);
802 fb_set_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
),
803 &fb_display
[con
].var
, 1, sbusfb_setcolreg
, info
);
805 (*fb
->loadcmap
)(fb
, 0, 256);
812 __initfunc(static void sbusfb_init_fb(int node
, int parent
, int fbtype
,
813 struct linux_sbus_device
*sbdp
))
815 struct fb_fix_screeninfo
*fix
;
816 struct fb_var_screeninfo
*var
;
817 struct display
*disp
;
818 struct fb_info_sbusfb
*fb
;
820 int linebytes
, w
, h
, depth
;
824 fb
= kmalloc(sizeof(struct fb_info_sbusfb
), GFP_ATOMIC
);
826 prom_printf("Could not allocate sbusfb structure\n");
829 memset(fb
, 0, sizeof(struct fb_info_sbusfb
));
835 fb
->prom_node
= node
;
836 fb
->prom_parent
= parent
;
839 fb
->iospace
= sbdp
->reg_addrs
[0].which_io
;
841 type
->fb_type
= fbtype
;
842 memset(&fb
->emulations
, 0xff, sizeof(fb
->emulations
));
843 fb
->emulations
[0] = fbtype
;
846 disp
->screen_base
= prom_getintdefault(node
, "address", 0);
849 type
->fb_height
= h
= prom_getintdefault(node
, "height", 900);
850 type
->fb_width
= w
= prom_getintdefault(node
, "width", 1152);
851 type
->fb_depth
= depth
= (fbtype
== FBTYPE_SUN2BW
) ? 1 : 8;
852 linebytes
= prom_getintdefault(node
, "linebytes", w
* depth
/ 8);
853 type
->fb_size
= PAGE_ALIGN((linebytes
) * h
);
856 for (margin
= 0; def_margins
[margin
].depth
; margin
++)
857 if (w
== def_margins
[margin
].xres
&&
858 h
== def_margins
[margin
].yres
&&
859 depth
== def_margins
[margin
].depth
) {
860 fb
->x_margin
= def_margins
[margin
].x_margin
;
861 fb
->y_margin
= def_margins
[margin
].y_margin
;
864 fb
->x_margin
+= ((w
- 2*fb
->x_margin
) & 15) / 2;
865 fb
->y_margin
+= ((h
- 2*fb
->y_margin
) & 15) / 2;
867 var
->xres_virtual
= w
;
868 var
->yres_virtual
= h
;
869 var
->xres
= w
- 2*fb
->x_margin
;
870 var
->yres
= h
- 2*fb
->y_margin
;
872 var
->bits_per_pixel
= depth
;
873 var
->height
= var
->width
= -1;
874 var
->pixclock
= 10000;
875 var
->vmode
= FB_VMODE_NONINTERLACED
;
876 var
->red
.length
= var
->green
.length
= var
->blue
.length
= 8;
878 fix
->line_length
= linebytes
;
879 fix
->smem_len
= type
->fb_size
;
880 fix
->type
= FB_TYPE_PACKED_PIXELS
;
881 fix
->visual
= FB_VISUAL_PSEUDOCOLOR
;
884 fb
->info
.fbops
= &sbusfb_ops
;
885 fb
->info
.disp
= disp
;
886 fb
->info
.fontname
[0] = '\0';
887 fb
->info
.changevar
= NULL
;
888 fb
->info
.switch_con
= &sbusfbcon_switch
;
889 fb
->info
.updatevar
= &sbusfbcon_updatevar
;
890 fb
->info
.blank
= &sbusfbcon_blank
;
892 fb
->cursor
.hwsize
.fbx
= 32;
893 fb
->cursor
.hwsize
.fby
= 32;
896 fb
->color_map
= kmalloc(256 * 3, GFP_ATOMIC
);
899 #ifdef CONFIG_FB_CREATOR
901 p
= creatorfb_init(fb
); break;
903 #ifdef CONFIG_FB_CGSIX
904 case FBTYPE_SUNFAST_COLOR
:
905 p
= cgsixfb_init(fb
); break;
907 #ifdef CONFIG_FB_CGTHREE
908 case FBTYPE_SUN3COLOR
:
909 p
= cgthreefb_init(fb
); break;
912 case FBTYPE_TCXCOLOR
:
913 p
= tcxfb_init(fb
); break;
917 p
= leofb_init(fb
); break;
919 #ifdef CONFIG_FB_BWTWO
921 p
= bwtwofb_init(fb
); break;
923 #ifdef CONFIG_FB_CGFOURTEEN
924 case FBTYPE_MDICOLOR
:
925 p
= cgfourteenfb_init(fb
); break;
934 disp
->dispsw
= &fb
->dispsw
;
936 fb
->dispsw
.cursor
= sbusfb_cursor
;
937 fb
->setup
= fb
->dispsw
.setup
;
938 fb
->dispsw
.setup
= sbusfb_disp_setup
;
941 disp
->visual
= fix
->visual
;
942 disp
->type
= fix
->type
;
943 disp
->type_aux
= fix
->type_aux
;
944 disp
->line_length
= fix
->line_length
;
946 sbusfb_set_var(var
, -1, &fb
->info
);
948 if (register_framebuffer(&fb
->info
) < 0) {
952 printk("fb%d: %s\n", GET_FB_IDX(fb
->info
.node
), p
);
955 static inline int known_card(char *name
)
958 for (p
= name
; *p
&& *p
!= ','; p
++);
959 if (*p
== ',') name
= p
+ 1;
960 if (!strcmp(p
, "cgsix") || !strcmp(p
, "cgthree+"))
961 return FBTYPE_SUNFAST_COLOR
;
962 if (!strcmp(p
, "cgthree") || !strcmp(p
, "cgRDI"))
963 return FBTYPE_SUN3COLOR
;
964 if (!strcmp(p
, "cgfourteen"))
965 return FBTYPE_MDICOLOR
;
966 if (!strcmp(p
, "leo"))
967 return FBTYPE_SUNLEO
;
968 if (!strcmp(p
, "bwtwo"))
969 return FBTYPE_SUN2BW
;
970 if (!strcmp(p
, "tcx"))
971 return FBTYPE_TCXCOLOR
;
972 return FBTYPE_NOTYPE
;
975 __initfunc(void sbusfb_init(void))
977 int node
, root
, type
;
978 struct linux_sbus_device
*sbdp
;
979 struct linux_sbus
*sbus
;
982 #ifdef CONFIG_FB_CREATOR
983 root
= prom_getchild(prom_root_node
);
984 for (node
= prom_searchsiblings(root
, "SUNW,ffb"); node
;
985 node
= prom_searchsiblings(prom_getsibling(node
), "SUNW,ffb")) {
986 sbusfb_init_fb(node
, prom_root_node
, FBTYPE_CREATOR
, NULL
);
990 sbusfb_init_fb(0, 0, FBTYPE_SUN2BW
, NULL
);
992 #if defined(CONFIG_FB_CGFOURTEEN) && !defined(__sparc_v9__)
993 root
= prom_getchild(prom_root_node
);
994 root
= prom_searchsiblings(root
, "obio");
996 (node
= prom_searchsiblings(prom_getchild(root
), "cgfourteen"))) {
997 sbusfb_init_fb(node
, root
, FBTYPE_MDICOLOR
, NULL
);
1000 if (!SBus_chain
) return;
1001 for_all_sbusdev(sbdp
, sbus
) {
1002 type
= known_card(sbdp
->prom_name
);
1003 if (type
== FBTYPE_NOTYPE
) continue;
1004 if (prom_getproperty(sbdp
->prom_node
, "emulation", prom_name
, sizeof(prom_name
)) > 0) {
1005 type
= known_card(prom_name
);
1006 if (type
== FBTYPE_NOTYPE
) type
= known_card(sbdp
->prom_name
);
1008 prom_apply_sbus_ranges(sbdp
->my_bus
, &sbdp
->reg_addrs
[0],
1009 sbdp
->num_registers
, sbdp
);
1010 sbusfb_init_fb(sbdp
->prom_node
, sbdp
->my_bus
->prom_node
, type
, sbdp
);