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