offb.c revision 40cd3a4564ed6b7bc0279430120ca0e9b83cf486
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/module.h> 16#include <linux/kernel.h> 17#include <linux/errno.h> 18#include <linux/string.h> 19#include <linux/mm.h> 20#include <linux/slab.h> 21#include <linux/vmalloc.h> 22#include <linux/delay.h> 23#include <linux/interrupt.h> 24#include <linux/fb.h> 25#include <linux/init.h> 26#include <linux/ioport.h> 27#include <linux/pci.h> 28#include <asm/io.h> 29#include <asm/prom.h> 30 31#ifdef CONFIG_PPC64 32#include <asm/pci-bridge.h> 33#endif 34 35#ifdef CONFIG_PPC32 36#include <asm/bootx.h> 37#endif 38 39#include "macmodes.h" 40 41/* Supported palette hacks */ 42enum { 43 cmap_unknown, 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 */ 50}; 51 52struct offb_par { 53 volatile void __iomem *cmap_adr; 54 volatile void __iomem *cmap_data; 55 int cmap_type; 56 int blanked; 57}; 58 59struct offb_par default_par; 60 61 /* 62 * Interface used by the world 63 */ 64 65static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 66 u_int transp, struct fb_info *info); 67static int offb_blank(int blank, struct fb_info *info); 68 69#ifdef CONFIG_PPC32 70extern boot_infos_t *boot_infos; 71#endif 72 73static struct fb_ops offb_ops = { 74 .owner = THIS_MODULE, 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, 80}; 81 82 /* 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. 86 */ 87 88static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 89 u_int transp, struct fb_info *info) 90{ 91 struct offb_par *par = (struct offb_par *) info->par; 92 int i, depth; 93 u32 *pal = info->pseudo_palette; 94 95 depth = info->var.bits_per_pixel; 96 if (depth == 16) 97 depth = (info->var.green.length == 5) ? 15 : 16; 98 99 if (regno > 255 || 100 (depth == 16 && regno > 63) || 101 (depth == 15 && regno > 31)) 102 return 1; 103 104 if (regno < 16) { 105 switch (depth) { 106 case 15: 107 pal[regno] = (regno << 10) | (regno << 5) | regno; 108 break; 109 case 16: 110 pal[regno] = (regno << 11) | (regno << 5) | regno; 111 break; 112 case 24: 113 pal[regno] = (regno << 16) | (regno << 8) | regno; 114 break; 115 case 32: 116 i = (regno << 8) | regno; 117 pal[regno] = (i << 16) | i; 118 break; 119 } 120 } 121 122 red >>= 8; 123 green >>= 8; 124 blue >>= 8; 125 126 if (!par->cmap_adr) 127 return 0; 128 129 switch (par->cmap_type) { 130 case cmap_m64: 131 writeb(regno, par->cmap_adr); 132 writeb(red, par->cmap_data); 133 writeb(green, par->cmap_data); 134 writeb(blue, par->cmap_data); 135 break; 136 case cmap_M3A: 137 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ 138 out_le32(par->cmap_adr + 0x58, 139 in_le32(par->cmap_adr + 0x58) & ~0x20); 140 case cmap_r128: 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)); 145 break; 146 case cmap_M3B: 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)); 153 break; 154 case cmap_radeon: 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)); 158 break; 159 case cmap_gxt2000: 160 out_le32(((unsigned __iomem *) par->cmap_adr) + regno, 161 (red << 16 | green << 8 | blue)); 162 break; 163 } 164 165 return 0; 166} 167 168 /* 169 * Blank the display. 170 */ 171 172static int offb_blank(int blank, struct fb_info *info) 173{ 174 struct offb_par *par = (struct offb_par *) info->par; 175 int i, j; 176 177 if (!par->cmap_adr) 178 return 0; 179 180 if (!par->blanked) 181 if (!blank) 182 return 0; 183 184 par->blanked = blank; 185 186 if (blank) 187 for (i = 0; i < 256; i++) { 188 switch (par->cmap_type) { 189 case cmap_m64: 190 writeb(i, par->cmap_adr); 191 for (j = 0; j < 3; j++) 192 writeb(0, par->cmap_data); 193 break; 194 case cmap_M3A: 195 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ 196 out_le32(par->cmap_adr + 0x58, 197 in_le32(par->cmap_adr + 0x58) & ~0x20); 198 case cmap_r128: 199 /* Set palette index & data */ 200 out_8(par->cmap_adr + 0xb0, i); 201 out_le32(par->cmap_adr + 0xb4, 0); 202 break; 203 case cmap_M3B: 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); 210 break; 211 case cmap_radeon: 212 out_8(par->cmap_adr + 0xb0, i); 213 out_le32(par->cmap_adr + 0xb4, 0); 214 break; 215 case cmap_gxt2000: 216 out_le32(((unsigned __iomem *) par->cmap_adr) + i, 217 0); 218 break; 219 } 220 } else 221 fb_set_cmap(&info->cmap, info); 222 return 0; 223} 224 225 226static void __iomem *offb_map_reg(struct device_node *np, int index, 227 unsigned long offset, unsigned long size) 228{ 229 const u32 *addrp; 230 u64 asize, taddr; 231 unsigned int flags; 232 233 addrp = of_get_pci_address(np, index, &asize, &flags); 234 if (addrp == NULL) 235 addrp = of_get_address(np, index, &asize, &flags); 236 if (addrp == NULL) 237 return NULL; 238 if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) 239 return NULL; 240 if ((offset + size) > asize) 241 return NULL; 242 taddr = of_translate_address(np, addrp); 243 if (taddr == OF_BAD_ADDR) 244 return NULL; 245 return ioremap(taddr + offset, size); 246} 247 248static void __init offb_init_fb(const char *name, const char *full_name, 249 int width, int height, int depth, 250 int pitch, unsigned long address, 251 struct device_node *dp) 252{ 253 unsigned long res_size = pitch * height * (depth + 7) / 8; 254 struct offb_par *par = &default_par; 255 unsigned long res_start = address; 256 struct fb_fix_screeninfo *fix; 257 struct fb_var_screeninfo *var; 258 struct fb_info *info; 259 int size; 260 261 if (!request_mem_region(res_start, res_size, "offb")) 262 return; 263 264 printk(KERN_INFO 265 "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n", 266 width, height, name, address, depth, pitch); 267 if (depth != 8 && depth != 15 && depth != 16 && depth != 32) { 268 printk(KERN_ERR "%s: can't use depth = %d\n", full_name, 269 depth); 270 release_mem_region(res_start, res_size); 271 return; 272 } 273 274 size = sizeof(struct fb_info) + sizeof(u32) * 17; 275 276 info = kmalloc(size, GFP_ATOMIC); 277 278 if (info == 0) { 279 release_mem_region(res_start, res_size); 280 return; 281 } 282 memset(info, 0, size); 283 284 fix = &info->fix; 285 var = &info->var; 286 287 strcpy(fix->id, "OFfb "); 288 strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb ")); 289 fix->id[sizeof(fix->id) - 1] = '\0'; 290 291 var->xres = var->xres_virtual = width; 292 var->yres = var->yres_virtual = height; 293 fix->line_length = pitch; 294 295 fix->smem_start = address; 296 fix->smem_len = pitch * height; 297 fix->type = FB_TYPE_PACKED_PIXELS; 298 fix->type_aux = 0; 299 300 par->cmap_type = cmap_unknown; 301 if (depth == 8) { 302 if (dp && !strncmp(name, "ATY,Rage128", 11)) { 303 par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); 304 if (par->cmap_adr) 305 par->cmap_type = cmap_r128; 306 } else if (dp && (!strncmp(name, "ATY,RageM3pA", 12) 307 || !strncmp(name, "ATY,RageM3p12A", 14))) { 308 par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); 309 if (par->cmap_adr) 310 par->cmap_type = cmap_M3A; 311 } else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) { 312 par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); 313 if (par->cmap_adr) 314 par->cmap_type = cmap_M3B; 315 } else if (dp && !strncmp(name, "ATY,Rage6", 9)) { 316 par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff); 317 if (par->cmap_adr) 318 par->cmap_type = cmap_radeon; 319 } else if (!strncmp(name, "ATY,", 4)) { 320 unsigned long base = address & 0xff000000UL; 321 par->cmap_adr = 322 ioremap(base + 0x7ff000, 0x1000) + 0xcc0; 323 par->cmap_data = par->cmap_adr + 1; 324 par->cmap_type = cmap_m64; 325 } else if (dp && (device_is_compatible(dp, "pci1014,b7") || 326 device_is_compatible(dp, "pci1014,21c"))) { 327 par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000); 328 if (par->cmap_adr) 329 par->cmap_type = cmap_gxt2000; 330 } 331 fix->visual = (par->cmap_type != cmap_unknown) ? 332 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; 333 } else 334 fix->visual = FB_VISUAL_TRUECOLOR; 335 336 var->xoffset = var->yoffset = 0; 337 switch (depth) { 338 case 8: 339 var->bits_per_pixel = 8; 340 var->red.offset = 0; 341 var->red.length = 8; 342 var->green.offset = 0; 343 var->green.length = 8; 344 var->blue.offset = 0; 345 var->blue.length = 8; 346 var->transp.offset = 0; 347 var->transp.length = 0; 348 break; 349 case 15: /* RGB 555 */ 350 var->bits_per_pixel = 16; 351 var->red.offset = 10; 352 var->red.length = 5; 353 var->green.offset = 5; 354 var->green.length = 5; 355 var->blue.offset = 0; 356 var->blue.length = 5; 357 var->transp.offset = 0; 358 var->transp.length = 0; 359 break; 360 case 16: /* RGB 565 */ 361 var->bits_per_pixel = 16; 362 var->red.offset = 11; 363 var->red.length = 5; 364 var->green.offset = 5; 365 var->green.length = 6; 366 var->blue.offset = 0; 367 var->blue.length = 5; 368 var->transp.offset = 0; 369 var->transp.length = 0; 370 break; 371 case 32: /* RGB 888 */ 372 var->bits_per_pixel = 32; 373 var->red.offset = 16; 374 var->red.length = 8; 375 var->green.offset = 8; 376 var->green.length = 8; 377 var->blue.offset = 0; 378 var->blue.length = 8; 379 var->transp.offset = 24; 380 var->transp.length = 8; 381 break; 382 } 383 var->red.msb_right = var->green.msb_right = var->blue.msb_right = 384 var->transp.msb_right = 0; 385 var->grayscale = 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; 395 396 info->fbops = &offb_ops; 397 info->screen_base = ioremap(address, fix->smem_len); 398 info->par = par; 399 info->pseudo_palette = (void *) (info + 1); 400 info->flags = FBINFO_DEFAULT; 401 402 fb_alloc_cmap(&info->cmap, 256, 0); 403 404 if (register_framebuffer(info) < 0) { 405 iounmap(par->cmap_adr); 406 par->cmap_adr = NULL; 407 iounmap(info->screen_base); 408 kfree(info); 409 release_mem_region(res_start, res_size); 410 return; 411 } 412 413 printk(KERN_INFO "fb%d: Open Firmware frame buffer device on %s\n", 414 info->node, full_name); 415} 416 417 418static void __init offb_init_nodriver(struct device_node *dp, int no_real_node) 419{ 420 unsigned int len; 421 int i, width = 640, height = 480, depth = 8, pitch = 640; 422 unsigned int flags, rsize, addr_prop = 0; 423 unsigned long max_size = 0; 424 u64 rstart, address = OF_BAD_ADDR; 425 const u32 *pp, *addrp, *up; 426 u64 asize; 427 428 pp = of_get_property(dp, "linux,bootx-depth", &len); 429 if (pp == NULL) 430 pp = of_get_property(dp, "depth", &len); 431 if (pp && len == sizeof(u32)) 432 depth = *pp; 433 434 pp = of_get_property(dp, "linux,bootx-width", &len); 435 if (pp == NULL) 436 pp = of_get_property(dp, "width", &len); 437 if (pp && len == sizeof(u32)) 438 width = *pp; 439 440 pp = of_get_property(dp, "linux,bootx-height", &len); 441 if (pp == NULL) 442 pp = of_get_property(dp, "height", &len); 443 if (pp && len == sizeof(u32)) 444 height = *pp; 445 446 pp = of_get_property(dp, "linux,bootx-linebytes", &len); 447 if (pp == NULL) 448 pp = of_get_property(dp, "linebytes", &len); 449 if (pp && len == sizeof(u32) && (*pp != 0xffffffffu)) 450 pitch = *pp; 451 else 452 pitch = width * ((depth + 7) / 8); 453 454 rsize = (unsigned long)pitch * (unsigned long)height; 455 456 /* Ok, now we try to figure out the address of the framebuffer. 457 * 458 * Unfortunately, Open Firmware doesn't provide a standard way to do 459 * so. All we can do is a dodgy heuristic that happens to work in 460 * practice. On most machines, the "address" property contains what 461 * we need, though not on Matrox cards found in IBM machines. What I've 462 * found that appears to give good results is to go through the PCI 463 * ranges and pick one that is both big enough and if possible encloses 464 * the "address" property. If none match, we pick the biggest 465 */ 466 up = of_get_property(dp, "linux,bootx-addr", &len); 467 if (up == NULL) 468 up = of_get_property(dp, "address", &len); 469 if (up && len == sizeof(u32)) 470 addr_prop = *up; 471 472 /* Hack for when BootX is passing us */ 473 if (no_real_node) 474 goto skip_addr; 475 476 for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags)) 477 != NULL; i++) { 478 int match_addrp = 0; 479 480 if (!(flags & IORESOURCE_MEM)) 481 continue; 482 if (asize < rsize) 483 continue; 484 rstart = of_translate_address(dp, addrp); 485 if (rstart == OF_BAD_ADDR) 486 continue; 487 if (addr_prop && (rstart <= addr_prop) && 488 ((rstart + asize) >= (addr_prop + rsize))) 489 match_addrp = 1; 490 if (match_addrp) { 491 address = addr_prop; 492 break; 493 } 494 if (rsize > max_size) { 495 max_size = rsize; 496 address = OF_BAD_ADDR; 497 } 498 499 if (address == OF_BAD_ADDR) 500 address = rstart; 501 } 502 skip_addr: 503 if (address == OF_BAD_ADDR && addr_prop) 504 address = (u64)addr_prop; 505 if (address != OF_BAD_ADDR) { 506 /* kludge for valkyrie */ 507 if (strcmp(dp->name, "valkyrie") == 0) 508 address += 0x1000; 509 offb_init_fb(no_real_node ? "bootx" : dp->name, 510 no_real_node ? "display" : dp->full_name, 511 width, height, depth, pitch, address, 512 no_real_node ? NULL : dp); 513 } 514} 515 516static int __init offb_init(void) 517{ 518 struct device_node *dp = NULL, *boot_disp = NULL; 519 520 if (fb_get_options("offb", NULL)) 521 return -ENODEV; 522 523 /* Check if we have a MacOS display without a node spec */ 524 if (of_get_property(of_chosen, "linux,bootx-noscreen", NULL) != NULL) { 525 /* The old code tried to work out which node was the MacOS 526 * display based on the address. I'm dropping that since the 527 * lack of a node spec only happens with old BootX versions 528 * (users can update) and with this code, they'll still get 529 * a display (just not the palette hacks). 530 */ 531 offb_init_nodriver(of_chosen, 1); 532 } 533 534 for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { 535 if (of_get_property(dp, "linux,opened", NULL) && 536 of_get_property(dp, "linux,boot-display", NULL)) { 537 boot_disp = dp; 538 offb_init_nodriver(dp, 0); 539 } 540 } 541 for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { 542 if (of_get_property(dp, "linux,opened", NULL) && 543 dp != boot_disp) 544 offb_init_nodriver(dp, 0); 545 } 546 547 return 0; 548} 549 550 551module_init(offb_init); 552MODULE_LICENSE("GPL"); 553