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
65 static int offb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
66 u_int transp
, struct fb_info
*info
);
67 static int offb_blank(int blank
, struct fb_info
*info
);
70 extern boot_infos_t
*boot_infos
;
73 static struct fb_ops offb_ops
= {
75 .fb_setcolreg
= offb_setcolreg
,
76 .fb_blank
= offb_blank
,
77 .fb_fillrect
= cfb_fillrect
,
78 .fb_copyarea
= cfb_copyarea
,
79 .fb_imageblit
= cfb_imageblit
,
83 * Set a single color register. The values supplied are already
84 * rounded down to the hardware's capabilities (according to the
85 * entries in the var structure). Return != 0 for invalid regno.
88 static int offb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
89 u_int transp
, struct fb_info
*info
)
91 struct offb_par
*par
= (struct offb_par
*) info
->par
;
93 u32
*pal
= info
->pseudo_palette
;
95 depth
= info
->var
.bits_per_pixel
;
97 depth
= (info
->var
.green
.length
== 5) ? 15 : 16;
100 (depth
== 16 && regno
> 63) ||
101 (depth
== 15 && regno
> 31))
107 pal
[regno
] = (regno
<< 10) | (regno
<< 5) | regno
;
110 pal
[regno
] = (regno
<< 11) | (regno
<< 5) | regno
;
113 pal
[regno
] = (regno
<< 16) | (regno
<< 8) | regno
;
116 i
= (regno
<< 8) | regno
;
117 pal
[regno
] = (i
<< 16) | i
;
129 switch (par
->cmap_type
) {
131 writeb(regno
, par
->cmap_adr
);
132 writeb(red
, par
->cmap_data
);
133 writeb(green
, par
->cmap_data
);
134 writeb(blue
, par
->cmap_data
);
137 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
138 out_le32(par
->cmap_adr
+ 0x58,
139 in_le32(par
->cmap_adr
+ 0x58) & ~0x20);
141 /* Set palette index & data */
142 out_8(par
->cmap_adr
+ 0xb0, regno
);
143 out_le32(par
->cmap_adr
+ 0xb4,
144 (red
<< 16 | green
<< 8 | blue
));
147 /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
148 out_le32(par
->cmap_adr
+ 0x58,
149 in_le32(par
->cmap_adr
+ 0x58) | 0x20);
150 /* Set palette index & data */
151 out_8(par
->cmap_adr
+ 0xb0, regno
);
152 out_le32(par
->cmap_adr
+ 0xb4, (red
<< 16 | green
<< 8 | blue
));
155 /* Set palette index & data (could be smarter) */
156 out_8(par
->cmap_adr
+ 0xb0, regno
);
157 out_le32(par
->cmap_adr
+ 0xb4, (red
<< 16 | green
<< 8 | blue
));
160 out_le32((unsigned __iomem
*) par
->cmap_adr
+ regno
,
161 (red
<< 16 | green
<< 8 | blue
));
172 static int offb_blank(int blank
, struct fb_info
*info
)
174 struct offb_par
*par
= (struct offb_par
*) info
->par
;
184 par
->blanked
= blank
;
187 for (i
= 0; i
< 256; i
++) {
188 switch (par
->cmap_type
) {
190 writeb(i
, par
->cmap_adr
);
191 for (j
= 0; j
< 3; j
++)
192 writeb(0, par
->cmap_data
);
195 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
196 out_le32(par
->cmap_adr
+ 0x58,
197 in_le32(par
->cmap_adr
+ 0x58) & ~0x20);
199 /* Set palette index & data */
200 out_8(par
->cmap_adr
+ 0xb0, i
);
201 out_le32(par
->cmap_adr
+ 0xb4, 0);
204 /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
205 out_le32(par
->cmap_adr
+ 0x58,
206 in_le32(par
->cmap_adr
+ 0x58) | 0x20);
207 /* Set palette index & data */
208 out_8(par
->cmap_adr
+ 0xb0, i
);
209 out_le32(par
->cmap_adr
+ 0xb4, 0);
212 out_8(par
->cmap_adr
+ 0xb0, i
);
213 out_le32(par
->cmap_adr
+ 0xb4, 0);
216 out_le32((unsigned __iomem
*) par
->cmap_adr
+ i
,
221 fb_set_cmap(&info
->cmap
, info
);
226 static void __iomem
*offb_map_reg(struct device_node
*np
, int index
,
227 unsigned long offset
, unsigned long size
)
231 if (of_address_to_resource(np
, index
, &r
))
233 if ((r
.start
+ offset
+ size
) > r
.end
)
235 return ioremap(r
.start
+ offset
, size
);
238 static void __init
offb_init_fb(const char *name
, const char *full_name
,
239 int width
, int height
, int depth
,
240 int pitch
, unsigned long address
,
241 struct device_node
*dp
)
243 unsigned long res_size
= pitch
* height
* (depth
+ 7) / 8;
244 struct offb_par
*par
= &default_par
;
245 unsigned long res_start
= address
;
246 struct fb_fix_screeninfo
*fix
;
247 struct fb_var_screeninfo
*var
;
248 struct fb_info
*info
;
251 if (!request_mem_region(res_start
, res_size
, "offb"))
255 "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",
256 width
, height
, name
, address
, depth
, pitch
);
257 if (depth
!= 8 && depth
!= 15 && depth
!= 16 && depth
!= 32) {
258 printk(KERN_ERR
"%s: can't use depth = %d\n", full_name
,
260 release_mem_region(res_start
, res_size
);
264 size
= sizeof(struct fb_info
) + sizeof(u32
) * 17;
266 info
= kmalloc(size
, GFP_ATOMIC
);
269 release_mem_region(res_start
, res_size
);
272 memset(info
, 0, size
);
277 strcpy(fix
->id
, "OFfb ");
278 strncat(fix
->id
, name
, sizeof(fix
->id
) - sizeof("OFfb "));
279 fix
->id
[sizeof(fix
->id
) - 1] = '\0';
281 var
->xres
= var
->xres_virtual
= width
;
282 var
->yres
= var
->yres_virtual
= height
;
283 fix
->line_length
= pitch
;
285 fix
->smem_start
= address
;
286 fix
->smem_len
= pitch
* height
;
287 fix
->type
= FB_TYPE_PACKED_PIXELS
;
290 par
->cmap_type
= cmap_unknown
;
292 /* Palette hacks disabled for now */
293 if (dp
&& !strncmp(name
, "ATY,Rage128", 11)) {
294 par
->cmap_adr
= offb_map_reg(dp
, 2, 0, 0x1fff);
296 par
->cmap_type
= cmap_r128
;
297 } else if (dp
&& (!strncmp(name
, "ATY,RageM3pA", 12)
298 || !strncmp(name
, "ATY,RageM3p12A", 14))) {
299 par
->cmap_adr
= offb_map_reg(dp
, 2, 0, 0x1fff);
301 par
->cmap_type
= cmap_M3A
;
302 } else if (dp
&& !strncmp(name
, "ATY,RageM3pB", 12)) {
303 par
->cmap_adr
= offb_map_reg(dp
, 2, 0, 0x1fff);
305 par
->cmap_type
= cmap_M3B
;
306 } else if (dp
&& !strncmp(name
, "ATY,Rage6", 9)) {
307 par
->cmap_adr
= offb_map_reg(dp
, 1, 0, 0x1fff);
309 par
->cmap_type
= cmap_radeon
;
310 } else if (!strncmp(name
, "ATY,", 4)) {
311 unsigned long base
= address
& 0xff000000UL
;
313 ioremap(base
+ 0x7ff000, 0x1000) + 0xcc0;
314 par
->cmap_data
= par
->cmap_adr
+ 1;
315 par
->cmap_type
= cmap_m64
;
316 } else if (dp
&& device_is_compatible(dp
, "pci1014,b7")) {
317 par
->cmap_adr
= offb_map_reg(dp
, 0, 0x6000, 0x1000);
319 par
->cmap_type
= cmap_gxt2000
;
321 fix
->visual
= (par
->cmap_type
!= cmap_unknown
) ?
322 FB_VISUAL_PSEUDOCOLOR
: FB_VISUAL_STATIC_PSEUDOCOLOR
;
324 fix
->visual
= FB_VISUAL_TRUECOLOR
;
326 var
->xoffset
= var
->yoffset
= 0;
329 var
->bits_per_pixel
= 8;
332 var
->green
.offset
= 0;
333 var
->green
.length
= 8;
334 var
->blue
.offset
= 0;
335 var
->blue
.length
= 8;
336 var
->transp
.offset
= 0;
337 var
->transp
.length
= 0;
339 case 15: /* RGB 555 */
340 var
->bits_per_pixel
= 16;
341 var
->red
.offset
= 10;
343 var
->green
.offset
= 5;
344 var
->green
.length
= 5;
345 var
->blue
.offset
= 0;
346 var
->blue
.length
= 5;
347 var
->transp
.offset
= 0;
348 var
->transp
.length
= 0;
350 case 16: /* RGB 565 */
351 var
->bits_per_pixel
= 16;
352 var
->red
.offset
= 11;
354 var
->green
.offset
= 5;
355 var
->green
.length
= 6;
356 var
->blue
.offset
= 0;
357 var
->blue
.length
= 5;
358 var
->transp
.offset
= 0;
359 var
->transp
.length
= 0;
361 case 32: /* RGB 888 */
362 var
->bits_per_pixel
= 32;
363 var
->red
.offset
= 16;
365 var
->green
.offset
= 8;
366 var
->green
.length
= 8;
367 var
->blue
.offset
= 0;
368 var
->blue
.length
= 8;
369 var
->transp
.offset
= 24;
370 var
->transp
.length
= 8;
373 var
->red
.msb_right
= var
->green
.msb_right
= var
->blue
.msb_right
=
374 var
->transp
.msb_right
= 0;
378 var
->height
= var
->width
= -1;
379 var
->pixclock
= 10000;
380 var
->left_margin
= var
->right_margin
= 16;
381 var
->upper_margin
= var
->lower_margin
= 16;
382 var
->hsync_len
= var
->vsync_len
= 8;
384 var
->vmode
= FB_VMODE_NONINTERLACED
;
386 info
->fbops
= &offb_ops
;
387 info
->screen_base
= ioremap(address
, fix
->smem_len
);
389 info
->pseudo_palette
= (void *) (info
+ 1);
390 info
->flags
= FBINFO_DEFAULT
;
392 fb_alloc_cmap(&info
->cmap
, 256, 0);
394 if (register_framebuffer(info
) < 0) {
396 release_mem_region(res_start
, res_size
);
400 printk(KERN_INFO
"fb%d: Open Firmware frame buffer device on %s\n",
401 info
->node
, full_name
);
405 static void __init
offb_init_nodriver(struct device_node
*dp
, int no_real_node
)
408 int i
, width
= 640, height
= 480, depth
= 8, pitch
= 640;
409 unsigned int flags
, rsize
, addr_prop
= 0;
410 unsigned long max_size
= 0;
411 u64 rstart
, address
= OF_BAD_ADDR
;
412 const u32
*pp
, *addrp
, *up
;
415 pp
= get_property(dp
, "linux,bootx-depth", &len
);
417 pp
= get_property(dp
, "depth", &len
);
418 if (pp
&& len
== sizeof(u32
))
421 pp
= get_property(dp
, "linux,bootx-width", &len
);
423 pp
= get_property(dp
, "width", &len
);
424 if (pp
&& len
== sizeof(u32
))
427 pp
= get_property(dp
, "linux,bootx-height", &len
);
429 pp
= get_property(dp
, "height", &len
);
430 if (pp
&& len
== sizeof(u32
))
433 pp
= get_property(dp
, "linux,bootx-linebytes", &len
);
435 pp
= get_property(dp
, "linebytes", &len
);
436 if (pp
&& len
== sizeof(u32
))
439 pitch
= width
* ((depth
+ 7) / 8);
441 rsize
= (unsigned long)pitch
* (unsigned long)height
;
443 /* Ok, now we try to figure out the address of the framebuffer.
445 * Unfortunately, Open Firmware doesn't provide a standard way to do
446 * so. All we can do is a dodgy heuristic that happens to work in
447 * practice. On most machines, the "address" property contains what
448 * we need, though not on Matrox cards found in IBM machines. What I've
449 * found that appears to give good results is to go through the PCI
450 * ranges and pick one that is both big enough and if possible encloses
451 * the "address" property. If none match, we pick the biggest
453 up
= get_property(dp
, "linux,bootx-addr", &len
);
455 up
= get_property(dp
, "address", &len
);
456 if (up
&& len
== sizeof(u32
))
459 /* Hack for when BootX is passing us */
463 for (i
= 0; (addrp
= of_get_address(dp
, i
, &asize
, &flags
))
467 if (!(flags
& IORESOURCE_MEM
))
471 rstart
= of_translate_address(dp
, addrp
);
472 if (rstart
== OF_BAD_ADDR
)
474 if (addr_prop
&& (rstart
<= addr_prop
) &&
475 ((rstart
+ asize
) >= (addr_prop
+ rsize
)))
481 if (rsize
> max_size
) {
483 address
= OF_BAD_ADDR
;
486 if (address
== OF_BAD_ADDR
)
490 if (address
== OF_BAD_ADDR
&& addr_prop
)
491 address
= (u64
)addr_prop
;
492 if (address
!= OF_BAD_ADDR
) {
493 /* kludge for valkyrie */
494 if (strcmp(dp
->name
, "valkyrie") == 0)
496 offb_init_fb(no_real_node
? "bootx" : dp
->name
,
497 no_real_node
? "display" : dp
->full_name
,
498 width
, height
, depth
, pitch
, address
,
499 no_real_node
? dp
: NULL
);
503 static int __init
offb_init(void)
505 struct device_node
*dp
= NULL
, *boot_disp
= NULL
;
507 if (fb_get_options("offb", NULL
))
510 /* Check if we have a MacOS display without a node spec */
511 if (get_property(of_chosen
, "linux,bootx-noscreen", NULL
) != NULL
) {
512 /* The old code tried to work out which node was the MacOS
513 * display based on the address. I'm dropping that since the
514 * lack of a node spec only happens with old BootX versions
515 * (users can update) and with this code, they'll still get
516 * a display (just not the palette hacks).
518 offb_init_nodriver(of_chosen
, 1);
521 for (dp
= NULL
; (dp
= of_find_node_by_type(dp
, "display"));) {
522 if (get_property(dp
, "linux,opened", NULL
) &&
523 get_property(dp
, "linux,boot-display", NULL
)) {
525 offb_init_nodriver(dp
, 0);
528 for (dp
= NULL
; (dp
= of_find_node_by_type(dp
, "display"));) {
529 if (get_property(dp
, "linux,opened", NULL
) &&
531 offb_init_nodriver(dp
, 0);
538 module_init(offb_init
);
539 MODULE_LICENSE("GPL");