722 lines · c
1/*2 * linux/drivers/video/offb.c -- Open Firmware based frame buffer device3 *4 * Copyright (C) 1997 Geert Uytterhoeven5 *6 * This driver is partly based on the PowerMac console driver:7 *8 * Copyright (C) 1996 Paul Mackerras9 *10 * This file is subject to the terms and conditions of the GNU General Public11 * License. See the file COPYING in the main directory of this archive for12 * more details.13 */14 15#include <linux/aperture.h>16#include <linux/module.h>17#include <linux/kernel.h>18#include <linux/errno.h>19#include <linux/string.h>20#include <linux/mm.h>21#include <linux/vmalloc.h>22#include <linux/delay.h>23#include <linux/of.h>24#include <linux/of_address.h>25#include <linux/interrupt.h>26#include <linux/fb.h>27#include <linux/init.h>28#include <linux/ioport.h>29#include <linux/pci.h>30#include <linux/platform_device.h>31#include <asm/io.h>32 33#ifdef CONFIG_PPC3234#include <asm/bootx.h>35#endif36 37#include "macmodes.h"38 39/* Supported palette hacks */40enum {41 cmap_unknown,42 cmap_simple, /* ATI Mach64 */43 cmap_r128, /* ATI Rage128 */44 cmap_M3A, /* ATI Rage Mobility M3 Head A */45 cmap_M3B, /* ATI Rage Mobility M3 Head B */46 cmap_radeon, /* ATI Radeon */47 cmap_gxt2000, /* IBM GXT2000 */48 cmap_avivo, /* ATI R5xx */49 cmap_qemu, /* qemu vga */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 u32 pseudo_palette[16];58 resource_size_t base;59 resource_size_t size;60};61 62#ifdef CONFIG_PPC3263extern boot_infos_t *boot_infos;64#endif65 66/* Definitions used by the Avivo palette hack */67#define AVIVO_DC_LUT_RW_SELECT 0x648068#define AVIVO_DC_LUT_RW_MODE 0x648469#define AVIVO_DC_LUT_RW_INDEX 0x648870#define AVIVO_DC_LUT_SEQ_COLOR 0x648c71#define AVIVO_DC_LUT_PWL_DATA 0x649072#define AVIVO_DC_LUT_30_COLOR 0x649473#define AVIVO_DC_LUT_READ_PIPE_SELECT 0x649874#define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c75#define AVIVO_DC_LUT_AUTOFILL 0x64a076 77#define AVIVO_DC_LUTA_CONTROL 0x64c078#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c479#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c880#define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc81#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d082#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d483#define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d884 85#define AVIVO_DC_LUTB_CONTROL 0x6cc086#define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE 0x6cc487#define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN 0x6cc888#define AVIVO_DC_LUTB_BLACK_OFFSET_RED 0x6ccc89#define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE 0x6cd090#define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN 0x6cd491#define AVIVO_DC_LUTB_WHITE_OFFSET_RED 0x6cd892 93 /*94 * Set a single color register. The values supplied are already95 * rounded down to the hardware's capabilities (according to the96 * entries in the var structure). Return != 0 for invalid regno.97 */98 99static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,100 u_int transp, struct fb_info *info)101{102 struct offb_par *par = (struct offb_par *) info->par;103 104 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {105 u32 *pal = info->pseudo_palette;106 u32 cr = red >> (16 - info->var.red.length);107 u32 cg = green >> (16 - info->var.green.length);108 u32 cb = blue >> (16 - info->var.blue.length);109 u32 value;110 111 if (regno >= 16)112 return -EINVAL;113 114 value = (cr << info->var.red.offset) |115 (cg << info->var.green.offset) |116 (cb << info->var.blue.offset);117 if (info->var.transp.length > 0) {118 u32 mask = (1 << info->var.transp.length) - 1;119 mask <<= info->var.transp.offset;120 value |= mask;121 }122 pal[regno] = value;123 return 0;124 }125 126 if (regno > 255)127 return -EINVAL;128 129 red >>= 8;130 green >>= 8;131 blue >>= 8;132 133 if (!par->cmap_adr)134 return 0;135 136 switch (par->cmap_type) {137 case cmap_simple:138 writeb(regno, par->cmap_adr);139 writeb(red, par->cmap_data);140 writeb(green, par->cmap_data);141 writeb(blue, par->cmap_data);142 break;143 case cmap_M3A:144 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */145 out_le32(par->cmap_adr + 0x58,146 in_le32(par->cmap_adr + 0x58) & ~0x20);147 fallthrough;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 case cmap_avivo:172 /* Write to both LUTs for now */173 writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);174 writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);175 writel(((red) << 22) | ((green) << 12) | ((blue) << 2),176 par->cmap_adr + AVIVO_DC_LUT_30_COLOR);177 writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);178 writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);179 writel(((red) << 22) | ((green) << 12) | ((blue) << 2),180 par->cmap_adr + AVIVO_DC_LUT_30_COLOR);181 break;182 }183 184 return 0;185}186 187 /*188 * Blank the display.189 */190 191static int offb_blank(int blank, struct fb_info *info)192{193 struct offb_par *par = (struct offb_par *) info->par;194 int i, j;195 196 if (!par->cmap_adr)197 return 0;198 199 if (!par->blanked)200 if (!blank)201 return 0;202 203 par->blanked = blank;204 205 if (blank)206 for (i = 0; i < 256; i++) {207 switch (par->cmap_type) {208 case cmap_simple:209 writeb(i, par->cmap_adr);210 for (j = 0; j < 3; j++)211 writeb(0, par->cmap_data);212 break;213 case cmap_M3A:214 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */215 out_le32(par->cmap_adr + 0x58,216 in_le32(par->cmap_adr + 0x58) & ~0x20);217 fallthrough;218 case cmap_r128:219 /* Set palette index & data */220 out_8(par->cmap_adr + 0xb0, i);221 out_le32(par->cmap_adr + 0xb4, 0);222 break;223 case cmap_M3B:224 /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */225 out_le32(par->cmap_adr + 0x58,226 in_le32(par->cmap_adr + 0x58) | 0x20);227 /* Set palette index & data */228 out_8(par->cmap_adr + 0xb0, i);229 out_le32(par->cmap_adr + 0xb4, 0);230 break;231 case cmap_radeon:232 out_8(par->cmap_adr + 0xb0, i);233 out_le32(par->cmap_adr + 0xb4, 0);234 break;235 case cmap_gxt2000:236 out_le32(((unsigned __iomem *) par->cmap_adr) + i,237 0);238 break;239 case cmap_avivo:240 writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);241 writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);242 writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR);243 writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);244 writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);245 writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR);246 break;247 }248 } else249 fb_set_cmap(&info->cmap, info);250 return 0;251}252 253static int offb_set_par(struct fb_info *info)254{255 struct offb_par *par = (struct offb_par *) info->par;256 257 /* On avivo, initialize palette control */258 if (par->cmap_type == cmap_avivo) {259 writel(0, par->cmap_adr + AVIVO_DC_LUTA_CONTROL);260 writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_BLUE);261 writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_GREEN);262 writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_RED);263 writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_BLUE);264 writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_GREEN);265 writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_RED);266 writel(0, par->cmap_adr + AVIVO_DC_LUTB_CONTROL);267 writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_BLUE);268 writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_GREEN);269 writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_RED);270 writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_BLUE);271 writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_GREEN);272 writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_RED);273 writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);274 writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE);275 writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK);276 writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);277 writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE);278 writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK);279 }280 return 0;281}282 283static void offb_destroy(struct fb_info *info)284{285 struct offb_par *par = info->par;286 287 if (info->screen_base)288 iounmap(info->screen_base);289 release_mem_region(par->base, par->size);290 fb_dealloc_cmap(&info->cmap);291 framebuffer_release(info);292}293 294static const struct fb_ops offb_ops = {295 .owner = THIS_MODULE,296 FB_DEFAULT_IOMEM_OPS,297 .fb_destroy = offb_destroy,298 .fb_setcolreg = offb_setcolreg,299 .fb_set_par = offb_set_par,300 .fb_blank = offb_blank,301};302 303static void __iomem *offb_map_reg(struct device_node *np, int index,304 unsigned long offset, unsigned long size)305{306 const __be32 *addrp;307 u64 asize, taddr;308 unsigned int flags;309 310 addrp = of_get_pci_address(np, index, &asize, &flags);311 if (addrp == NULL)312 addrp = of_get_address(np, index, &asize, &flags);313 if (addrp == NULL)314 return NULL;315 if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)316 return NULL;317 if ((offset + size) > asize)318 return NULL;319 taddr = of_translate_address(np, addrp);320 if (taddr == OF_BAD_ADDR)321 return NULL;322 return ioremap(taddr + offset, size);323}324 325static void offb_init_palette_hacks(struct fb_info *info, struct device_node *dp,326 unsigned long address)327{328 struct offb_par *par = (struct offb_par *) info->par;329 330 if (of_node_name_prefix(dp, "ATY,Rage128")) {331 par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);332 if (par->cmap_adr)333 par->cmap_type = cmap_r128;334 } else if (of_node_name_prefix(dp, "ATY,RageM3pA") ||335 of_node_name_prefix(dp, "ATY,RageM3p12A")) {336 par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);337 if (par->cmap_adr)338 par->cmap_type = cmap_M3A;339 } else if (of_node_name_prefix(dp, "ATY,RageM3pB")) {340 par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);341 if (par->cmap_adr)342 par->cmap_type = cmap_M3B;343 } else if (of_node_name_prefix(dp, "ATY,Rage6")) {344 par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff);345 if (par->cmap_adr)346 par->cmap_type = cmap_radeon;347 } else if (of_node_name_prefix(dp, "ATY,")) {348 unsigned long base = address & 0xff000000UL;349 par->cmap_adr =350 ioremap(base + 0x7ff000, 0x1000) + 0xcc0;351 par->cmap_data = par->cmap_adr + 1;352 par->cmap_type = cmap_simple;353 } else if (dp && (of_device_is_compatible(dp, "pci1014,b7") ||354 of_device_is_compatible(dp, "pci1014,21c"))) {355 par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000);356 if (par->cmap_adr)357 par->cmap_type = cmap_gxt2000;358 } else if (of_node_name_prefix(dp, "vga,Display-")) {359 /* Look for AVIVO initialized by SLOF */360 struct device_node *pciparent __free(device_node) = of_get_parent(dp);361 const u32 *vid, *did;362 vid = of_get_property(pciparent, "vendor-id", NULL);363 did = of_get_property(pciparent, "device-id", NULL);364 /* This will match most R5xx */365 if (vid && did && *vid == 0x1002 &&366 ((*did >= 0x7100 && *did < 0x7800) ||367 (*did >= 0x9400))) {368 par->cmap_adr = offb_map_reg(pciparent, 2, 0, 0x10000);369 if (par->cmap_adr)370 par->cmap_type = cmap_avivo;371 }372 } else if (dp && of_device_is_compatible(dp, "qemu,std-vga")) {373#ifdef __BIG_ENDIAN374 const __be32 io_of_addr[3] = { 0x01000000, 0x0, 0x0 };375#else376 const __be32 io_of_addr[3] = { 0x00000001, 0x0, 0x0 };377#endif378 u64 io_addr = of_translate_address(dp, io_of_addr);379 if (io_addr != OF_BAD_ADDR) {380 par->cmap_adr = ioremap(io_addr + 0x3c8, 2);381 if (par->cmap_adr) {382 par->cmap_type = cmap_simple;383 par->cmap_data = par->cmap_adr + 1;384 }385 }386 }387 info->fix.visual = (par->cmap_type != cmap_unknown) ?388 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR;389}390 391static void offb_init_fb(struct platform_device *parent, const char *name,392 int width, int height, int depth,393 int pitch, unsigned long address,394 int foreign_endian, struct device_node *dp)395{396 unsigned long res_size = pitch * height;397 unsigned long res_start = address;398 struct fb_fix_screeninfo *fix;399 struct fb_var_screeninfo *var;400 struct fb_info *info;401 struct offb_par *par;402 403 if (!request_mem_region(res_start, res_size, "offb"))404 return;405 406 printk(KERN_INFO407 "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",408 width, height, name, address, depth, pitch);409 if (depth != 8 && depth != 15 && depth != 16 && depth != 32) {410 printk(KERN_ERR "%pOF: can't use depth = %d\n", dp, depth);411 release_mem_region(res_start, res_size);412 return;413 }414 415 info = framebuffer_alloc(sizeof(*par), &parent->dev);416 if (!info) {417 release_mem_region(res_start, res_size);418 return;419 }420 platform_set_drvdata(parent, info);421 par = info->par;422 fix = &info->fix;423 var = &info->var;424 425 if (name)426 snprintf(fix->id, sizeof(fix->id), "OFfb %s", name);427 else428 snprintf(fix->id, sizeof(fix->id), "OFfb %pOFn", dp);429 430 431 var->xres = var->xres_virtual = width;432 var->yres = var->yres_virtual = height;433 fix->line_length = pitch;434 435 fix->smem_start = address;436 fix->smem_len = pitch * height;437 fix->type = FB_TYPE_PACKED_PIXELS;438 fix->type_aux = 0;439 440 par->cmap_type = cmap_unknown;441 if (depth == 8)442 offb_init_palette_hacks(info, dp, address);443 else444 fix->visual = 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 par->base = address;507 par->size = fix->smem_len;508 509 info->fbops = &offb_ops;510 info->screen_base = ioremap(address, fix->smem_len);511 info->pseudo_palette = par->pseudo_palette;512 info->flags = foreign_endian;513 514 fb_alloc_cmap(&info->cmap, 256, 0);515 516 if (devm_aperture_acquire_for_platform_device(parent, par->base, par->size) < 0)517 goto out_err;518 if (register_framebuffer(info) < 0)519 goto out_err;520 521 fb_info(info, "Open Firmware frame buffer device on %pOF\n", dp);522 return;523 524out_err:525 fb_dealloc_cmap(&info->cmap);526 iounmap(info->screen_base);527 iounmap(par->cmap_adr);528 par->cmap_adr = NULL;529 framebuffer_release(info);530 release_mem_region(res_start, res_size);531}532 533 534static void offb_init_nodriver(struct platform_device *parent, struct device_node *dp,535 int no_real_node)536{537 unsigned int len;538 int i, width = 640, height = 480, depth = 8, pitch = 640;539 unsigned int flags, rsize, addr_prop = 0;540 unsigned long max_size = 0;541 u64 rstart, address = OF_BAD_ADDR;542 const __be32 *pp, *addrp, *up;543 u64 asize;544 int foreign_endian = 0;545 546#ifdef __BIG_ENDIAN547 if (of_property_read_bool(dp, "little-endian"))548 foreign_endian = FBINFO_FOREIGN_ENDIAN;549#else550 if (of_property_read_bool(dp, "big-endian"))551 foreign_endian = FBINFO_FOREIGN_ENDIAN;552#endif553 554 pp = of_get_property(dp, "linux,bootx-depth", &len);555 if (pp == NULL)556 pp = of_get_property(dp, "depth", &len);557 if (pp && len == sizeof(u32))558 depth = be32_to_cpup(pp);559 560 pp = of_get_property(dp, "linux,bootx-width", &len);561 if (pp == NULL)562 pp = of_get_property(dp, "width", &len);563 if (pp && len == sizeof(u32))564 width = be32_to_cpup(pp);565 566 pp = of_get_property(dp, "linux,bootx-height", &len);567 if (pp == NULL)568 pp = of_get_property(dp, "height", &len);569 if (pp && len == sizeof(u32))570 height = be32_to_cpup(pp);571 572 pp = of_get_property(dp, "linux,bootx-linebytes", &len);573 if (pp == NULL)574 pp = of_get_property(dp, "linebytes", &len);575 if (pp && len == sizeof(u32) && (*pp != 0xffffffffu))576 pitch = be32_to_cpup(pp);577 else578 pitch = width * ((depth + 7) / 8);579 580 rsize = (unsigned long)pitch * (unsigned long)height;581 582 /* Ok, now we try to figure out the address of the framebuffer.583 *584 * Unfortunately, Open Firmware doesn't provide a standard way to do585 * so. All we can do is a dodgy heuristic that happens to work in586 * practice. On most machines, the "address" property contains what587 * we need, though not on Matrox cards found in IBM machines. What I've588 * found that appears to give good results is to go through the PCI589 * ranges and pick one that is both big enough and if possible encloses590 * the "address" property. If none match, we pick the biggest591 */592 up = of_get_property(dp, "linux,bootx-addr", &len);593 if (up == NULL)594 up = of_get_property(dp, "address", &len);595 if (up && len == sizeof(u32))596 addr_prop = *up;597 598 /* Hack for when BootX is passing us */599 if (no_real_node)600 goto skip_addr;601 602 for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags))603 != NULL; i++) {604 int match_addrp = 0;605 606 if (!(flags & IORESOURCE_MEM))607 continue;608 if (asize < rsize)609 continue;610 rstart = of_translate_address(dp, addrp);611 if (rstart == OF_BAD_ADDR)612 continue;613 if (addr_prop && (rstart <= addr_prop) &&614 ((rstart + asize) >= (addr_prop + rsize)))615 match_addrp = 1;616 if (match_addrp) {617 address = addr_prop;618 break;619 }620 if (rsize > max_size) {621 max_size = rsize;622 address = OF_BAD_ADDR;623 }624 625 if (address == OF_BAD_ADDR)626 address = rstart;627 }628 skip_addr:629 if (address == OF_BAD_ADDR && addr_prop)630 address = (u64)addr_prop;631 if (address != OF_BAD_ADDR) {632#ifdef CONFIG_PCI633 const __be32 *vidp, *didp;634 u32 vid, did;635 struct pci_dev *pdev;636 637 vidp = of_get_property(dp, "vendor-id", NULL);638 didp = of_get_property(dp, "device-id", NULL);639 if (vidp && didp) {640 vid = be32_to_cpup(vidp);641 did = be32_to_cpup(didp);642 pdev = pci_get_device(vid, did, NULL);643 if (!pdev || pci_enable_device(pdev))644 return;645 }646#endif647 /* kludge for valkyrie */648 if (of_node_name_eq(dp, "valkyrie"))649 address += 0x1000;650 offb_init_fb(parent, no_real_node ? "bootx" : NULL,651 width, height, depth, pitch, address,652 foreign_endian, no_real_node ? NULL : dp);653 }654}655 656static void offb_remove(struct platform_device *pdev)657{658 struct fb_info *info = platform_get_drvdata(pdev);659 660 if (info)661 unregister_framebuffer(info);662}663 664static int offb_probe_bootx_noscreen(struct platform_device *pdev)665{666 offb_init_nodriver(pdev, of_chosen, 1);667 668 return 0;669}670 671static struct platform_driver offb_driver_bootx_noscreen = {672 .driver = {673 .name = "bootx-noscreen",674 },675 .probe = offb_probe_bootx_noscreen,676 .remove = offb_remove,677};678 679static int offb_probe_display(struct platform_device *pdev)680{681 offb_init_nodriver(pdev, pdev->dev.of_node, 0);682 683 return 0;684}685 686static const struct of_device_id offb_of_match_display[] = {687 { .compatible = "display", },688 { },689};690MODULE_DEVICE_TABLE(of, offb_of_match_display);691 692static struct platform_driver offb_driver_display = {693 .driver = {694 .name = "of-display",695 .of_match_table = offb_of_match_display,696 },697 .probe = offb_probe_display,698 .remove = offb_remove,699};700 701static int __init offb_init(void)702{703 if (fb_get_options("offb", NULL))704 return -ENODEV;705 706 platform_driver_register(&offb_driver_bootx_noscreen);707 platform_driver_register(&offb_driver_display);708 709 return 0;710}711module_init(offb_init);712 713static void __exit offb_exit(void)714{715 platform_driver_unregister(&offb_driver_display);716 platform_driver_unregister(&offb_driver_bootx_noscreen);717}718module_exit(offb_exit);719 720MODULE_DESCRIPTION("Open Firmware frame buffer device driver");721MODULE_LICENSE("GPL");722