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
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>
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>
27 #include <linux/selection.h>
28 #include <linux/init.h>
29 #ifdef CONFIG_FB_COMPAT_XPMAC
30 #include <asm/vc_ioctl.h>
35 #include "fbcon-cfb8.h"
38 static int currcon
= 0;
42 struct fb_fix_screeninfo fix
;
43 struct fb_var_screeninfo var
;
45 struct { u_char red
, green
, blue
, pad
; } palette
[256];
46 volatile unsigned char *cmap_adr
;
47 volatile unsigned char *cmap_data
;
51 #define mach_eieio() eieio()
53 #define mach_eieio() do {} while (0)
56 static int ofonly
= 0;
60 * Interface used by the world
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
*)
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
);
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
136 static int offb_release(struct fb_info
*info
, int user
)
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
));
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
));
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
;
184 display
= &fb_display
[con
];
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
||
193 (var
->vmode
& FB_VMODE_MASK
) != FB_VMODE_NONINTERLACED
)
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
;
201 if (oldbpp
!= var
->bits_per_pixel
) {
202 if ((err
= fb_alloc_cmap(&display
->cmap
, 0, 0)))
204 do_install_cmap(con
, info
);
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
)
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
,
235 else if (fb_display
[con
].cmap
.len
) /* non default colormap? */
236 fb_copy_cmap(&fb_display
[con
].cmap
, cmap
, kspc
? 0 : 2);
238 fb_copy_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
),
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
;
253 if (!info2
->cmap_adr
)
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)))
261 if (con
== currcon
) /* current console? */
262 return fb_set_cmap(cmap
, &fb_display
[con
].var
, kspc
, offb_setcolreg
,
265 fb_copy_cmap(cmap
, &fb_display
[con
].cmap
, kspc
? 0 : 1);
270 static int offb_ioctl(struct inode
*inode
, struct file
*file
, u_int cmd
,
271 u_long arg
, int con
, struct fb_info
*info
)
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 */
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
])))
311 for (i
= 0; i
< sizeof(aty_names
)/sizeof(*aty_names
); i
++)
312 if (!strcmp(dp
->name
, aty_names
[i
]))
314 if (i
< sizeof(aty_names
)/sizeof(*aty_names
)) {
318 #endif /* CONFIG_FB_ATY */
319 #ifdef CONFIG_FB_S3TRIO
320 if (s3triofb_init_of(dp
))
322 #endif /* CONFIG_FB_S3TRIO */
325 info
= kmalloc(sizeof(struct fb_info_offb
), GFP_ATOMIC
);
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
);
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
;
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
;
356 for (i
= 0; i
< dp
->n_addrs
; ++i
)
357 if (dp
->addrs
[i
].size
>= len
)
359 if (i
>= dp
->n_addrs
) {
360 printk("no framebuffer address found for %s\n", dp
->full_name
);
364 address
= (u_long
)dp
->addrs
[i
].address
;
366 fix
->smem_start
= (char *)address
;
367 fix
->type
= FB_TYPE_PACKED_PIXELS
;
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;
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;
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;
394 var
->vmode
= FB_VMODE_NONINTERLACED
;
397 disp
->cmap
.start
= 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
;
409 disp
->line_length
= fix
->line_length
;
410 disp
->can_soft_blank
= info
->cmap_adr
? 1 : 0;
412 #ifdef CONFIG_FBCON_CFB8
413 disp
->dispsw
= &fbcon_cfb8
;
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) {
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
)
481 if (!strcmp(options
, "ofonly"))
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
);
494 /* Install new colormap */
495 do_install_cmap(con
, info
);
500 * Update the `var' structure (called by fbcon.c)
503 static int offbcon_updatevar(int con
, struct fb_info
*info
)
513 static void offbcon_blank(int blank
, struct fb_info
*info
)
515 struct fb_info_offb
*info2
= (struct fb_info_offb
*)info
;
518 if (!info2
->cmap_adr
)
522 for (i
= 0; i
< 256; i
++) {
523 *info2
->cmap_adr
= i
;
525 for (j
= 0; j
< 3; j
++) {
526 *info2
->cmap_data
= 0;
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)
546 *red
= info2
->palette
[regno
].red
;
547 *green
= info2
->palette
[regno
].green
;
548 *blue
= info2
->palette
[regno
].blue
;
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)
566 info2
->palette
[regno
].red
= red
;
567 info2
->palette
[regno
].green
= green
;
568 info2
->palette
[regno
].blue
= blue
;
569 *info2
->cmap_adr
= regno
;
571 *info2
->cmap_data
= red
;
573 *info2
->cmap_data
= green
;
575 *info2
->cmap_data
= blue
;
581 static void do_install_cmap(int con
, struct fb_info
*info
)
585 if (fb_display
[con
].cmap
.len
)
586 fb_set_cmap(&fb_display
[con
].cmap
, &fb_display
[con
].var
, 1,
587 offb_setcolreg
, info
);
589 fb_set_cmap(fb_default_cmap(1<<fb_display
[con
].var
.bits_per_pixel
),
590 &fb_display
[con
].var
, 1, offb_setcolreg
,
595 #ifdef CONFIG_FB_COMPAT_XPMAC
598 * Backward compatibility mode for Xpmac
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
;
618 int console_setmode(struct vc_mode
*mode
, int doit
)
622 if (console_setmode_ptr
== NULL
)
625 err
= (*console_setmode_ptr
)(mode
, doit
);
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
,
642 if (console_set_cmap_ptr
== NULL
)
644 for (i
= 0; i
< n_entries
; i
+= n
) {
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
);
660 int console_powermode(int mode
)
662 if (mode
== VC_POWERMODE_INQUIRY
)
664 if (mode
< VESA_NO_BLANKING
|| mode
> VESA_POWERDOWN
)
670 #endif /* CONFIG_FB_COMPAT_XPMAC */