Merge branch 'master' into 83xx
[linux-2.6] / drivers / video / sun3fb.c
1 /*
2  *  linux/drivers/video/sun3fb.c -- Frame buffer driver for Sun3
3  *
4  * (C) 1998 Thomas Bogendoerfer
5  *
6  * This driver is bases on sbusfb.c, which is
7  *
8  *      Copyright (C) 1998 Jakub Jelinek
9  *
10  *  This driver is partly based on the Open Firmware console driver
11  *
12  *      Copyright (C) 1997 Geert Uytterhoeven
13  *
14  *  and SPARC console subsystem
15  *
16  *      Copyright (C) 1995 Peter Zaitcev (zaitcev@yahoo.com)
17  *      Copyright (C) 1995-1997 David S. Miller (davem@caip.rutgers.edu)
18  *      Copyright (C) 1995-1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
19  *      Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
20  *      Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
21  *      Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
22  *
23  *  This file is subject to the terms and conditions of the GNU General Public
24  *  License. See the file COPYING in the main directory of this archive for
25  *  more details.
26  */
27
28 #include <linux/module.h>
29 #include <linux/kernel.h>
30 #include <linux/errno.h>
31 #include <linux/string.h>
32 #include <linux/mm.h>
33 #include <linux/slab.h>
34 #include <linux/vmalloc.h>
35 #include <linux/delay.h>
36 #include <linux/interrupt.h>
37 #include <linux/fb.h>
38 #include <linux/selection.h>
39 #include <linux/init.h>
40 #include <linux/console.h>
41 #include <linux/kd.h>
42 #include <linux/vt_kern.h>
43
44 #include <asm/uaccess.h>
45 #include <asm/pgtable.h>        /* io_remap_page_range() */
46
47 #ifdef CONFIG_SUN3
48 #include <asm/oplib.h>
49 #include <asm/machines.h>
50 #include <asm/idprom.h>
51
52 #define CGFOUR_OBMEM_ADDR 0x1f300000
53 #define BWTWO_OBMEM_ADDR 0x1f000000
54 #define BWTWO_OBMEM50_ADDR 0x00100000
55
56 #endif
57 #ifdef CONFIG_SUN3X
58 #include <asm/sun3x.h>
59 #endif
60 #include <video/sbusfb.h>
61
62 #define DEFAULT_CURSOR_BLINK_RATE       (2*HZ/5)
63
64 #define CURSOR_SHAPE                    1
65 #define CURSOR_BLINK                    2
66
67 #define mymemset(x,y) memset(x,0,y)
68
69     /*
70      *  Interface used by the world
71      */
72
73 int sun3fb_init(void);
74 void sun3fb_setup(char *options);
75
76 static char fontname[40] __initdata = { 0 };
77 static int curblink __initdata = 1;
78
79 static int sun3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
80                         struct fb_info *info);
81 static int sun3fb_get_var(struct fb_var_screeninfo *var, int con,
82                         struct fb_info *info);
83 static int sun3fb_set_var(struct fb_var_screeninfo *var, int con,
84                         struct fb_info *info);
85 static int sun3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
86                         struct fb_info *info);
87 static int sun3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
88                         struct fb_info *info);
89 static int sun3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
90                             u_int transp, struct fb_info *info);
91 static int sun3fb_blank(int blank, struct fb_info *info);
92 static void sun3fb_cursor(struct display *p, int mode, int x, int y);
93 static void sun3fb_clear_margin(struct display *p, int s);
94
95     /*
96      *  Interface to the low level console driver
97      */
98
99 static int sun3fbcon_switch(int con, struct fb_info *info);
100 static int sun3fbcon_updatevar(int con, struct fb_info *info);
101
102     /*
103      *  Internal routines
104      */
105
106 static int sun3fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
107                             u_int *transp, struct fb_info *info);
108
109 static struct fb_ops sun3fb_ops = {
110         .owner =        THIS_MODULE,
111         .fb_get_fix =   sun3fb_get_fix,
112         .fb_get_var =   sun3fb_get_var,
113         .fb_set_var =   sun3fb_set_var,
114         .fb_get_cmap =  sun3fb_get_cmap,
115         .fb_set_cmap =  sun3fb_set_cmap,
116         .fb_setcolreg = sun3fb_setcolreg,
117         .fb_blank =     sun3fb_blank,
118 };
119
120 static void sun3fb_clear_margin(struct display *p, int s)
121 {
122         struct fb_info_sbusfb *fb = sbusfbinfod(p);
123         
124         return;
125
126         if (fb->switch_from_graph)
127                 (*fb->switch_from_graph)(fb);
128         if (fb->fill) {
129                 unsigned short rects [16];
130
131                 rects [0] = 0;
132                 rects [1] = 0;
133                 rects [2] = fb->var.xres_virtual;
134                 rects [3] = fb->y_margin;
135                 rects [4] = 0;
136                 rects [5] = fb->y_margin;
137                 rects [6] = fb->x_margin;
138                 rects [7] = fb->var.yres_virtual;
139                 rects [8] = fb->var.xres_virtual - fb->x_margin;
140                 rects [9] = fb->y_margin;
141                 rects [10] = fb->var.xres_virtual;
142                 rects [11] = fb->var.yres_virtual;
143                 rects [12] = fb->x_margin;
144                 rects [13] = fb->var.yres_virtual - fb->y_margin;
145                 rects [14] = fb->var.xres_virtual - fb->x_margin;
146                 rects [15] = fb->var.yres_virtual;
147                 (*fb->fill)(fb, p, s, 4, rects);
148         } else {
149                 unsigned char *fb_base = fb->info.screen_base, *q;
150                 int skip_bytes = fb->y_margin * fb->var.xres_virtual;
151                 int scr_size = fb->var.xres_virtual * fb->var.yres_virtual;
152                 int h, he, incr, size;
153
154                 he = fb->var.yres;
155                 if (fb->var.bits_per_pixel == 1) {
156                         fb_base -= (skip_bytes + fb->x_margin) / 8;
157                         skip_bytes /= 8;
158                         scr_size /= 8;
159                         mymemset (fb_base, skip_bytes - fb->x_margin / 8);
160                         mymemset (fb_base + scr_size - skip_bytes + fb->x_margin / 8, skip_bytes - fb->x_margin / 8);
161                         incr = fb->var.xres_virtual / 8;
162                         size = fb->x_margin / 8 * 2;
163                         for (q = fb_base + skip_bytes - fb->x_margin / 8, h = 0;
164                              h <= he; q += incr, h++)
165                                 mymemset (q, size);
166                 } else {
167                         fb_base -= (skip_bytes + fb->x_margin);
168                         memset (fb_base, attr_bgcol(p,s), skip_bytes - fb->x_margin);
169                         memset (fb_base + scr_size - skip_bytes + fb->x_margin, attr_bgcol(p,s), skip_bytes - fb->x_margin);
170                         incr = fb->var.xres_virtual;
171                         size = fb->x_margin * 2;
172                         for (q = fb_base + skip_bytes - fb->x_margin, h = 0;
173                              h <= he; q += incr, h++)
174                                 memset (q, attr_bgcol(p,s), size);
175                 }
176         }
177 }
178
179 static void sun3fb_disp_setup(struct display *p)
180 {
181         struct fb_info_sbusfb *fb = sbusfbinfod(p);
182
183         if (fb->setup)
184                 fb->setup(p);   
185         sun3fb_clear_margin(p, 0);
186 }
187
188     /*
189      *  Get the Fixed Part of the Display
190      */
191
192 static int sun3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
193                           struct fb_info *info)
194 {
195         struct fb_info_sbusfb *fb = sbusfbinfo(info);
196
197         memcpy(fix, &fb->fix, sizeof(struct fb_fix_screeninfo));
198         return 0;
199 }
200
201     /*
202      *  Get the User Defined Part of the Display
203      */
204
205 static int sun3fb_get_var(struct fb_var_screeninfo *var, int con,
206                           struct fb_info *info)
207 {
208         struct fb_info_sbusfb *fb = sbusfbinfo(info);
209
210         memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
211         return 0;
212 }
213
214     /*
215      *  Set the User Defined Part of the Display
216      */
217
218 static int sun3fb_set_var(struct fb_var_screeninfo *var, int con,
219                         struct fb_info *info)
220 {
221         struct fb_info_sbusfb *fb = sbusfbinfo(info);
222
223         if (var->xres > fb->var.xres || var->yres > fb->var.yres ||
224             var->xres_virtual > fb->var.xres_virtual ||
225             var->yres_virtual > fb->var.yres_virtual ||
226             var->bits_per_pixel != fb->var.bits_per_pixel ||
227             var->nonstd ||
228             (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
229                 return -EINVAL;
230         memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
231         return 0;
232 }
233
234     /*
235      *  Hardware cursor
236      */
237      
238 static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
239
240 static void
241 sun3fb_cursor_timer_handler(unsigned long dev_addr)
242 {
243         struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)dev_addr;
244         
245         if (!fb->setcursor) return;
246                                 
247         if (fb->cursor.mode & CURSOR_BLINK) {
248                 fb->cursor.enable ^= 1;
249                 fb->setcursor(fb);
250         }
251         
252         fb->cursor.timer.expires = jiffies + fb->cursor.blink_rate;
253         add_timer(&fb->cursor.timer);
254 }
255
256 static void sun3fb_cursor(struct display *p, int mode, int x, int y)
257 {
258         struct fb_info_sbusfb *fb = sbusfbinfod(p);
259         
260         switch (mode) {
261         case CM_ERASE:
262                 fb->cursor.mode &= ~CURSOR_BLINK;
263                 fb->cursor.enable = 0;
264                 (*fb->setcursor)(fb);
265                 break;
266                                   
267         case CM_MOVE:
268         case CM_DRAW:
269                 if (fb->cursor.mode & CURSOR_SHAPE) {
270                         fb->cursor.size.fbx = fontwidth(p);
271                         fb->cursor.size.fby = fontheight(p);
272                         fb->cursor.chot.fbx = 0;
273                         fb->cursor.chot.fby = 0;
274                         fb->cursor.enable = 1;
275                         memset (fb->cursor.bits, 0, sizeof (fb->cursor.bits));
276                         fb->cursor.bits[0][fontheight(p) - 2] = (0xffffffff << (32 - fontwidth(p)));
277                         fb->cursor.bits[1][fontheight(p) - 2] = (0xffffffff << (32 - fontwidth(p)));
278                         fb->cursor.bits[0][fontheight(p) - 1] = (0xffffffff << (32 - fontwidth(p)));
279                         fb->cursor.bits[1][fontheight(p) - 1] = (0xffffffff << (32 - fontwidth(p)));
280                         (*fb->setcursormap) (fb, hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
281                         (*fb->setcurshape) (fb);
282                 }
283                 fb->cursor.mode = CURSOR_BLINK;
284                 if (fontwidthlog(p))
285                         fb->cursor.cpos.fbx = (x << fontwidthlog(p)) + fb->x_margin;
286                 else
287                         fb->cursor.cpos.fbx = (x * fontwidth(p)) + fb->x_margin;
288                 if (fontheightlog(p))
289                         fb->cursor.cpos.fby = (y << fontheightlog(p)) + fb->y_margin;
290                 else
291                         fb->cursor.cpos.fby = (y * fontheight(p)) + fb->y_margin;
292                 (*fb->setcursor)(fb);
293                 break;
294         }
295 }
296
297     /*
298      *  Get the Colormap
299      */
300
301 static int sun3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
302                          struct fb_info *info)
303 {
304         if (con == info->currcon) /* current console? */
305                 return fb_get_cmap(cmap, kspc, sun3fb_getcolreg, info);
306         else if (fb_display[con].cmap.len) /* non default colormap? */
307                 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
308         else
309                 fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
310         return 0;
311 }
312
313     /*
314      *  Set the Colormap
315      */
316
317 static int sun3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
318                          struct fb_info *info)
319 {
320         int err;
321
322         if (!fb_display[con].cmap.len) {        /* no colormap allocated? */
323                 if ((err = fb_alloc_cmap(&fb_display[con].cmap, 1<<fb_display[con].var.bits_per_pixel, 0)))
324                         return err;
325         }
326         if (con == info->currcon) {                     /* current console? */
327                 err = fb_set_cmap(cmap, kspc, info);
328                 if (!err) {
329                         struct fb_info_sbusfb *fb = sbusfbinfo(info);
330                         
331                         if (fb->loadcmap)
332                                 (*fb->loadcmap)(fb, &fb_display[con], cmap->start, cmap->len);
333                 }
334                 return err;
335         } else
336                 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
337         return 0;
338 }
339
340     /*
341      *  Setup: parse used options
342      */
343
344 void __init sun3fb_setup(char *options)
345 {
346         char *p;
347         
348         for (p = options;;) {
349                 if (!strncmp(p, "font=", 5)) {
350                         int i;
351                         
352                         for (i = 0; i < sizeof(fontname) - 1; i++)
353                                 if (p[i+5] == ' ' || !p[i+5])
354                                         break;
355                         memcpy(fontname, p+5, i);
356                         fontname[i] = 0;
357                 } else if (!strncmp(p, "noblink", 7))
358                         curblink = 0;
359                 while (*p && *p != ' ' && *p != ',') p++;
360                 if (*p != ',') break;
361                 p++;
362         }
363
364         return;
365 }
366
367 static int sun3fbcon_switch(int con, struct fb_info *info)
368 {
369         int x_margin, y_margin;
370         struct fb_info_sbusfb *fb = sbusfbinfo(info);
371         int lastconsole;
372     
373         /* Do we have to save the colormap? */
374         if (fb_display[info->currcon].cmap.len)
375                 fb_get_cmap(&fb_display[info->currcon].cmap, 1, sun3fb_getcolreg, info);
376
377         if (info->display_fg) {
378                 lastconsole = info->display_fg->vc_num;
379                 if (lastconsole != con && 
380                     (fontwidth(&fb_display[lastconsole]) != fontwidth(&fb_display[con]) ||
381                      fontheight(&fb_display[lastconsole]) != fontheight(&fb_display[con])))
382                         fb->cursor.mode |= CURSOR_SHAPE;
383         }
384         x_margin = (fb_display[con].var.xres_virtual - fb_display[con].var.xres) / 2;
385         y_margin = (fb_display[con].var.yres_virtual - fb_display[con].var.yres) / 2;
386         if (fb->margins)
387                 fb->margins(fb, &fb_display[con], x_margin, y_margin);
388         if (fb->graphmode || fb->x_margin != x_margin || fb->y_margin != y_margin) {
389                 fb->x_margin = x_margin; fb->y_margin = y_margin;
390                 sun3fb_clear_margin(&fb_display[con], 0);
391         }
392         info->currcon = con;
393         /* Install new colormap */
394         do_install_cmap(con, info);
395         return 0;
396 }
397
398     /*
399      *  Update the `var' structure (called by fbcon.c)
400      */
401
402 static int sun3fbcon_updatevar(int con, struct fb_info *info)
403 {
404         /* Nothing */
405         return 0;
406 }
407
408     /*
409      *  Blank the display.
410      */
411
412 static int sun3fb_blank(int blank, struct fb_info *info)
413 {
414     struct fb_info_sbusfb *fb = sbusfbinfo(info);
415     
416     if (blank && fb->blank)
417         return fb->blank(fb);
418     else if (!blank && fb->unblank)
419         return fb->unblank(fb);
420     return 0;
421 }
422
423     /*
424      *  Read a single color register and split it into
425      *  colors/transparent. Return != 0 for invalid regno.
426      */
427
428 static int sun3fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
429                           u_int *transp, struct fb_info *info)
430 {
431         struct fb_info_sbusfb *fb = sbusfbinfo(info);
432
433         if (!fb->color_map || regno > 255)
434                 return 1;
435         *red = (fb->color_map CM(regno, 0)<<8) | fb->color_map CM(regno, 0);
436         *green = (fb->color_map CM(regno, 1)<<8) | fb->color_map CM(regno, 1);
437         *blue = (fb->color_map CM(regno, 2)<<8) | fb->color_map CM(regno, 2);
438         *transp = 0;
439         return 0;
440 }
441
442
443     /*
444      *  Set a single color register. The values supplied are already
445      *  rounded down to the hardware's capabilities (according to the
446      *  entries in the var structure). Return != 0 for invalid regno.
447      */
448
449 static int sun3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
450                             u_int transp, struct fb_info *info)
451 {
452         struct fb_info_sbusfb *fb = sbusfbinfo(info);
453
454         if (!fb->color_map || regno > 255)
455                 return 1;
456         red >>= 8;
457         green >>= 8;
458         blue >>= 8;
459         fb->color_map CM(regno, 0) = red;
460         fb->color_map CM(regno, 1) = green;
461         fb->color_map CM(regno, 2) = blue;
462         return 0;
463 }
464
465 static int sun3fb_set_font(struct display *p, int width, int height)
466 {
467         int w = p->var.xres_virtual, h = p->var.yres_virtual;
468         int depth = p->var.bits_per_pixel;
469         struct fb_info_sbusfb *fb = sbusfbinfod(p);
470         int x_margin, y_margin;
471         
472         if (depth > 8) depth = 8;
473         x_margin = (w % width) / 2;
474         y_margin = (h % height) / 2;
475
476         p->var.xres = w - 2*x_margin;
477         p->var.yres = h - 2*y_margin;
478         
479         fb->cursor.mode |= CURSOR_SHAPE;
480         
481         if (fb->margins)
482                 fb->margins(fb, p, x_margin, y_margin);
483         if (fb->x_margin != x_margin || fb->y_margin != y_margin) {
484                 fb->x_margin = x_margin; fb->y_margin = y_margin;
485                 sun3fb_clear_margin(p, 0);
486         }
487
488         return 1;
489 }
490
491 void sun3fb_palette(int enter)
492 {
493         int i;
494         struct display *p;
495         
496         for (i = 0; i < MAX_NR_CONSOLES; i++) {
497                 p = &fb_display[i];
498                 if (p->dispsw && p->dispsw->setup == sun3fb_disp_setup &&
499                     p->fb_info->display_fg &&
500                     p->fb_info->display_fg->vc_num == i) {
501                         struct fb_info_sbusfb *fb = sbusfbinfod(p);
502
503                         if (fb->restore_palette) {
504                                 if (enter)
505                                         fb->restore_palette(fb);
506                                 else if (vc_cons[i].d->vc_mode != KD_GRAPHICS)
507                                          vc_cons[i].d->vc_sw->con_set_palette(vc_cons[i].d, color_table);
508                         }
509                 }
510         }
511 }
512
513     /*
514      *  Initialisation
515      */
516 static int __init sun3fb_init_fb(int fbtype, unsigned long addr)
517 {
518         static struct sbus_dev sdb;
519         struct fb_fix_screeninfo *fix;
520         struct fb_var_screeninfo *var;
521         struct display *disp;
522         struct fb_info_sbusfb *fb;
523         struct fbtype *type;
524         int linebytes, w, h, depth;
525         char *p = NULL;
526         
527         fb = kmalloc(sizeof(struct fb_info_sbusfb), GFP_ATOMIC);
528         if (!fb)
529                 return -ENOMEM;
530         
531         memset(fb, 0, sizeof(struct fb_info_sbusfb));
532         fix = &fb->fix;
533         var = &fb->var;
534         disp = &fb->disp;
535         type = &fb->type;
536         
537         sdb.reg_addrs[0].phys_addr = addr;
538         fb->sbdp = &sdb;
539
540         type->fb_type = fbtype;
541         
542         type->fb_height = h = 900;
543         type->fb_width  = w = 1152;
544 sizechange:
545         type->fb_depth  = depth = (fbtype == FBTYPE_SUN2BW) ? 1 : 8;
546         linebytes = w * depth / 8;
547         type->fb_size   = PAGE_ALIGN((linebytes) * h);
548 /*      
549         fb->x_margin = (w & 7) / 2;
550         fb->y_margin = (h & 15) / 2;
551 */
552         fb->x_margin = fb->y_margin = 0;
553
554         var->xres_virtual = w;
555         var->yres_virtual = h;
556         var->xres = w - 2*fb->x_margin;
557         var->yres = h - 2*fb->y_margin;
558         
559         var->bits_per_pixel = depth;
560         var->height = var->width = -1;
561         var->pixclock = 10000;
562         var->vmode = FB_VMODE_NONINTERLACED;
563         var->red.length = var->green.length = var->blue.length = 8;
564
565         fix->line_length = linebytes;
566         fix->smem_len = type->fb_size;
567         fix->type = FB_TYPE_PACKED_PIXELS;
568         fix->visual = FB_VISUAL_PSEUDOCOLOR;
569         
570         fb->info.fbops = &sun3fb_ops;
571         fb->info.disp = disp;
572         fb->info.currcon = -1;
573         strcpy(fb->info.fontname, fontname);
574         fb->info.changevar = NULL;
575         fb->info.switch_con = &sun3fbcon_switch;
576         fb->info.updatevar = &sun3fbcon_updatevar;
577         fb->info.flags = FBINFO_FLAG_DEFAULT;
578         
579         fb->cursor.hwsize.fbx = 32;
580         fb->cursor.hwsize.fby = 32;
581         
582         if (depth > 1 && !fb->color_map) {
583                 if((fb->color_map = kmalloc(256 * 3, GFP_ATOMIC))==NULL)
584                         return -ENOMEM;
585         }
586                         
587         switch(fbtype) {
588 #ifdef CONFIG_FB_CGSIX
589         case FBTYPE_SUNFAST_COLOR:
590                 p = cgsixfb_init(fb); break;
591 #endif
592 #ifdef CONFIG_FB_BWTWO
593         case FBTYPE_SUN2BW:
594                 p = bwtwofb_init(fb); break;
595 #endif
596 #ifdef CONFIG_FB_CGTHREE
597         case FBTYPE_SUN4COLOR:
598         case FBTYPE_SUN3COLOR:
599                 type->fb_size = 0x100000;
600                 p = cgthreefb_init(fb); break;
601 #endif
602         }
603         fix->smem_start = (unsigned long)fb->info.screen_base;  // FIXME
604         
605         if (!p) {
606                 kfree(fb);
607                 return -ENODEV;
608         }
609         
610         if (p == SBUSFBINIT_SIZECHANGE)
611                 goto sizechange;
612
613         disp->dispsw = &fb->dispsw;
614         if (fb->setcursor) {
615                 fb->dispsw.cursor = sun3fb_cursor;
616                 if (curblink) {
617                         fb->cursor.blink_rate = DEFAULT_CURSOR_BLINK_RATE;
618                         init_timer(&fb->cursor.timer);
619                         fb->cursor.timer.expires = jiffies + fb->cursor.blink_rate;
620                         fb->cursor.timer.data = (unsigned long)fb;
621                         fb->cursor.timer.function = sun3fb_cursor_timer_handler;
622                         add_timer(&fb->cursor.timer);
623                 }
624         }
625         fb->cursor.mode = CURSOR_SHAPE;
626         fb->dispsw.set_font = sun3fb_set_font;
627         fb->setup = fb->dispsw.setup;
628         fb->dispsw.setup = sun3fb_disp_setup;
629         fb->dispsw.clear_margins = NULL;
630
631         disp->var = *var;
632         disp->visual = fix->visual;
633         disp->type = fix->type;
634         disp->type_aux = fix->type_aux;
635         disp->line_length = fix->line_length;
636         
637         if (fb->blank)
638                 disp->can_soft_blank = 1;
639
640         sun3fb_set_var(var, -1, &fb->info);
641
642         if (register_framebuffer(&fb->info) < 0) {
643                 kfree(fb);
644                 return -EINVAL;
645         }
646         printk("fb%d: %s\n", fb->info.node, p);
647
648         return 0;
649 }
650
651
652 int __init sun3fb_init(void)
653 {
654         extern int con_is_present(void);
655         unsigned long addr;
656         char p4id;
657         
658         if (!con_is_present()) return -ENODEV;
659 #ifdef CONFIG_SUN3
660         switch(*(romvec->pv_fbtype))
661         {
662         case FBTYPE_SUN2BW:
663                 addr = 0xfe20000;
664                 return sun3fb_init_fb(FBTYPE_SUN2BW, addr);
665         case FBTYPE_SUN3COLOR:
666         case FBTYPE_SUN4COLOR:
667                 if(idprom->id_machtype != (SM_SUN3|SM_3_60)) {
668                         printk("sun3fb: cgthree/four only supported on 3/60\n");
669                         return -ENODEV;
670                 }
671                 
672                 addr = CGFOUR_OBMEM_ADDR;
673                 return sun3fb_init_fb(*(romvec->pv_fbtype), addr);
674         default:
675                 printk("sun3fb: unsupported framebuffer\n");
676                 return -ENODEV;
677         }
678 #else
679         addr = SUN3X_VIDEO_BASE;
680         p4id = *(char *)SUN3X_VIDEO_P4ID;
681
682         p4id = (p4id == 0x45) ? p4id : (p4id & 0xf0);
683         switch (p4id) {
684                 case 0x00:
685                         return sun3fb_init_fb(FBTYPE_SUN2BW, addr);
686 #if 0 /* not yet */
687                 case 0x40:
688                         return sun3fb_init_fb(FBTYPE_SUN4COLOR, addr);
689                         break;
690                 case 0x45:
691                         return sun3fb_init_fb(FBTYPE_SUN8COLOR, addr);
692                         break;
693 #endif
694                 case 0x60:
695                         return sun3fb_init_fb(FBTYPE_SUNFAST_COLOR, addr);
696         }
697 #endif                  
698         
699         return -ENODEV;
700 }
701
702 MODULE_LICENSE("GPL");