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/module.h>
16 #include <linux/kernel.h>
17 #include <linux/errno.h>
18 #include <linux/string.h>
20 #include <linux/slab.h>
21 #include <linux/vmalloc.h>
22 #include <linux/delay.h>
23 #include <linux/interrupt.h>
25 #include <linux/init.h>
26 #include <linux/ioport.h>
27 #include <linux/pci.h>
32 #include <asm/pci-bridge.h>
36 #include <asm/bootx.h>
41 /* Supported palette hacks */
44 cmap_m64
, /* ATI Mach64 */
45 cmap_r128
, /* ATI Rage128 */
46 cmap_M3A
, /* ATI Rage Mobility M3 Head A */
47 cmap_M3B
, /* ATI Rage Mobility M3 Head B */
48 cmap_radeon
, /* ATI Radeon */
49 cmap_gxt2000
, /* IBM GXT2000 */
53 volatile void __iomem
*cmap_adr
;
54 volatile void __iomem
*cmap_data
;
59 struct offb_par default_par
;
62 * Interface used by the world
67 static int offb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
68 u_int transp
, struct fb_info
*info
);
69 static int offb_blank(int blank
, struct fb_info
*info
);
72 extern boot_infos_t
*boot_infos
;
75 static void offb_init_nodriver(struct device_node
*);
76 static void offb_init_fb(const char *name
, const char *full_name
,
77 int width
, int height
, int depth
, int pitch
,
78 unsigned long address
, struct device_node
*dp
);
80 static struct fb_ops offb_ops
= {
82 .fb_setcolreg
= offb_setcolreg
,
83 .fb_blank
= offb_blank
,
84 .fb_fillrect
= cfb_fillrect
,
85 .fb_copyarea
= cfb_copyarea
,
86 .fb_imageblit
= cfb_imageblit
,
90 * Set a single color register. The values supplied are already
91 * rounded down to the hardware's capabilities (according to the
92 * entries in the var structure). Return != 0 for invalid regno.
95 static int offb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
96 u_int transp
, struct fb_info
*info
)
98 struct offb_par
*par
= (struct offb_par
*) info
->par
;
100 u32
*pal
= info
->pseudo_palette
;
102 depth
= info
->var
.bits_per_pixel
;
104 depth
= (info
->var
.green
.length
== 5) ? 15 : 16;
107 (depth
== 16 && regno
> 63) ||
108 (depth
== 15 && regno
> 31))
114 pal
[regno
] = (regno
<< 10) | (regno
<< 5) | regno
;
117 pal
[regno
] = (regno
<< 11) | (regno
<< 5) | regno
;
120 pal
[regno
] = (regno
<< 16) | (regno
<< 8) | regno
;
123 i
= (regno
<< 8) | regno
;
124 pal
[regno
] = (i
<< 16) | i
;
136 switch (par
->cmap_type
) {
138 writeb(regno
, par
->cmap_adr
);
139 writeb(red
, par
->cmap_data
);
140 writeb(green
, par
->cmap_data
);
141 writeb(blue
, par
->cmap_data
);
144 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
145 out_le32(par
->cmap_adr
+ 0x58,
146 in_le32(par
->cmap_adr
+ 0x58) & ~0x20);
148 /* Set palette index & data */
149 out_8(par
->cmap_adr
+ 0xb0, regno
);
150 out_le32(par
->cmap_adr
+ 0xb4,
151 (red
<< 16 | green
<< 8 | blue
));
154 /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
155 out_le32(par
->cmap_adr
+ 0x58,
156 in_le32(par
->cmap_adr
+ 0x58) | 0x20);
157 /* Set palette index & data */
158 out_8(par
->cmap_adr
+ 0xb0, regno
);
159 out_le32(par
->cmap_adr
+ 0xb4, (red
<< 16 | green
<< 8 | blue
));
162 /* Set palette index & data (could be smarter) */
163 out_8(par
->cmap_adr
+ 0xb0, regno
);
164 out_le32(par
->cmap_adr
+ 0xb4, (red
<< 16 | green
<< 8 | blue
));
167 out_le32((unsigned __iomem
*) par
->cmap_adr
+ regno
,
168 (red
<< 16 | green
<< 8 | blue
));
179 static int offb_blank(int blank
, struct fb_info
*info
)
181 struct offb_par
*par
= (struct offb_par
*) info
->par
;
191 par
->blanked
= blank
;
194 for (i
= 0; i
< 256; i
++) {
195 switch (par
->cmap_type
) {
197 writeb(i
, par
->cmap_adr
);
198 for (j
= 0; j
< 3; j
++)
199 writeb(0, par
->cmap_data
);
202 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
203 out_le32(par
->cmap_adr
+ 0x58,
204 in_le32(par
->cmap_adr
+ 0x58) & ~0x20);
206 /* Set palette index & data */
207 out_8(par
->cmap_adr
+ 0xb0, i
);
208 out_le32(par
->cmap_adr
+ 0xb4, 0);
211 /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
212 out_le32(par
->cmap_adr
+ 0x58,
213 in_le32(par
->cmap_adr
+ 0x58) | 0x20);
214 /* Set palette index & data */
215 out_8(par
->cmap_adr
+ 0xb0, i
);
216 out_le32(par
->cmap_adr
+ 0xb4, 0);
219 out_8(par
->cmap_adr
+ 0xb0, i
);
220 out_le32(par
->cmap_adr
+ 0xb4, 0);
223 out_le32((unsigned __iomem
*) par
->cmap_adr
+ i
,
228 fb_set_cmap(&info
->cmap
, info
);
236 int __init
offb_init(void)
238 struct device_node
*dp
= NULL
, *boot_disp
= NULL
;
240 if (fb_get_options("offb", NULL
))
243 for (dp
= NULL
; (dp
= of_find_node_by_type(dp
, "display"));) {
244 if (get_property(dp
, "linux,opened", NULL
) &&
245 get_property(dp
, "linux,boot-display", NULL
)) {
247 offb_init_nodriver(dp
);
250 for (dp
= NULL
; (dp
= of_find_node_by_type(dp
, "display"));) {
251 if (get_property(dp
, "linux,opened", NULL
) &&
253 offb_init_nodriver(dp
);
260 static void __init
offb_init_nodriver(struct device_node
*dp
)
263 int i
, width
= 640, height
= 480, depth
= 8, pitch
= 640;
264 unsigned int flags
, rsize
, addr_prop
= 0;
265 unsigned long max_size
= 0;
266 u64 rstart
, address
= OF_BAD_ADDR
;
267 u32
*pp
, *addrp
, *up
;
270 pp
= (u32
*)get_property(dp
, "linux,bootx-depth", &len
);
272 pp
= (u32
*)get_property(dp
, "depth", &len
);
273 if (pp
&& len
== sizeof(u32
))
276 pp
= (u32
*)get_property(dp
, "linux,bootx-width", &len
);
278 pp
= (u32
*)get_property(dp
, "width", &len
);
279 if (pp
&& len
== sizeof(u32
))
282 pp
= (u32
*)get_property(dp
, "linux,bootx-height", &len
);
284 pp
= (u32
*)get_property(dp
, "height", &len
);
285 if (pp
&& len
== sizeof(u32
))
288 pp
= (u32
*)get_property(dp
, "linux,bootx-linebytes", &len
);
290 pp
= (u32
*)get_property(dp
, "linebytes", &len
);
291 if (pp
&& len
== sizeof(u32
))
294 pitch
= width
* ((depth
+ 7) / 8);
296 rsize
= (unsigned long)pitch
* (unsigned long)height
;
298 /* Ok, now we try to figure out the address of the framebuffer.
300 * Unfortunately, Open Firmware doesn't provide a standard way to do
301 * so. All we can do is a dodgy heuristic that happens to work in
302 * practice. On most machines, the "address" property contains what
303 * we need, though not on Matrox cards found in IBM machines. What I've
304 * found that appears to give good results is to go through the PCI
305 * ranges and pick one that is both big enough and if possible encloses
306 * the "address" property. If none match, we pick the biggest
308 up
= (u32
*)get_property(dp
, "linux,bootx-addr", &len
);
310 up
= (u32
*)get_property(dp
, "address", &len
);
311 if (up
&& len
== sizeof(u32
))
314 for (i
= 0; (addrp
= of_get_address(dp
, i
, &asize
, &flags
))
318 if (!(flags
& IORESOURCE_MEM
))
322 rstart
= of_translate_address(dp
, addrp
);
323 if (rstart
== OF_BAD_ADDR
)
325 if (addr_prop
&& (rstart
<= addr_prop
) &&
326 ((rstart
+ asize
) >= (addr_prop
+ rsize
)))
332 if (rsize
> max_size
) {
334 address
= OF_BAD_ADDR
;
337 if (address
== OF_BAD_ADDR
)
340 if (address
== OF_BAD_ADDR
&& addr_prop
)
341 address
= (u64
)addr_prop
;
342 if (address
!= OF_BAD_ADDR
) {
343 /* kludge for valkyrie */
344 if (strcmp(dp
->name
, "valkyrie") == 0)
346 offb_init_fb(dp
->name
, dp
->full_name
, width
, height
, depth
,
351 static void __init
offb_init_fb(const char *name
, const char *full_name
,
352 int width
, int height
, int depth
,
353 int pitch
, unsigned long address
,
354 struct device_node
*dp
)
356 unsigned long res_size
= pitch
* height
* (depth
+ 7) / 8;
357 struct offb_par
*par
= &default_par
;
358 unsigned long res_start
= address
;
359 struct fb_fix_screeninfo
*fix
;
360 struct fb_var_screeninfo
*var
;
361 struct fb_info
*info
;
364 if (!request_mem_region(res_start
, res_size
, "offb"))
368 "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",
369 width
, height
, name
, address
, depth
, pitch
);
370 if (depth
!= 8 && depth
!= 15 && depth
!= 16 && depth
!= 32) {
371 printk(KERN_ERR
"%s: can't use depth = %d\n", full_name
,
373 release_mem_region(res_start
, res_size
);
377 size
= sizeof(struct fb_info
) + sizeof(u32
) * 17;
379 info
= kmalloc(size
, GFP_ATOMIC
);
382 release_mem_region(res_start
, res_size
);
385 memset(info
, 0, size
);
390 strcpy(fix
->id
, "OFfb ");
391 strncat(fix
->id
, name
, sizeof(fix
->id
) - sizeof("OFfb "));
392 fix
->id
[sizeof(fix
->id
) - 1] = '\0';
394 var
->xres
= var
->xres_virtual
= width
;
395 var
->yres
= var
->yres_virtual
= height
;
396 fix
->line_length
= pitch
;
398 fix
->smem_start
= address
;
399 fix
->smem_len
= pitch
* height
;
400 fix
->type
= FB_TYPE_PACKED_PIXELS
;
403 par
->cmap_type
= cmap_unknown
;
406 /* Palette hacks disabled for now */
408 if (dp
&& !strncmp(name
, "ATY,Rage128", 11)) {
409 unsigned long regbase
= dp
->addrs
[2].address
;
410 par
->cmap_adr
= ioremap(regbase
, 0x1FFF);
411 par
->cmap_type
= cmap_r128
;
412 } else if (dp
&& (!strncmp(name
, "ATY,RageM3pA", 12)
413 || !strncmp(name
, "ATY,RageM3p12A", 14))) {
414 unsigned long regbase
=
415 dp
->parent
->addrs
[2].address
;
416 par
->cmap_adr
= ioremap(regbase
, 0x1FFF);
417 par
->cmap_type
= cmap_M3A
;
418 } else if (dp
&& !strncmp(name
, "ATY,RageM3pB", 12)) {
419 unsigned long regbase
=
420 dp
->parent
->addrs
[2].address
;
421 par
->cmap_adr
= ioremap(regbase
, 0x1FFF);
422 par
->cmap_type
= cmap_M3B
;
423 } else if (dp
&& !strncmp(name
, "ATY,Rage6", 9)) {
424 unsigned long regbase
= dp
->addrs
[1].address
;
425 par
->cmap_adr
= ioremap(regbase
, 0x1FFF);
426 par
->cmap_type
= cmap_radeon
;
427 } else if (!strncmp(name
, "ATY,", 4)) {
428 unsigned long base
= address
& 0xff000000UL
;
430 ioremap(base
+ 0x7ff000, 0x1000) + 0xcc0;
431 par
->cmap_data
= par
->cmap_adr
+ 1;
432 par
->cmap_type
= cmap_m64
;
433 } else if (device_is_compatible(dp
, "pci1014,b7")) {
434 unsigned long regbase
= dp
->addrs
[0].address
;
435 par
->cmap_adr
= ioremap(regbase
+ 0x6000, 0x1000);
436 par
->cmap_type
= cmap_gxt2000
;
439 fix
->visual
= par
->cmap_adr
? FB_VISUAL_PSEUDOCOLOR
440 : FB_VISUAL_STATIC_PSEUDOCOLOR
;
442 fix
->visual
= /* par->cmap_adr ? FB_VISUAL_DIRECTCOLOR
443 : */ FB_VISUAL_TRUECOLOR
;
445 var
->xoffset
= var
->yoffset
= 0;
448 var
->bits_per_pixel
= 8;
451 var
->green
.offset
= 0;
452 var
->green
.length
= 8;
453 var
->blue
.offset
= 0;
454 var
->blue
.length
= 8;
455 var
->transp
.offset
= 0;
456 var
->transp
.length
= 0;
458 case 15: /* RGB 555 */
459 var
->bits_per_pixel
= 16;
460 var
->red
.offset
= 10;
462 var
->green
.offset
= 5;
463 var
->green
.length
= 5;
464 var
->blue
.offset
= 0;
465 var
->blue
.length
= 5;
466 var
->transp
.offset
= 0;
467 var
->transp
.length
= 0;
469 case 16: /* RGB 565 */
470 var
->bits_per_pixel
= 16;
471 var
->red
.offset
= 11;
473 var
->green
.offset
= 5;
474 var
->green
.length
= 6;
475 var
->blue
.offset
= 0;
476 var
->blue
.length
= 5;
477 var
->transp
.offset
= 0;
478 var
->transp
.length
= 0;
480 case 32: /* RGB 888 */
481 var
->bits_per_pixel
= 32;
482 var
->red
.offset
= 16;
484 var
->green
.offset
= 8;
485 var
->green
.length
= 8;
486 var
->blue
.offset
= 0;
487 var
->blue
.length
= 8;
488 var
->transp
.offset
= 24;
489 var
->transp
.length
= 8;
492 var
->red
.msb_right
= var
->green
.msb_right
= var
->blue
.msb_right
=
493 var
->transp
.msb_right
= 0;
497 var
->height
= var
->width
= -1;
498 var
->pixclock
= 10000;
499 var
->left_margin
= var
->right_margin
= 16;
500 var
->upper_margin
= var
->lower_margin
= 16;
501 var
->hsync_len
= var
->vsync_len
= 8;
503 var
->vmode
= FB_VMODE_NONINTERLACED
;
505 info
->fbops
= &offb_ops
;
506 info
->screen_base
= ioremap(address
, fix
->smem_len
);
508 info
->pseudo_palette
= (void *) (info
+ 1);
509 info
->flags
= FBINFO_DEFAULT
;
511 fb_alloc_cmap(&info
->cmap
, 256, 0);
513 if (register_framebuffer(info
) < 0) {
515 release_mem_region(res_start
, res_size
);
519 printk(KERN_INFO
"fb%d: Open Firmware frame buffer device on %s\n",
520 info
->node
, full_name
);
523 module_init(offb_init
);
524 MODULE_LICENSE("GPL");