pre-2.1.109-2..
[davej-history.git] / drivers / video / sbusfb.c
blob833c888ad938ea6273f90a4888a3531843e667e7
1 /*
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
21 * more details.
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>
29 #include <linux/mm.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>
35 #include <linux/fb.h>
36 #include <linux/selection.h>
37 #include <linux/init.h>
38 #include <linux/console.h>
39 #include <linux/kd.h>
40 #include <linux/vt_kern.h>
42 #include <asm/uaccess.h>
44 #include "sbusfb.h"
47 * Interface used by the world
50 void sbusfb_init(void);
51 void sbusfb_setup(char *options, int *ints);
53 static int currcon;
54 static int nomargins __initdata = 0;
55 static struct {
56 int depth;
57 int xres, yres;
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 },
67 { 0 },
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);
104 * Internal routines
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);
127 if (user) {
128 if (fb->open) return -EBUSY;
129 fb->mmaped = 0;
130 fb->open = 1;
131 fb->vtconsole = -1;
132 } else
133 fb->consolecnt++;
134 MOD_INC_USE_COUNT;
135 return 0;
138 static int sbusfb_release(struct fb_info *info, int user)
140 struct fb_info_sbusfb *fb = sbusfbinfo(info);
142 if (user) {
143 if (fb->vtconsole != -1) {
144 vt_cons[fb->vtconsole]->vc_mode = KD_TEXT;
145 if (fb->mmaped)
146 sbusfb_clear_margin(&fb_display[fb->vtconsole], 0);
148 fb->open = 0;
149 } else
150 fb->consolecnt--;
151 MOD_DEC_USE_COUNT;
152 return 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;
161 int i;
163 size = vma->vm_end - vma->vm_start;
164 if (vma->vm_offset & ~PAGE_MASK)
165 return -ENXIO;
167 /* To stop the swapper from even considering these pages */
168 vma->vm_flags |= (VM_SHM| VM_LOCKED);
170 #ifdef __sparc_v9__
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)
178 continue;
179 if (fb->mmap_map[i].voff >= map_offset)
180 break;
181 if (max < 0 || fb->mmap_map[i].size > fb->mmap_map[max].size)
182 max = i;
184 if (max >= 0) {
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)))
190 break;
191 if (alignment > PAGE_SIZE) {
192 j = alignment;
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;
204 #endif
206 /* Each page, see which map applies */
207 for (page = 0; page < size; ){
208 map_size = 0;
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;
213 break;
215 if (!map_size){
216 page += PAGE_SIZE;
217 continue;
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);
222 if (r)
223 return -EAGAIN;
224 page += map_size;
227 vma->vm_file = file;
228 file->f_count++;
229 vma->vm_flags |= VM_IO;
230 if (!fb->mmaped) {
231 fb->mmaped = 1;
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)
236 (*fb->unblank)(fb);
238 return 0;
241 static void sbusfb_clear_margin(struct display *p, int s)
243 struct fb_info_sbusfb *fb = sbusfbinfod(p);
245 if (fb->fill) {
246 unsigned short rects [16];
248 rects [0] = 0;
249 rects [1] = 0;
250 rects [2] = fb->var.xres_virtual;
251 rects [3] = fb->y_margin;
252 rects [4] = 0;
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);
265 } else {
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;
271 he = fb->var.yres;
272 if (fb->var.bits_per_pixel == 1) {
273 fb_base -= (skip_bytes + fb->x_margin) / 8;
274 skip_bytes /= 8;
275 scr_size /= 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);
283 } else {
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);
302 if (fb->setup)
303 fb->setup(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));
317 return 0;
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));
330 return 0;
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)
340 #if 1
341 return -EINVAL;
342 #else
343 struct display *display;
344 int oldbpp = -1, err;
345 int activate = var->activate;
346 struct fb_info_sbusfb *fb = sbusfbinfo(info);
348 if (con >= 0)
349 display = &fb_display[con];
350 else
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 ||
357 var->nonstd ||
358 (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
359 return -EINVAL;
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;
364 display->var = *var;
366 if (oldbpp != var->bits_per_pixel) {
367 if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
368 return err;
369 do_install_cmap(con, info);
371 return 0;
372 #endif
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)
385 return -EINVAL;
386 else
387 return 0;
391 * Hardware cursor
394 static int sbus_hw_scursor (struct fbcursor *cursor, struct fb_info_sbusfb *fb)
396 int op;
397 int i, bytes = 0;
398 struct fbcursor f;
399 char red[2], green[2], blue[2];
401 if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
402 return -EFAULT;
403 op = f.set;
404 if (op & FB_CUR_SETSHAPE){
405 if ((u32) f.size.fbx > fb->cursor.hwsize.fbx)
406 return -EINVAL;
407 if ((u32) f.size.fby > fb->cursor.hwsize.fby)
408 return -EINVAL;
409 if (f.size.fbx > 32)
410 bytes = f.size.fby << 3;
411 else
412 bytes = f.size.fby << 2;
414 if (op & FB_CUR_SETCMAP){
415 if (f.cmap.index || f.cmap.count != 2)
416 return -EINVAL;
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))
420 return -EFAULT;
422 if (op & FB_CUR_SETCMAP)
423 (*fb->setcursormap) (fb, red, green, blue);
424 if (op & FB_CUR_SETSHAPE){
425 u32 u;
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))
431 return -EFAULT;
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];
438 } else {
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);
457 return 0;
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);
466 switch (mode) {
467 case CM_ERASE:
468 fb->cursor.enable = 0;
469 (*fb->setcursor)(fb);
470 fb->hw_cursor_shown = 0;
471 break;
473 case CM_MOVE:
474 case CM_DRAW:
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;
493 else
494 fb->cursor.cpos.fby = (y * p->fontheight) + fb->y_margin;
495 (*fb->setcursor)(fb);
496 break;
501 * Get the Colormap
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);
511 else
512 fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
513 return 0;
517 * Set the Colormap
520 static int sbusfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
521 struct fb_info *info)
523 int err;
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)))
527 return err;
529 if (con == currcon) { /* current console? */
530 err = fb_set_cmap(cmap, &fb_display[con].var, kspc, sbusfb_setcolreg, info);
531 if (!err) {
532 struct fb_info_sbusfb *fb = sbusfbinfo(info);
534 if (fb->loadcmap)
535 (*fb->loadcmap)(fb, cmap->start, cmap->len);
537 return err;
538 } else
539 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
540 return 0;
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);
547 int i;
549 switch (cmd){
550 case FBIOGTYPE: /* return frame buffer type */
551 copy_to_user_ret((struct fbtype *)arg, &fb->type, sizeof(struct fbtype), -EFAULT);
552 break;
553 case FBIOGATTR: {
554 struct fbgattr *fba = (struct fbgattr *) arg;
556 i = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct fbgattr));
557 if (i) return i;
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);
567 break;
569 case FBIOSATTR:
570 i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbsattr));
571 if (i) return i;
572 return -EINVAL;
573 case FBIOSVIDEO:
574 if (fb->consolecnt &&
575 vt_cons[fb->lastconsole]->vc_mode == KD_TEXT)
576 break;
577 get_user_ret(i, (int *)arg, -EFAULT);
578 if (i){
579 if (!fb->blanked || !fb->unblank)
580 break;
581 if (fb->consolecnt || (fb->open && fb->mmaped))
582 (*fb->unblank)(fb);
583 fb->blanked = 0;
584 } else {
585 if (fb->blanked || !fb->blank)
586 break;
587 (*fb->blank)(fb);
588 fb->blanked = 1;
590 break;
591 case FBIOGVIDEO:
592 put_user_ret(fb->blanked, (int *) arg, -EFAULT);
593 break;
594 case FBIOGETCMAP: {
595 char *rp, *gp, *bp;
596 int end, count, index;
597 struct fbcmap *cmap;
599 if (!fb->loadcmap)
600 return -EINVAL;
601 i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap));
602 if (i) return i;
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))
607 return -EINVAL;
608 if (index + count > 256)
609 count = 256 - index;
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;
616 end = index + count;
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);
621 rp++; gp++; bp++;
623 (*fb->loadcmap)(fb, index, count);
624 break;
626 case FBIOPUTCMAP: { /* load color map entries */
627 char *rp, *gp, *bp;
628 int end, count, index;
629 struct fbcmap *cmap;
631 if (!fb->loadcmap)
632 return -EINVAL;
633 i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap));
634 if (i) return i;
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))
639 return -EINVAL;
640 if (index + count > 256)
641 count = 256 - index;
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;
649 end = index + count;
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);
654 rp++; gp++; bp++;
656 (*fb->loadcmap)(fb, index, count);
657 break;
659 case FBIOGCURMAX: {
660 struct fbcurpos *p = (struct fbcurpos *) arg;
661 if (!fb->setcursor) return -EINVAL;
662 if(verify_area (VERIFY_WRITE, p, sizeof (struct fbcurpos)))
663 return -EFAULT;
664 __put_user_ret(fb->cursor.hwsize.fbx, &p->fbx, -EFAULT);
665 __put_user_ret(fb->cursor.hwsize.fby, &p->fby, -EFAULT);
666 break;
668 case FBIOSCURSOR:
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);
677 case FBIOSCURPOS:
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)))
685 return -EFAULT;
686 (*fb->setcursor) (fb);
687 break;
688 default:
689 /* FIXME: Call here possible fb specific ioctl */
690 return -EINVAL;
692 return 0;
696 * Setup: parse used options
699 void sbusfb_setup(char *options, int *ints)
701 if (!strncmp(options, "nomargins", 9))
702 nomargins = 1;
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;
712 currcon = con;
713 /* Install new colormap */
714 do_install_cmap(con, info);
715 return 0;
719 * Update the `var' structure (called by fbcon.c)
722 static int sbusfbcon_updatevar(int con, struct fb_info *info)
724 /* Nothing */
725 return 0;
729 * Blank the display.
732 static void sbusfbcon_blank(int blank, struct fb_info *info)
734 #if 0
735 struct fb_info_sbusfb *fb = sbusfbinfo(info);
736 int i, j;
738 if (!fb->cmap_adr)
739 return;
741 if (blank)
742 for (i = 0; i < 256; i++) {
743 *fb->cmap_adr = i;
744 for (j = 0; j < 3; j++) {
745 *fb->cmap_data = 0;
748 else
749 do_install_cmap(currcon, info);
750 #endif
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)
764 return 1;
765 *red = fb->color_map CM(regno, 0);
766 *green = fb->color_map CM(regno, 1);
767 *blue = fb->color_map CM(regno, 2);
768 return 0;
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)
784 return 1;
785 fb->color_map CM(regno, 0) = red;
786 fb->color_map CM(regno, 1) = green;
787 fb->color_map CM(regno, 2) = blue;
788 return 0;
792 static void do_install_cmap(int con, struct fb_info *info)
794 struct fb_info_sbusfb *fb = sbusfbinfo(info);
796 if (con != currcon)
797 return;
798 if (fb_display[con].cmap.len)
799 fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
800 sbusfb_setcolreg, info);
801 else
802 fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
803 &fb_display[con].var, 1, sbusfb_setcolreg, info);
804 if (fb->loadcmap)
805 (*fb->loadcmap)(fb, 0, 256);
809 * Initialisation
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;
819 struct fbtype *type;
820 int linebytes, w, h, depth;
821 char *p = NULL;
822 int margin;
824 fb = kmalloc(sizeof(struct fb_info_sbusfb), GFP_ATOMIC);
825 if (!fb) {
826 prom_printf("Could not allocate sbusfb structure\n");
827 return;
829 memset(fb, 0, sizeof(struct fb_info_sbusfb));
830 fix = &fb->fix;
831 var = &fb->var;
832 disp = &fb->disp;
833 type = &fb->type;
835 fb->prom_node = node;
836 fb->prom_parent = parent;
837 fb->sbdp = sbdp;
838 if (sbdp)
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;
845 #ifndef __sparc_v9__
846 disp->screen_base = prom_getintdefault(node, "address", 0);
847 #endif
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);
855 if (!nomargins)
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;
862 break;
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;
883 fb->info.node = -1;
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;
895 if (depth > 1)
896 fb->color_map = kmalloc(256 * 3, GFP_ATOMIC);
898 switch(fbtype) {
899 #ifdef CONFIG_FB_CREATOR
900 case FBTYPE_CREATOR:
901 p = creatorfb_init(fb); break;
902 #endif
903 #ifdef CONFIG_FB_CGSIX
904 case FBTYPE_SUNFAST_COLOR:
905 p = cgsixfb_init(fb); break;
906 #endif
907 #ifdef CONFIG_FB_CGTHREE
908 case FBTYPE_SUN3COLOR:
909 p = cgthreefb_init(fb); break;
910 #endif
911 #ifdef CONFIG_FB_TCX
912 case FBTYPE_TCXCOLOR:
913 p = tcxfb_init(fb); break;
914 #endif
915 #ifdef CONFIG_FB_LEO
916 case FBTYPE_SUNLEO:
917 p = leofb_init(fb); break;
918 #endif
919 #ifdef CONFIG_FB_BWTWO
920 case FBTYPE_SUN2BW:
921 p = bwtwofb_init(fb); break;
922 #endif
923 #ifdef CONFIG_FB_CGFOURTEEN
924 case FBTYPE_MDICOLOR:
925 p = cgfourteenfb_init(fb); break;
926 #endif
929 if (!p) {
930 kfree(fb);
931 return;
934 disp->dispsw = &fb->dispsw;
935 if (fb->setcursor)
936 fb->dispsw.cursor = sbusfb_cursor;
937 fb->setup = fb->dispsw.setup;
938 fb->dispsw.setup = sbusfb_disp_setup;
940 disp->var = *var;
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) {
949 kfree(fb);
950 return;
952 printk("fb%d: %s\n", GET_FB_IDX(fb->info.node), p);
955 static inline int known_card(char *name)
957 char *p;
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;
980 char prom_name[40];
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);
988 #endif
989 #ifdef CONFIG_SUN4
990 sbusfb_init_fb(0, 0, FBTYPE_SUN2BW, NULL);
991 #endif
992 #if defined(CONFIG_FB_CGFOURTEEN) && !defined(__sparc_v9__)
993 root = prom_getchild(prom_root_node);
994 root = prom_searchsiblings(root, "obio");
995 if (root &&
996 (node = prom_searchsiblings(prom_getchild(root), "cgfourteen"))) {
997 sbusfb_init_fb(node, root, FBTYPE_MDICOLOR, NULL);
999 #endif
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);