pre-2.1.109-2..
[davej-history.git] / drivers / video / offb.c
blob6fc8b398cd6a19b0503d668e1f912559b3c42a04
1 /*
2 * linux/drivers/video/offb.c -- Open Firmware based frame buffer device
4 * Copyright (C) 1997 Geert Uytterhoeven
6 * This driver is partly based on the PowerMac console driver:
8 * Copyright (C) 1996 Paul Mackerras
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.
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/malloc.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 #ifdef CONFIG_FB_COMPAT_XPMAC
30 #include <asm/vc_ioctl.h>
31 #endif
32 #include <asm/io.h>
33 #include <asm/prom.h>
35 #include "fbcon-cfb8.h"
38 static int currcon = 0;
40 struct fb_info_offb {
41 struct fb_info info;
42 struct fb_fix_screeninfo fix;
43 struct fb_var_screeninfo var;
44 struct display disp;
45 struct { u_char red, green, blue, pad; } palette[256];
46 volatile unsigned char *cmap_adr;
47 volatile unsigned char *cmap_data;
50 #ifdef __powerpc__
51 #define mach_eieio() eieio()
52 #else
53 #define mach_eieio() do {} while (0)
54 #endif
56 static int ofonly = 0;
60 * Interface used by the world
63 void offb_init(void);
64 void offb_setup(char *options, int *ints);
66 static int offb_open(struct fb_info *info, int user);
67 static int offb_release(struct fb_info *info, int user);
68 static int offb_get_fix(struct fb_fix_screeninfo *fix, int con,
69 struct fb_info *info);
70 static int offb_get_var(struct fb_var_screeninfo *var, int con,
71 struct fb_info *info);
72 static int offb_set_var(struct fb_var_screeninfo *var, int con,
73 struct fb_info *info);
74 static int offb_pan_display(struct fb_var_screeninfo *var, int con,
75 struct fb_info *info);
76 static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
77 struct fb_info *info);
78 static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
79 struct fb_info *info);
80 static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd,
81 u_long arg, int con, struct fb_info *info);
83 #ifdef CONFIG_FB_COMPAT_XPMAC
84 int console_getmode(struct vc_mode *);
85 int console_setmode(struct vc_mode *, int);
86 int console_setcmap(int, unsigned char *, unsigned char *, unsigned char *);
87 int console_powermode(int);
88 struct fb_info *console_fb_info = NULL;
89 int (*console_setmode_ptr)(struct vc_mode *, int) = NULL;
90 int (*console_set_cmap_ptr)(struct fb_cmap *, int, int, struct fb_info *)
91 = NULL;
92 struct vc_mode display_info;
93 #endif /* CONFIG_FB_COMPAT_XPMAC */
97 * Interface to the low level console driver
100 static int offbcon_switch(int con, struct fb_info *info);
101 static int offbcon_updatevar(int con, struct fb_info *info);
102 static void offbcon_blank(int blank, struct fb_info *info);
106 * Internal routines
109 static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
110 u_int *transp, struct fb_info *info);
111 static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
112 u_int transp, struct fb_info *info);
113 static void do_install_cmap(int con, struct fb_info *info);
116 static struct fb_ops offb_ops = {
117 offb_open, offb_release, offb_get_fix, offb_get_var, offb_set_var,
118 offb_get_cmap, offb_set_cmap, offb_pan_display, offb_ioctl
123 * Open/Release the frame buffer device
126 static int offb_open(struct fb_info *info, int user)
129 * Nothing, only a usage count for the moment
132 MOD_INC_USE_COUNT;
133 return(0);
136 static int offb_release(struct fb_info *info, int user)
138 MOD_DEC_USE_COUNT;
139 return(0);
144 * Get the Fixed Part of the Display
147 static int offb_get_fix(struct fb_fix_screeninfo *fix, int con,
148 struct fb_info *info)
150 struct fb_info_offb *info2 = (struct fb_info_offb *)info;
152 memcpy(fix, &info2->fix, sizeof(struct fb_fix_screeninfo));
153 return 0;
158 * Get the User Defined Part of the Display
161 static int offb_get_var(struct fb_var_screeninfo *var, int con,
162 struct fb_info *info)
164 struct fb_info_offb *info2 = (struct fb_info_offb *)info;
166 memcpy(var, &info2->var, sizeof(struct fb_var_screeninfo));
167 return 0;
172 * Set the User Defined Part of the Display
175 static int offb_set_var(struct fb_var_screeninfo *var, int con,
176 struct fb_info *info)
178 struct display *display;
179 int oldbpp = -1, err;
180 int activate = var->activate;
181 struct fb_info_offb *info2 = (struct fb_info_offb *)info;
183 if (con >= 0)
184 display = &fb_display[con];
185 else
186 display = &info2->disp; /* used during initialization */
188 if (var->xres > info2->var.xres || var->yres > info2->var.yres ||
189 var->xres_virtual > info2->var.xres_virtual ||
190 var->yres_virtual > info2->var.yres_virtual ||
191 var->bits_per_pixel > info2->var.bits_per_pixel ||
192 var->nonstd ||
193 (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
194 return -EINVAL;
195 memcpy(var, &info2->var, sizeof(struct fb_var_screeninfo));
197 if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
198 oldbpp = display->var.bits_per_pixel;
199 display->var = *var;
201 if (oldbpp != var->bits_per_pixel) {
202 if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
203 return err;
204 do_install_cmap(con, info);
206 return 0;
211 * Pan or Wrap the Display
213 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
216 static int offb_pan_display(struct fb_var_screeninfo *var, int con,
217 struct fb_info *info)
219 if (var->xoffset || var->yoffset)
220 return -EINVAL;
221 else
222 return 0;
226 * Get the Colormap
229 static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
230 struct fb_info *info)
232 if (con == currcon) /* current console? */
233 return fb_get_cmap(cmap, &fb_display[con].var, kspc, offb_getcolreg,
234 info);
235 else if (fb_display[con].cmap.len) /* non default colormap? */
236 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
237 else
238 fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
239 cmap, kspc ? 0 : 2);
240 return 0;
244 * Set the Colormap
247 static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
248 struct fb_info *info)
250 struct fb_info_offb *info2 = (struct fb_info_offb *)info;
251 int err;
253 if (!info2->cmap_adr)
254 return -ENOSYS;
256 if (!fb_display[con].cmap.len) { /* no colormap allocated? */
257 if ((err = fb_alloc_cmap(&fb_display[con].cmap,
258 1<<fb_display[con].var.bits_per_pixel, 0)))
259 return err;
261 if (con == currcon) /* current console? */
262 return fb_set_cmap(cmap, &fb_display[con].var, kspc, offb_setcolreg,
263 info);
264 else
265 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
266 return 0;
270 static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd,
271 u_long arg, int con, struct fb_info *info)
273 return -EINVAL;
277 #ifdef CONFIG_FB_ATY
278 extern void atyfb_of_init(struct device_node *dp);
280 static const char *aty_names[] = {
281 "ATY,mach64", "ATY,XCLAIM", "ATY,264VT", "ATY,mach64ii", "ATY,264GT-B",
282 "ATY,mach64_3D_pcc", "ATY,XCLAIM3D", "ATY,XCLAIMVR", "ATY,RAGEII_M",
283 "ATY,XCLAIMVRPro", "ATY,mach64_3DU", "ATY,XCLAIM3DPro"
285 #endif /* CONFIG_FB_ATY */
286 #ifdef CONFIG_FB_S3TRIO
287 extern void s3triofb_init_of(struct device_node *dp);
288 #endif /* CONFIG_FB_S3TRIO */
292 * Initialisation
295 __initfunc(void offb_init(void))
297 struct device_node *dp;
298 int dpy, i, *pp, len;
299 unsigned *up, address;
300 struct fb_fix_screeninfo *fix;
301 struct fb_var_screeninfo *var;
302 struct display *disp;
303 struct fb_info_offb *info;
305 for (dpy = 0; dpy < prom_num_displays; dpy++) {
306 if (!(dp = find_path_device(prom_display_paths[dpy])))
307 continue;
309 if (!ofonly) {
310 #ifdef CONFIG_FB_ATY
311 for (i = 0; i < sizeof(aty_names)/sizeof(*aty_names); i++)
312 if (!strcmp(dp->name, aty_names[i]))
313 break;
314 if (i < sizeof(aty_names)/sizeof(*aty_names)) {
315 atyfb_of_init(dp);
316 continue;
318 #endif /* CONFIG_FB_ATY */
319 #ifdef CONFIG_FB_S3TRIO
320 if (s3triofb_init_of(dp))
321 continue;
322 #endif /* CONFIG_FB_S3TRIO */
325 info = kmalloc(sizeof(struct fb_info_offb), GFP_ATOMIC);
326 fix = &info->fix;
327 var = &info->var;
328 disp = &info->disp;
330 strcpy(fix->id, "OFfb ");
331 strncat(fix->id, dp->name, sizeof(fix->id));
332 fix->id[sizeof(fix->id)-1] = '\0';
334 if ((pp = (int *)get_property(dp, "depth", &len)) != NULL
335 && len == sizeof(int) && *pp != 8) {
336 printk("%s: can't use depth = %d\n", dp->full_name, *pp);
337 kfree(info);
338 continue;
340 if ((pp = (int *)get_property(dp, "width", &len)) != NULL
341 && len == sizeof(int))
342 var->xres = var->xres_virtual = *pp;
343 if ((pp = (int *)get_property(dp, "height", &len)) != NULL
344 && len == sizeof(int))
345 var->yres = var->yres_virtual = *pp;
346 if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL
347 && len == sizeof(int))
348 fix->line_length = *pp;
349 else
350 fix->line_length = var->xres_virtual;
351 fix->smem_len = fix->line_length*var->yres;
352 if ((up = (unsigned *)get_property(dp, "address", &len)) != NULL
353 && len == sizeof(unsigned))
354 address = (u_long)*up;
355 else {
356 for (i = 0; i < dp->n_addrs; ++i)
357 if (dp->addrs[i].size >= len)
358 break;
359 if (i >= dp->n_addrs) {
360 printk("no framebuffer address found for %s\n", dp->full_name);
361 kfree(info);
362 continue;
364 address = (u_long)dp->addrs[i].address;
366 fix->smem_start = (char *)address;
367 fix->type = FB_TYPE_PACKED_PIXELS;
368 fix->type_aux = 0;
370 /* XXX kludge for ati */
371 if (strncmp(dp->name, "ATY,", 4) == 0) {
372 info->cmap_adr = ioremap(address + 0x7ff000, 0x1000) + 0xcc0;
373 info->cmap_data = info->cmap_adr + 1;
376 fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR :
377 FB_VISUAL_STATIC_PSEUDOCOLOR;
379 var->xoffset = var->yoffset = 0;
380 var->bits_per_pixel = 8;
381 var->grayscale = 0;
382 var->red.offset = var->green.offset = var->blue.offset = 0;
383 var->red.length = var->green.length = var->blue.length = 8;
384 var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0;
385 var->transp.offset = var->transp.length = var->transp.msb_right = 0;
386 var->nonstd = 0;
387 var->activate = 0;
388 var->height = var->width = -1;
389 var->pixclock = 10000;
390 var->left_margin = var->right_margin = 16;
391 var->upper_margin = var->lower_margin = 16;
392 var->hsync_len = var->vsync_len = 8;
393 var->sync = 0;
394 var->vmode = FB_VMODE_NONINTERLACED;
396 disp->var = *var;
397 disp->cmap.start = 0;
398 disp->cmap.len = 0;
399 disp->cmap.red = NULL;
400 disp->cmap.green = NULL;
401 disp->cmap.blue = NULL;
402 disp->cmap.transp = NULL;
403 disp->screen_base = ioremap(address, fix->smem_len);
404 disp->visual = fix->visual;
405 disp->type = fix->type;
406 disp->type_aux = fix->type_aux;
407 disp->ypanstep = 0;
408 disp->ywrapstep = 0;
409 disp->line_length = fix->line_length;
410 disp->can_soft_blank = info->cmap_adr ? 1 : 0;
411 disp->inverse = 0;
412 #ifdef CONFIG_FBCON_CFB8
413 disp->dispsw = &fbcon_cfb8;
414 #else
415 disp->dispsw = NULL;
416 #endif
418 strcpy(info->info.modename, "OFfb ");
419 strncat(info->info.modename, dp->full_name,
420 sizeof(info->info.modename));
421 info->info.node = -1;
422 info->info.fbops = &offb_ops;
423 info->info.disp = disp;
424 info->info.fontname[0] = '\0';
425 info->info.changevar = NULL;
426 info->info.switch_con = &offbcon_switch;
427 info->info.updatevar = &offbcon_updatevar;
428 info->info.blank = &offbcon_blank;
430 for (i = 0; i < 16; i++) {
431 int j = color_table[i];
432 info->palette[i].red = default_red[j];
433 info->palette[i].green = default_grn[j];
434 info->palette[i].blue = default_blu[j];
436 offb_set_var(var, -1, &info->info);
438 if (register_framebuffer(&info->info) < 0) {
439 kfree(info);
440 return;
443 printk("fb%d: Open Firmware frame buffer device on %s\n",
444 GET_FB_IDX(info->info.node), dp->full_name);
446 #ifdef CONFIG_FB_COMPAT_XPMAC
447 if (!console_fb_info) {
448 display_info.height = var->yres;
449 display_info.width = var->xres;
450 display_info.depth = 8;
451 display_info.pitch = fix->line_length;
452 display_info.mode = 0;
453 strncpy(display_info.name, dp->name, sizeof(display_info.name));
454 display_info.fb_address = address;
455 display_info.cmap_adr_address = 0;
456 display_info.cmap_data_address = 0;
457 display_info.disp_reg_address = 0;
458 /* XXX kludge for ati */
459 if (strncmp(dp->name, "ATY,", 4) == 0) {
460 display_info.disp_reg_address = address + 0x7ffc00;
461 display_info.cmap_adr_address = address + 0x7ffcc0;
462 display_info.cmap_data_address = address + 0x7ffcc1;
464 console_fb_info = &info->info;
465 console_set_cmap_ptr = offb_set_cmap;
467 #endif /* CONFIG_FB_COMPAT_XPMAC) */
473 * Setup: parse used options
476 void offb_setup(char *options, int *ints)
478 if (!options || !*options)
479 return;
481 if (!strcmp(options, "ofonly"))
482 ofonly = 1;
486 static int offbcon_switch(int con, struct fb_info *info)
488 /* Do we have to save the colormap? */
489 if (fb_display[currcon].cmap.len)
490 fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
491 offb_getcolreg, info);
493 currcon = con;
494 /* Install new colormap */
495 do_install_cmap(con, info);
496 return 0;
500 * Update the `var' structure (called by fbcon.c)
503 static int offbcon_updatevar(int con, struct fb_info *info)
505 /* Nothing */
506 return 0;
510 * Blank the display.
513 static void offbcon_blank(int blank, struct fb_info *info)
515 struct fb_info_offb *info2 = (struct fb_info_offb *)info;
516 int i, j;
518 if (!info2->cmap_adr)
519 return;
521 if (blank)
522 for (i = 0; i < 256; i++) {
523 *info2->cmap_adr = i;
524 mach_eieio();
525 for (j = 0; j < 3; j++) {
526 *info2->cmap_data = 0;
527 mach_eieio();
530 else
531 do_install_cmap(currcon, info);
535 * Read a single color register and split it into
536 * colors/transparent. Return != 0 for invalid regno.
539 static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
540 u_int *transp, struct fb_info *info)
542 struct fb_info_offb *info2 = (struct fb_info_offb *)info;
544 if (!info2->cmap_adr || regno > 255)
545 return 1;
546 *red = info2->palette[regno].red;
547 *green = info2->palette[regno].green;
548 *blue = info2->palette[regno].blue;
549 return 0;
554 * Set a single color register. The values supplied are already
555 * rounded down to the hardware's capabilities (according to the
556 * entries in the var structure). Return != 0 for invalid regno.
559 static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
560 u_int transp, struct fb_info *info)
562 struct fb_info_offb *info2 = (struct fb_info_offb *)info;
564 if (!info2->cmap_adr || regno > 255)
565 return 1;
566 info2->palette[regno].red = red;
567 info2->palette[regno].green = green;
568 info2->palette[regno].blue = blue;
569 *info2->cmap_adr = regno;
570 mach_eieio();
571 *info2->cmap_data = red;
572 mach_eieio();
573 *info2->cmap_data = green;
574 mach_eieio();
575 *info2->cmap_data = blue;
576 mach_eieio();
577 return 0;
581 static void do_install_cmap(int con, struct fb_info *info)
583 if (con != currcon)
584 return;
585 if (fb_display[con].cmap.len)
586 fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
587 offb_setcolreg, info);
588 else
589 fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
590 &fb_display[con].var, 1, offb_setcolreg,
591 info);
595 #ifdef CONFIG_FB_COMPAT_XPMAC
598 * Backward compatibility mode for Xpmac
600 * To do:
602 * - console_setmode() should fill in a struct fb_var_screeninfo (using
603 * the MacOS video mode database) and simply call a decode_var()
604 * function, so console_setmode_ptr is no longer needed.
605 * console_getmode() should convert in the other direction.
607 * - instead of using the console_* stuff (filled in by the frame
608 * buffer), we should use the correct struct fb_info for the
609 * foreground virtual console.
612 int console_getmode(struct vc_mode *mode)
614 *mode = display_info;
615 return 0;
618 int console_setmode(struct vc_mode *mode, int doit)
620 int err;
622 if (console_setmode_ptr == NULL)
623 return -EINVAL;
625 err = (*console_setmode_ptr)(mode, doit);
626 return err;
629 static u16 palette_red[16];
630 static u16 palette_green[16];
631 static u16 palette_blue[16];
633 static struct fb_cmap palette_cmap = {
634 0, 16, palette_red, palette_green, palette_blue, NULL
637 int console_setcmap(int n_entries, unsigned char *red, unsigned char *green,
638 unsigned char *blue)
640 int i, j, n;
642 if (console_set_cmap_ptr == NULL)
643 return -EOPNOTSUPP;
644 for (i = 0; i < n_entries; i += n) {
645 n = n_entries-i;
646 if (n > 16)
647 n = 16;
648 palette_cmap.start = i;
649 palette_cmap.len = n;
650 for (j = 0; j < n; j++) {
651 palette_cmap.red[j] = (red[i+j] << 8) | red[i+j];
652 palette_cmap.green[j] = (green[i+j] << 8) | green[i+j];
653 palette_cmap.blue[j] = (blue[i+j] << 8) | blue[i+j];
655 (*console_set_cmap_ptr)(&palette_cmap, 1, fg_console, console_fb_info);
657 return 0;
660 int console_powermode(int mode)
662 if (mode == VC_POWERMODE_INQUIRY)
663 return 0;
664 if (mode < VESA_NO_BLANKING || mode > VESA_POWERDOWN)
665 return -EINVAL;
666 /* Not supported */
667 return -ENXIO;
670 #endif /* CONFIG_FB_COMPAT_XPMAC */