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