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