File: /usr/src/linux/drivers/video/offb.c

1     /*
2      *  linux/drivers/video/offb.c -- Open Firmware based frame buffer device
3      *
4      *	Copyright (C) 1997 Geert Uytterhoeven
5      *
6      *  This driver is partly based on the PowerMac console driver:
7      *
8      *	Copyright (C) 1996 Paul Mackerras
9      *
10      *  This file is subject to the terms and conditions of the GNU General Public
11      *  License. See the file COPYING in the main directory of this archive for
12      *  more details.
13      */
14     
15     #include <linux/config.h>
16     #include <linux/module.h>
17     #include <linux/kernel.h>
18     #include <linux/errno.h>
19     #include <linux/string.h>
20     #include <linux/mm.h>
21     #include <linux/tty.h>
22     #include <linux/slab.h>
23     #include <linux/vmalloc.h>
24     #include <linux/delay.h>
25     #include <linux/interrupt.h>
26     #include <linux/fb.h>
27     #include <linux/selection.h>
28     #include <linux/init.h>
29     #include <linux/ioport.h>
30     #ifdef CONFIG_FB_COMPAT_XPMAC
31     #include <asm/vc_ioctl.h>
32     #endif
33     #include <asm/io.h>
34     #include <asm/prom.h>
35     #include <asm/bootx.h>
36     
37     #include <video/fbcon.h>
38     #include <video/fbcon-cfb8.h>
39     #include <video/fbcon-cfb16.h>
40     #include <video/fbcon-cfb32.h>
41     #include <video/macmodes.h>
42     
43     
44     static int currcon = 0;
45     
46     /* Supported palette hacks */
47     enum {
48     	cmap_unknown,
49     	cmap_m64,	/* ATI Mach64 */
50     	cmap_r128,	/* ATI Rage128 */
51     	cmap_M3A,	/* ATI Rage Mobility M3 Head A */
52     	cmap_M3B,	/* ATI Rage Mobility M3 Head B */
53     	cmap_radeon	/* ATI Radeon */
54     };
55     
56     struct fb_info_offb {
57         struct fb_info info;
58         struct fb_fix_screeninfo fix;
59         struct fb_var_screeninfo var;
60         struct display disp;
61         struct { u_char red, green, blue, pad; } palette[256];
62         volatile unsigned char *cmap_adr;
63         volatile unsigned char *cmap_data;
64         int cmap_type;
65         union {
66     #ifdef FBCON_HAS_CFB16
67     	u16 cfb16[16];
68     #endif
69     #ifdef FBCON_HAS_CFB32
70     	u32 cfb32[16];
71     #endif
72         } fbcon_cmap;
73     };
74     
75     #ifdef __powerpc__
76     #define mach_eieio()	eieio()
77     #else
78     #define mach_eieio()	do {} while (0)
79     #endif
80     
81     
82         /*
83          *  Interface used by the world
84          */
85     
86     int offb_init(void);
87     
88     static int offb_get_fix(struct fb_fix_screeninfo *fix, int con,
89     			struct fb_info *info);
90     static int offb_get_var(struct fb_var_screeninfo *var, int con,
91     			struct fb_info *info);
92     static int offb_set_var(struct fb_var_screeninfo *var, int con,
93     			struct fb_info *info);
94     static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
95     			struct fb_info *info);
96     static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
97     			struct fb_info *info);
98     
99     extern boot_infos_t *boot_infos;
100     
101     static void offb_init_nodriver(struct device_node *);
102     static void offb_init_fb(const char *name, const char *full_name, int width,
103     		      int height, int depth, int pitch, unsigned long address,
104     		      struct device_node *dp);
105     
106         /*
107          *  Interface to the low level console driver
108          */
109     
110     static int offbcon_switch(int con, struct fb_info *info);
111     static int offbcon_updatevar(int con, struct fb_info *info);
112     static void offbcon_blank(int blank, struct fb_info *info);
113     
114     
115         /*
116          *  Internal routines
117          */
118     
119     static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
120     			 u_int *transp, struct fb_info *info);
121     static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
122     			 u_int transp, struct fb_info *info);
123     static void do_install_cmap(int con, struct fb_info *info);
124     
125     
126     static struct fb_ops offb_ops = {
127     	owner:		THIS_MODULE,
128     	fb_get_fix:	offb_get_fix,
129     	fb_get_var:	offb_get_var,
130     	fb_set_var:	offb_set_var,
131     	fb_get_cmap:	offb_get_cmap,
132     	fb_set_cmap:	offb_set_cmap,
133     };
134     
135         /*
136          *  Get the Fixed Part of the Display
137          */
138     
139     static int offb_get_fix(struct fb_fix_screeninfo *fix, int con,
140     			struct fb_info *info)
141     {
142         struct fb_info_offb *info2 = (struct fb_info_offb *)info;
143     
144         memcpy(fix, &info2->fix, sizeof(struct fb_fix_screeninfo));
145         return 0;
146     }
147     
148     
149         /*
150          *  Get the User Defined Part of the Display
151          */
152     
153     static int offb_get_var(struct fb_var_screeninfo *var, int con,
154     			struct fb_info *info)
155     {
156         struct fb_info_offb *info2 = (struct fb_info_offb *)info;
157     
158         memcpy(var, &info2->var, sizeof(struct fb_var_screeninfo));
159         return 0;
160     }
161     
162     
163         /*
164          *  Set the User Defined Part of the Display
165          */
166     
167     static int offb_set_var(struct fb_var_screeninfo *var, int con,
168     			struct fb_info *info)
169     {
170         struct display *display;
171         unsigned int oldbpp = 0;
172         int err;
173         int activate = var->activate;
174         struct fb_info_offb *info2 = (struct fb_info_offb *)info;
175     
176         if (con >= 0)
177     	display = &fb_display[con];
178         else
179     	display = &info2->disp;	/* used during initialization */
180     
181         if (var->xres > info2->var.xres || var->yres > info2->var.yres ||
182     	var->xres_virtual > info2->var.xres_virtual ||
183     	var->yres_virtual > info2->var.yres_virtual ||
184     	var->bits_per_pixel > info2->var.bits_per_pixel ||
185     	var->nonstd ||
186     	(var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
187     	return -EINVAL;
188         memcpy(var, &info2->var, sizeof(struct fb_var_screeninfo));
189     
190         if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
191     	oldbpp = display->var.bits_per_pixel;
192     	display->var = *var;
193         }
194         if ((oldbpp != var->bits_per_pixel) || (display->cmap.len == 0)) {
195     	if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
196     	    return err;
197     	do_install_cmap(con, info);
198         }
199         return 0;
200     }
201     
202     
203         /*
204          *  Get the Colormap
205          */
206     
207     static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
208     			 struct fb_info *info)
209     {
210         if (con == currcon) /* current console? */
211     	return fb_get_cmap(cmap, kspc, offb_getcolreg, info);
212         else if (fb_display[con].cmap.len) /* non default colormap? */
213     	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
214         else
215         {
216     	int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
217     	fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
218         }
219         return 0;
220     }
221     
222         /*
223          *  Set the Colormap
224          */
225     
226     static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
227     			 struct fb_info *info)
228     {
229         struct fb_info_offb *info2 = (struct fb_info_offb *)info;
230         int err;
231     
232         if (!info2->cmap_adr)
233     	return -ENOSYS;
234     
235         if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
236     	int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
237     	if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
238     	    return err;
239         }
240         if (con == currcon)			/* current console? */
241     	return fb_set_cmap(cmap, kspc, offb_setcolreg, info);
242         else
243     	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
244         return 0;
245     }
246     
247     
248         /*
249          *  Initialisation
250          */
251     
252     int __init offb_init(void)
253     {
254         struct device_node *dp;
255         unsigned int dpy;
256         struct device_node *displays = find_type_devices("display");
257         struct device_node *macos_display = NULL;
258     
259         /* If we're booted from BootX... */
260         if (prom_num_displays == 0 && boot_infos != 0) {
261     	unsigned long addr = (unsigned long) boot_infos->dispDeviceBase;
262     	/* find the device node corresponding to the macos display */
263     	for (dp = displays; dp != NULL; dp = dp->next) {
264     	    int i;
265     	    /*
266     	     * Grrr...  It looks like the MacOS ATI driver
267     	     * munges the assigned-addresses property (but
268     	     * the AAPL,address value is OK).
269     	     */
270     	    if (strncmp(dp->name, "ATY,", 4) == 0 && dp->n_addrs == 1) {
271     		unsigned int *ap = (unsigned int *)
272     		    get_property(dp, "AAPL,address", NULL);
273     		if (ap != NULL) {
274     		    dp->addrs[0].address = *ap;
275     		    dp->addrs[0].size = 0x01000000;
276     		}
277     	    }
278     
279     	    /*
280     	     * The LTPro on the Lombard powerbook has no addresses
281     	     * on the display nodes, they are on their parent.
282     	     */
283     	    if (dp->n_addrs == 0 && device_is_compatible(dp, "ATY,264LTPro")) {
284     		int na;
285     		unsigned int *ap = (unsigned int *)
286     		    get_property(dp, "AAPL,address", &na);
287     		if (ap != 0)
288     		    for (na /= sizeof(unsigned int); na > 0; --na, ++ap)
289     			if (*ap <= addr && addr < *ap + 0x1000000)
290     			    goto foundit;
291     	    }
292     
293     	    /*
294     	     * See if the display address is in one of the address
295     	     * ranges for this display.
296     	     */
297     	    for (i = 0; i < dp->n_addrs; ++i) {
298     		if (dp->addrs[i].address <= addr
299     		    && addr < dp->addrs[i].address + dp->addrs[i].size)
300     		    break;
301     	    }
302     	    if (i < dp->n_addrs) {
303     	    foundit:
304     		printk(KERN_INFO "MacOS display is %s\n", dp->full_name);
305     		macos_display = dp;
306     		break;
307     	    }
308     	}
309     
310     	/* initialize it */
311     	offb_init_fb(macos_display? macos_display->name: "MacOS display",
312     		     macos_display? macos_display->full_name: "MacOS display",
313     		     boot_infos->dispDeviceRect[2],
314     		     boot_infos->dispDeviceRect[3],
315     		     boot_infos->dispDeviceDepth,
316     		     boot_infos->dispDeviceRowBytes, addr, NULL);
317         }
318     
319         for (dpy = 0; dpy < prom_num_displays; dpy++) {
320     	if ((dp = find_path_device(prom_display_paths[dpy])))
321     	    offb_init_nodriver(dp);
322         }
323         return 0;
324     }
325     
326     
327     static void __init offb_init_nodriver(struct device_node *dp)
328     {
329         int *pp, i;
330         unsigned int len;
331         int width = 640, height = 480, depth = 8, pitch;
332         unsigned *up, address;
333     
334         if ((pp = (int *)get_property(dp, "depth", &len)) != NULL
335     	&& len == sizeof(int))
336     	depth = *pp;
337         if ((pp = (int *)get_property(dp, "width", &len)) != NULL
338     	&& len == sizeof(int))
339     	width = *pp;
340         if ((pp = (int *)get_property(dp, "height", &len)) != NULL
341     	&& len == sizeof(int))
342     	height = *pp;
343         if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL
344     	&& len == sizeof(int)) {
345     	pitch = *pp;
346     	if (pitch == 1)
347     	    pitch = 0x1000;
348         } else
349     	pitch = width;
350         if ((up = (unsigned *)get_property(dp, "address", &len)) != NULL
351     	&& len == sizeof(unsigned))
352     	address = (u_long)*up;
353         else {
354     	for (i = 0; i < dp->n_addrs; ++i)
355     	    if (dp->addrs[i].size >= pitch*height*depth/8)
356     		break;
357     	if (i >= dp->n_addrs) {
358     	    printk(KERN_ERR "no framebuffer address found for %s\n", dp->full_name);
359     	    return;
360     	}
361     
362     	address = (u_long)dp->addrs[i].address;
363     
364     	/* kludge for valkyrie */
365     	if (strcmp(dp->name, "valkyrie") == 0) 
366     	    address += 0x1000;
367         }
368         offb_init_fb(dp->name, dp->full_name, width, height, depth,
369     		 pitch, address, dp);
370         
371     }
372     
373     static void __init offb_init_fb(const char *name, const char *full_name,
374     				    int width, int height, int depth,
375     				    int pitch, unsigned long address,
376     				    struct device_node *dp)
377     {
378         int i;
379         struct fb_fix_screeninfo *fix;
380         struct fb_var_screeninfo *var;
381         struct display *disp;
382         struct fb_info_offb *info;
383         unsigned long res_start = address;
384         unsigned long res_size = pitch*height*depth/8;
385     
386         if (!request_mem_region(res_start, res_size, "offb"))
387     	return;
388     
389         printk(KERN_INFO "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",
390     	   width, height, name, address, depth, pitch);
391         if (depth != 8 && depth != 16 && depth != 32) {
392     	printk(KERN_ERR "%s: can't use depth = %d\n", full_name, depth);
393     	release_mem_region(res_start, res_size);
394     	return;
395         }
396     
397         info = kmalloc(sizeof(struct fb_info_offb), GFP_ATOMIC);
398         if (info == 0) {
399     	release_mem_region(res_start, res_size);
400     	return;
401         }
402         memset(info, 0, sizeof(*info));
403     
404         fix = &info->fix;
405         var = &info->var;
406         disp = &info->disp;
407     
408         strcpy(fix->id, "OFfb ");
409         strncat(fix->id, name, sizeof(fix->id));
410         fix->id[sizeof(fix->id)-1] = '\0';
411     
412         var->xres = var->xres_virtual = width;
413         var->yres = var->yres_virtual = height;
414         fix->line_length = pitch;
415     
416         fix->smem_start = address;
417         fix->smem_len = pitch * height;
418         fix->type = FB_TYPE_PACKED_PIXELS;
419         fix->type_aux = 0;
420     
421         info->cmap_type = cmap_unknown;
422         if (depth == 8)
423         {
424         	/* XXX kludge for ati */
425     	if (dp && !strncmp(name, "ATY,Rage128", 11)) {
426     		unsigned long regbase = dp->addrs[2].address;
427     		info->cmap_adr = ioremap(regbase, 0x1FFF);
428     		info->cmap_type = cmap_r128;
429     	} else if (dp && !strncmp(name, "ATY,RageM3pA", 12)) {
430     		unsigned long regbase = dp->parent->addrs[2].address;
431     		info->cmap_adr = ioremap(regbase, 0x1FFF);
432     		info->cmap_type = cmap_M3A;
433     	} else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) {
434     		unsigned long regbase = dp->parent->addrs[2].address;
435     		info->cmap_adr = ioremap(regbase, 0x1FFF);
436     		info->cmap_type = cmap_M3B;
437     	} else if (dp && !strncmp(name, "ATY,Rage6", 9)) {
438     		unsigned long regbase = dp->addrs[1].address;
439     		info->cmap_adr = ioremap(regbase, 0x1FFF);
440     		info->cmap_type = cmap_radeon;
441     	} else if (!strncmp(name, "ATY,", 4)) {
442     		unsigned long base = address & 0xff000000UL;
443     		info->cmap_adr = ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
444     		info->cmap_data = info->cmap_adr + 1;
445     		info->cmap_type = cmap_m64;
446     	}
447             fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR
448     				     : FB_VISUAL_STATIC_PSEUDOCOLOR;
449         }
450         else
451     	fix->visual = /*info->cmap_adr ? FB_VISUAL_DIRECTCOLOR
452     				     : */FB_VISUAL_TRUECOLOR;
453     
454         var->xoffset = var->yoffset = 0;
455         var->bits_per_pixel = depth;
456         switch (depth) {
457     	case 8:
458     	    var->bits_per_pixel = 8;
459     	    var->red.offset = 0;
460     	    var->red.length = 8;
461     	    var->green.offset = 0;
462     	    var->green.length = 8;
463     	    var->blue.offset = 0;
464     	    var->blue.length = 8;
465     	    var->transp.offset = 0;
466     	    var->transp.length = 0;
467     	    break;
468     	case 16:	/* RGB 555 */
469     	    var->bits_per_pixel = 16;
470     	    var->red.offset = 10;
471     	    var->red.length = 5;
472     	    var->green.offset = 5;
473     	    var->green.length = 5;
474     	    var->blue.offset = 0;
475     	    var->blue.length = 5;
476     	    var->transp.offset = 0;
477     	    var->transp.length = 0;
478     	    break;
479     	case 32:	/* RGB 888 */
480     	    var->bits_per_pixel = 32;
481     	    var->red.offset = 16;
482     	    var->red.length = 8;
483     	    var->green.offset = 8;
484     	    var->green.length = 8;
485     	    var->blue.offset = 0;
486     	    var->blue.length = 8;
487     	    var->transp.offset = 24;
488     	    var->transp.length = 8;
489     	    break;
490         }
491         var->red.msb_right = var->green.msb_right = var->blue.msb_right = var->transp.msb_right = 0;
492         var->grayscale = 0;
493         var->nonstd = 0;
494         var->activate = 0;
495         var->height = var->width = -1;
496         var->pixclock = 10000;
497         var->left_margin = var->right_margin = 16;
498         var->upper_margin = var->lower_margin = 16;
499         var->hsync_len = var->vsync_len = 8;
500         var->sync = 0;
501         var->vmode = FB_VMODE_NONINTERLACED;
502     
503         disp->var = *var;
504         disp->cmap.start = 0;
505         disp->cmap.len = 0;
506         disp->cmap.red = NULL;
507         disp->cmap.green = NULL;
508         disp->cmap.blue = NULL;
509         disp->cmap.transp = NULL;
510         disp->screen_base = ioremap(address, fix->smem_len);
511         disp->visual = fix->visual;
512         disp->type = fix->type;
513         disp->type_aux = fix->type_aux;
514         disp->ypanstep = 0;
515         disp->ywrapstep = 0;
516         disp->line_length = fix->line_length;
517         disp->can_soft_blank = info->cmap_adr ? 1 : 0;
518         disp->inverse = 0;
519         switch (depth) {
520     #ifdef FBCON_HAS_CFB8
521             case 8:
522                 disp->dispsw = &fbcon_cfb8;
523                 break;
524     #endif
525     #ifdef FBCON_HAS_CFB16
526             case 16:
527                 disp->dispsw = &fbcon_cfb16;
528                 disp->dispsw_data = info->fbcon_cmap.cfb16;
529                 for (i = 0; i < 16; i++)
530                 	if (fix->visual == FB_VISUAL_TRUECOLOR)
531     		    info->fbcon_cmap.cfb16[i] =
532     			    (((default_blu[i] >> 3) & 0x1f) << 10) |
533     			    (((default_grn[i] >> 3) & 0x1f) << 5) |
534     			    ((default_red[i] >> 3) & 0x1f);
535     		else
536     		    info->fbcon_cmap.cfb16[i] =
537     			    (i << 10) | (i << 5) | i;
538                 break;
539     #endif
540     #ifdef FBCON_HAS_CFB32
541             case 32:
542                 disp->dispsw = &fbcon_cfb32;
543                 disp->dispsw_data = info->fbcon_cmap.cfb32;
544                 for (i = 0; i < 16; i++)
545                 	if (fix->visual == FB_VISUAL_TRUECOLOR)
546     		    info->fbcon_cmap.cfb32[i] =
547     			(default_blu[i] << 16) |
548     			(default_grn[i] << 8) |
549     			default_red[i];
550     		else
551     		    info->fbcon_cmap.cfb32[i] =
552     			    (i << 16) | (i << 8) | i;
553                 break;
554     #endif
555             default:
556                 disp->dispsw = &fbcon_dummy;
557         }
558     
559         disp->scrollmode = SCROLL_YREDRAW;
560     
561         strcpy(info->info.modename, "OFfb ");
562         strncat(info->info.modename, full_name, sizeof(info->info.modename));
563         info->info.node = -1;
564         info->info.fbops = &offb_ops;
565         info->info.disp = disp;
566         info->info.fontname[0] = '\0';
567         info->info.changevar = NULL;
568         info->info.switch_con = &offbcon_switch;
569         info->info.updatevar = &offbcon_updatevar;
570         info->info.blank = &offbcon_blank;
571         info->info.flags = FBINFO_FLAG_DEFAULT;
572     
573         for (i = 0; i < 16; i++) {
574     	int j = color_table[i];
575     	info->palette[i].red = default_red[j];
576     	info->palette[i].green = default_grn[j];
577     	info->palette[i].blue = default_blu[j];
578         }
579         offb_set_var(var, -1, &info->info);
580     
581         if (register_framebuffer(&info->info) < 0) {
582     	kfree(info);
583     	release_mem_region(res_start, res_size);
584     	return;
585         }
586     
587         printk(KERN_INFO "fb%d: Open Firmware frame buffer device on %s\n",
588     	   GET_FB_IDX(info->info.node), full_name);
589     
590     #ifdef CONFIG_FB_COMPAT_XPMAC
591         if (!console_fb_info) {
592     	display_info.height = var->yres;
593     	display_info.width = var->xres;
594     	display_info.depth = depth;
595     	display_info.pitch = fix->line_length;
596     	display_info.mode = 0;
597     	strncpy(display_info.name, name, sizeof(display_info.name));
598     	display_info.fb_address = address;
599     	display_info.cmap_adr_address = 0;
600     	display_info.cmap_data_address = 0;
601     	display_info.disp_reg_address = 0;
602     	/* XXX kludge for ati */
603     	if (info->cmap_type == cmap_m64) {
604     	    unsigned long base = address & 0xff000000UL;
605     	    display_info.disp_reg_address = base + 0x7ffc00;
606     	    display_info.cmap_adr_address = base + 0x7ffcc0;
607     	    display_info.cmap_data_address = base + 0x7ffcc1;
608     	}
609     	console_fb_info = &info->info;
610         }
611     #endif /* CONFIG_FB_COMPAT_XPMAC) */
612     }
613     
614     
615     static int offbcon_switch(int con, struct fb_info *info)
616     {
617         /* Do we have to save the colormap? */
618         if (fb_display[currcon].cmap.len)
619     	fb_get_cmap(&fb_display[currcon].cmap, 1, offb_getcolreg, info);
620     
621         currcon = con;
622         /* Install new colormap */
623         do_install_cmap(con, info);
624         return 0;
625     }
626     
627         /*
628          *  Update the `var' structure (called by fbcon.c)
629          */
630     
631     static int offbcon_updatevar(int con, struct fb_info *info)
632     {
633         /* Nothing */
634         return 0;
635     }
636     
637         /*
638          *  Blank the display.
639          */
640     
641     static void offbcon_blank(int blank, struct fb_info *info)
642     {
643         struct fb_info_offb *info2 = (struct fb_info_offb *)info;
644         int i, j;
645     
646         if (!info2->cmap_adr)
647     	return;
648     
649         if (blank)
650     	for (i = 0; i < 256; i++) {
651     	    switch(info2->cmap_type) {
652     	    case cmap_m64:
653     	        *info2->cmap_adr = i;
654     	  	mach_eieio();
655     	  	for (j = 0; j < 3; j++) {
656     		    *info2->cmap_data = 0;
657     		    mach_eieio();
658     	    	}
659     	    	break;
660     	    case cmap_M3A:
661     	        /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
662     	    	out_le32((unsigned *)(info2->cmap_adr + 0x58),
663     	    		in_le32((unsigned *)(info2->cmap_adr + 0x58)) & ~0x20);
664     	    case cmap_r128:
665     	    	/* Set palette index & data */
666         	        out_8(info2->cmap_adr + 0xb0, i);
667     	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
668     	    	break;
669     	    case cmap_M3B:
670     	        /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
671     	    	out_le32((unsigned *)(info2->cmap_adr + 0x58),
672     	    		in_le32((unsigned *)(info2->cmap_adr + 0x58)) | 0x20);
673     	    	/* Set palette index & data */
674     	    	out_8(info2->cmap_adr + 0xb0, i);
675     	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
676     	    	break;
677     	    case cmap_radeon:
678         	        out_8(info2->cmap_adr + 0xb0, i);
679     	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
680     	    	break;
681     	    }
682     	}
683         else
684     	do_install_cmap(currcon, info);
685     }
686     
687         /*
688          *  Read a single color register and split it into
689          *  colors/transparent. Return != 0 for invalid regno.
690          */
691     
692     static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
693     			  u_int *transp, struct fb_info *info)
694     {
695         struct fb_info_offb *info2 = (struct fb_info_offb *)info;
696     
697         if (!info2->cmap_adr || regno > 255)
698     	return 1;
699         
700         *red = (info2->palette[regno].red<<8) | info2->palette[regno].red;
701         *green = (info2->palette[regno].green<<8) | info2->palette[regno].green;
702         *blue = (info2->palette[regno].blue<<8) | info2->palette[regno].blue;
703         *transp = 0;
704         return 0;
705     }
706     
707     
708         /*
709          *  Set a single color register. The values supplied are already
710          *  rounded down to the hardware's capabilities (according to the
711          *  entries in the var structure). Return != 0 for invalid regno.
712          */
713     
714     static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
715     			 u_int transp, struct fb_info *info)
716     {
717         struct fb_info_offb *info2 = (struct fb_info_offb *)info;
718     	
719         if (!info2->cmap_adr || regno > 255)
720     	return 1;
721     
722         red >>= 8;
723         green >>= 8;
724         blue >>= 8;
725     
726         info2->palette[regno].red = red;
727         info2->palette[regno].green = green;
728         info2->palette[regno].blue = blue;
729     
730         switch(info2->cmap_type) {
731         case cmap_m64:
732             *info2->cmap_adr = regno;
733     	mach_eieio();
734     	*info2->cmap_data = red;
735     	mach_eieio();
736     	*info2->cmap_data = green;
737     	mach_eieio();
738     	*info2->cmap_data = blue;
739     	mach_eieio();
740     	break;
741         case cmap_M3A:
742     	/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
743     	out_le32((unsigned *)(info2->cmap_adr + 0x58),
744     		in_le32((unsigned *)(info2->cmap_adr + 0x58)) & ~0x20);
745         case cmap_r128:
746     	/* Set palette index & data */
747     	out_8(info2->cmap_adr + 0xb0, regno);
748     	out_le32((unsigned *)(info2->cmap_adr + 0xb4),
749     		(red << 16 | green << 8 | blue));
750     	break;
751         case cmap_M3B:
752             /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
753         	out_le32((unsigned *)(info2->cmap_adr + 0x58),
754         		in_le32((unsigned *)(info2->cmap_adr + 0x58)) | 0x20);
755         	/* Set palette index & data */
756         	out_8(info2->cmap_adr + 0xb0, regno);
757       	out_le32((unsigned *)(info2->cmap_adr + 0xb4),
758         		(red << 16 | green << 8 | blue));
759         	break;
760         case cmap_radeon:
761     	/* Set palette index & data (could be smarter) */
762     	out_8(info2->cmap_adr + 0xb0, regno);
763       	out_le32((unsigned *)(info2->cmap_adr + 0xb4),
764         		(red << 16 | green << 8 | blue));
765     	break;
766         }
767     
768         if (regno < 16)
769     	switch (info2->var.bits_per_pixel) {
770     #ifdef FBCON_HAS_CFB16
771     	    case 16:
772     		info2->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) | regno;
773     		break;
774     #endif
775     #ifdef FBCON_HAS_CFB32
776     	    case 32:
777     	    {
778     		int i = (regno << 8) | regno;
779     		info2->fbcon_cmap.cfb32[regno] = (i << 16) | i;
780     		break;
781     	    }
782     #endif
783            }
784     
785         return 0;
786     }
787     
788     
789     static void do_install_cmap(int con, struct fb_info *info)
790     {
791         if (con != currcon)
792     	return;
793         if (fb_display[con].cmap.len)
794     	fb_set_cmap(&fb_display[con].cmap, 1, offb_setcolreg, info);
795         else
796         {
797     	int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
798     	fb_set_cmap(fb_default_cmap(size), 1, offb_setcolreg, info);
799         }
800     }
801     
802     MODULE_LICENSE("GPL");
803