Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *  linux/drivers/video/offb.c -- Open Firmware based frame buffer device
0003  *
0004  *  Copyright (C) 1997 Geert Uytterhoeven
0005  *
0006  *  This driver is partly based on the PowerMac console driver:
0007  *
0008  *  Copyright (C) 1996 Paul Mackerras
0009  *
0010  *  This file is subject to the terms and conditions of the GNU General Public
0011  *  License. See the file COPYING in the main directory of this archive for
0012  *  more details.
0013  */
0014 
0015 #include <linux/module.h>
0016 #include <linux/kernel.h>
0017 #include <linux/errno.h>
0018 #include <linux/string.h>
0019 #include <linux/mm.h>
0020 #include <linux/vmalloc.h>
0021 #include <linux/delay.h>
0022 #include <linux/of.h>
0023 #include <linux/of_address.h>
0024 #include <linux/interrupt.h>
0025 #include <linux/fb.h>
0026 #include <linux/init.h>
0027 #include <linux/ioport.h>
0028 #include <linux/pci.h>
0029 #include <linux/platform_device.h>
0030 #include <asm/io.h>
0031 
0032 #ifdef CONFIG_PPC32
0033 #include <asm/bootx.h>
0034 #endif
0035 
0036 #include "macmodes.h"
0037 
0038 /* Supported palette hacks */
0039 enum {
0040     cmap_unknown,
0041     cmap_simple,        /* ATI Mach64 */
0042     cmap_r128,      /* ATI Rage128 */
0043     cmap_M3A,       /* ATI Rage Mobility M3 Head A */
0044     cmap_M3B,       /* ATI Rage Mobility M3 Head B */
0045     cmap_radeon,        /* ATI Radeon */
0046     cmap_gxt2000,       /* IBM GXT2000 */
0047     cmap_avivo,     /* ATI R5xx */
0048     cmap_qemu,      /* qemu vga */
0049 };
0050 
0051 struct offb_par {
0052     volatile void __iomem *cmap_adr;
0053     volatile void __iomem *cmap_data;
0054     int cmap_type;
0055     int blanked;
0056 };
0057 
0058 struct offb_par default_par;
0059 
0060 #ifdef CONFIG_PPC32
0061 extern boot_infos_t *boot_infos;
0062 #endif
0063 
0064 /* Definitions used by the Avivo palette hack */
0065 #define AVIVO_DC_LUT_RW_SELECT                  0x6480
0066 #define AVIVO_DC_LUT_RW_MODE                    0x6484
0067 #define AVIVO_DC_LUT_RW_INDEX                   0x6488
0068 #define AVIVO_DC_LUT_SEQ_COLOR                  0x648c
0069 #define AVIVO_DC_LUT_PWL_DATA                   0x6490
0070 #define AVIVO_DC_LUT_30_COLOR                   0x6494
0071 #define AVIVO_DC_LUT_READ_PIPE_SELECT           0x6498
0072 #define AVIVO_DC_LUT_WRITE_EN_MASK              0x649c
0073 #define AVIVO_DC_LUT_AUTOFILL                   0x64a0
0074 
0075 #define AVIVO_DC_LUTA_CONTROL                   0x64c0
0076 #define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE         0x64c4
0077 #define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN        0x64c8
0078 #define AVIVO_DC_LUTA_BLACK_OFFSET_RED          0x64cc
0079 #define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE         0x64d0
0080 #define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN        0x64d4
0081 #define AVIVO_DC_LUTA_WHITE_OFFSET_RED          0x64d8
0082 
0083 #define AVIVO_DC_LUTB_CONTROL                   0x6cc0
0084 #define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE         0x6cc4
0085 #define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN        0x6cc8
0086 #define AVIVO_DC_LUTB_BLACK_OFFSET_RED          0x6ccc
0087 #define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE         0x6cd0
0088 #define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN        0x6cd4
0089 #define AVIVO_DC_LUTB_WHITE_OFFSET_RED          0x6cd8
0090 
0091     /*
0092      *  Set a single color register. The values supplied are already
0093      *  rounded down to the hardware's capabilities (according to the
0094      *  entries in the var structure). Return != 0 for invalid regno.
0095      */
0096 
0097 static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
0098               u_int transp, struct fb_info *info)
0099 {
0100     struct offb_par *par = (struct offb_par *) info->par;
0101 
0102     if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
0103         u32 *pal = info->pseudo_palette;
0104         u32 cr = red >> (16 - info->var.red.length);
0105         u32 cg = green >> (16 - info->var.green.length);
0106         u32 cb = blue >> (16 - info->var.blue.length);
0107         u32 value;
0108 
0109         if (regno >= 16)
0110             return -EINVAL;
0111 
0112         value = (cr << info->var.red.offset) |
0113             (cg << info->var.green.offset) |
0114             (cb << info->var.blue.offset);
0115         if (info->var.transp.length > 0) {
0116             u32 mask = (1 << info->var.transp.length) - 1;
0117             mask <<= info->var.transp.offset;
0118             value |= mask;
0119         }
0120         pal[regno] = value;
0121         return 0;
0122     }
0123 
0124     if (regno > 255)
0125         return -EINVAL;
0126 
0127     red >>= 8;
0128     green >>= 8;
0129     blue >>= 8;
0130 
0131     if (!par->cmap_adr)
0132         return 0;
0133 
0134     switch (par->cmap_type) {
0135     case cmap_simple:
0136         writeb(regno, par->cmap_adr);
0137         writeb(red, par->cmap_data);
0138         writeb(green, par->cmap_data);
0139         writeb(blue, par->cmap_data);
0140         break;
0141     case cmap_M3A:
0142         /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
0143         out_le32(par->cmap_adr + 0x58,
0144              in_le32(par->cmap_adr + 0x58) & ~0x20);
0145         fallthrough;
0146     case cmap_r128:
0147         /* Set palette index & data */
0148         out_8(par->cmap_adr + 0xb0, regno);
0149         out_le32(par->cmap_adr + 0xb4,
0150              (red << 16 | green << 8 | blue));
0151         break;
0152     case cmap_M3B:
0153         /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
0154         out_le32(par->cmap_adr + 0x58,
0155              in_le32(par->cmap_adr + 0x58) | 0x20);
0156         /* Set palette index & data */
0157         out_8(par->cmap_adr + 0xb0, regno);
0158         out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
0159         break;
0160     case cmap_radeon:
0161         /* Set palette index & data (could be smarter) */
0162         out_8(par->cmap_adr + 0xb0, regno);
0163         out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
0164         break;
0165     case cmap_gxt2000:
0166         out_le32(((unsigned __iomem *) par->cmap_adr) + regno,
0167              (red << 16 | green << 8 | blue));
0168         break;
0169     case cmap_avivo:
0170         /* Write to both LUTs for now */
0171         writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
0172         writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
0173         writel(((red) << 22) | ((green) << 12) | ((blue) << 2),
0174                par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
0175         writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
0176         writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
0177         writel(((red) << 22) | ((green) << 12) | ((blue) << 2),
0178                par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
0179         break;
0180     }
0181 
0182     return 0;
0183 }
0184 
0185     /*
0186      *  Blank the display.
0187      */
0188 
0189 static int offb_blank(int blank, struct fb_info *info)
0190 {
0191     struct offb_par *par = (struct offb_par *) info->par;
0192     int i, j;
0193 
0194     if (!par->cmap_adr)
0195         return 0;
0196 
0197     if (!par->blanked)
0198         if (!blank)
0199             return 0;
0200 
0201     par->blanked = blank;
0202 
0203     if (blank)
0204         for (i = 0; i < 256; i++) {
0205             switch (par->cmap_type) {
0206             case cmap_simple:
0207                 writeb(i, par->cmap_adr);
0208                 for (j = 0; j < 3; j++)
0209                     writeb(0, par->cmap_data);
0210                 break;
0211             case cmap_M3A:
0212                 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
0213                 out_le32(par->cmap_adr + 0x58,
0214                      in_le32(par->cmap_adr + 0x58) & ~0x20);
0215                 fallthrough;
0216             case cmap_r128:
0217                 /* Set palette index & data */
0218                 out_8(par->cmap_adr + 0xb0, i);
0219                 out_le32(par->cmap_adr + 0xb4, 0);
0220                 break;
0221             case cmap_M3B:
0222                 /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
0223                 out_le32(par->cmap_adr + 0x58,
0224                      in_le32(par->cmap_adr + 0x58) | 0x20);
0225                 /* Set palette index & data */
0226                 out_8(par->cmap_adr + 0xb0, i);
0227                 out_le32(par->cmap_adr + 0xb4, 0);
0228                 break;
0229             case cmap_radeon:
0230                 out_8(par->cmap_adr + 0xb0, i);
0231                 out_le32(par->cmap_adr + 0xb4, 0);
0232                 break;
0233             case cmap_gxt2000:
0234                 out_le32(((unsigned __iomem *) par->cmap_adr) + i,
0235                      0);
0236                 break;
0237             case cmap_avivo:
0238                 writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
0239                 writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
0240                 writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
0241                 writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
0242                 writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
0243                 writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
0244                 break;
0245             }
0246     } else
0247         fb_set_cmap(&info->cmap, info);
0248     return 0;
0249 }
0250 
0251 static int offb_set_par(struct fb_info *info)
0252 {
0253     struct offb_par *par = (struct offb_par *) info->par;
0254 
0255     /* On avivo, initialize palette control */
0256     if (par->cmap_type == cmap_avivo) {
0257         writel(0, par->cmap_adr + AVIVO_DC_LUTA_CONTROL);
0258         writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_BLUE);
0259         writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_GREEN);
0260         writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_RED);
0261         writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_BLUE);
0262         writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_GREEN);
0263         writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_RED);
0264         writel(0, par->cmap_adr + AVIVO_DC_LUTB_CONTROL);
0265         writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_BLUE);
0266         writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_GREEN);
0267         writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_RED);
0268         writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_BLUE);
0269         writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_GREEN);
0270         writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_RED);
0271         writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
0272         writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE);
0273         writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK);
0274         writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
0275         writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE);
0276         writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK);
0277     }
0278     return 0;
0279 }
0280 
0281 static void offb_destroy(struct fb_info *info)
0282 {
0283     if (info->screen_base)
0284         iounmap(info->screen_base);
0285     release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size);
0286     fb_dealloc_cmap(&info->cmap);
0287     framebuffer_release(info);
0288 }
0289 
0290 static const struct fb_ops offb_ops = {
0291     .owner      = THIS_MODULE,
0292     .fb_destroy = offb_destroy,
0293     .fb_setcolreg   = offb_setcolreg,
0294     .fb_set_par = offb_set_par,
0295     .fb_blank   = offb_blank,
0296     .fb_fillrect    = cfb_fillrect,
0297     .fb_copyarea    = cfb_copyarea,
0298     .fb_imageblit   = cfb_imageblit,
0299 };
0300 
0301 static void __iomem *offb_map_reg(struct device_node *np, int index,
0302                   unsigned long offset, unsigned long size)
0303 {
0304     const __be32 *addrp;
0305     u64 asize, taddr;
0306     unsigned int flags;
0307 
0308     addrp = of_get_pci_address(np, index, &asize, &flags);
0309     if (addrp == NULL)
0310         addrp = of_get_address(np, index, &asize, &flags);
0311     if (addrp == NULL)
0312         return NULL;
0313     if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
0314         return NULL;
0315     if ((offset + size) > asize)
0316         return NULL;
0317     taddr = of_translate_address(np, addrp);
0318     if (taddr == OF_BAD_ADDR)
0319         return NULL;
0320     return ioremap(taddr + offset, size);
0321 }
0322 
0323 static void offb_init_palette_hacks(struct fb_info *info, struct device_node *dp,
0324                     unsigned long address)
0325 {
0326     struct offb_par *par = (struct offb_par *) info->par;
0327 
0328     if (of_node_name_prefix(dp, "ATY,Rage128")) {
0329         par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
0330         if (par->cmap_adr)
0331             par->cmap_type = cmap_r128;
0332     } else if (of_node_name_prefix(dp, "ATY,RageM3pA") ||
0333            of_node_name_prefix(dp, "ATY,RageM3p12A")) {
0334         par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
0335         if (par->cmap_adr)
0336             par->cmap_type = cmap_M3A;
0337     } else if (of_node_name_prefix(dp, "ATY,RageM3pB")) {
0338         par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
0339         if (par->cmap_adr)
0340             par->cmap_type = cmap_M3B;
0341     } else if (of_node_name_prefix(dp, "ATY,Rage6")) {
0342         par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff);
0343         if (par->cmap_adr)
0344             par->cmap_type = cmap_radeon;
0345     } else if (of_node_name_prefix(dp, "ATY,")) {
0346         unsigned long base = address & 0xff000000UL;
0347         par->cmap_adr =
0348             ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
0349         par->cmap_data = par->cmap_adr + 1;
0350         par->cmap_type = cmap_simple;
0351     } else if (dp && (of_device_is_compatible(dp, "pci1014,b7") ||
0352               of_device_is_compatible(dp, "pci1014,21c"))) {
0353         par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000);
0354         if (par->cmap_adr)
0355             par->cmap_type = cmap_gxt2000;
0356     } else if (of_node_name_prefix(dp, "vga,Display-")) {
0357         /* Look for AVIVO initialized by SLOF */
0358         struct device_node *pciparent = of_get_parent(dp);
0359         const u32 *vid, *did;
0360         vid = of_get_property(pciparent, "vendor-id", NULL);
0361         did = of_get_property(pciparent, "device-id", NULL);
0362         /* This will match most R5xx */
0363         if (vid && did && *vid == 0x1002 &&
0364             ((*did >= 0x7100 && *did < 0x7800) ||
0365              (*did >= 0x9400))) {
0366             par->cmap_adr = offb_map_reg(pciparent, 2, 0, 0x10000);
0367             if (par->cmap_adr)
0368                 par->cmap_type = cmap_avivo;
0369         }
0370         of_node_put(pciparent);
0371     } else if (dp && of_device_is_compatible(dp, "qemu,std-vga")) {
0372 #ifdef __BIG_ENDIAN
0373         const __be32 io_of_addr[3] = { 0x01000000, 0x0, 0x0 };
0374 #else
0375         const __be32 io_of_addr[3] = { 0x00000001, 0x0, 0x0 };
0376 #endif
0377         u64 io_addr = of_translate_address(dp, io_of_addr);
0378         if (io_addr != OF_BAD_ADDR) {
0379             par->cmap_adr = ioremap(io_addr + 0x3c8, 2);
0380             if (par->cmap_adr) {
0381                 par->cmap_type = cmap_simple;
0382                 par->cmap_data = par->cmap_adr + 1;
0383             }
0384         }
0385     }
0386     info->fix.visual = (par->cmap_type != cmap_unknown) ?
0387         FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR;
0388 }
0389 
0390 static void offb_init_fb(struct platform_device *parent, const char *name,
0391              int width, int height, int depth,
0392              int pitch, unsigned long address,
0393              int foreign_endian, struct device_node *dp)
0394 {
0395     unsigned long res_size = pitch * height;
0396     struct offb_par *par = &default_par;
0397     unsigned long res_start = address;
0398     struct fb_fix_screeninfo *fix;
0399     struct fb_var_screeninfo *var;
0400     struct fb_info *info;
0401 
0402     if (!request_mem_region(res_start, res_size, "offb"))
0403         return;
0404 
0405     printk(KERN_INFO
0406            "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",
0407            width, height, name, address, depth, pitch);
0408     if (depth != 8 && depth != 15 && depth != 16 && depth != 32) {
0409         printk(KERN_ERR "%pOF: can't use depth = %d\n", dp, depth);
0410         release_mem_region(res_start, res_size);
0411         return;
0412     }
0413 
0414     info = framebuffer_alloc(sizeof(u32) * 16, &parent->dev);
0415 
0416     if (!info) {
0417         release_mem_region(res_start, res_size);
0418         return;
0419     }
0420     platform_set_drvdata(parent, info);
0421 
0422     fix = &info->fix;
0423     var = &info->var;
0424     info->par = par;
0425 
0426     if (name) {
0427         strcpy(fix->id, "OFfb ");
0428         strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb "));
0429         fix->id[sizeof(fix->id) - 1] = '\0';
0430     } else
0431         snprintf(fix->id, sizeof(fix->id), "OFfb %pOFn", dp);
0432 
0433 
0434     var->xres = var->xres_virtual = width;
0435     var->yres = var->yres_virtual = height;
0436     fix->line_length = pitch;
0437 
0438     fix->smem_start = address;
0439     fix->smem_len = pitch * height;
0440     fix->type = FB_TYPE_PACKED_PIXELS;
0441     fix->type_aux = 0;
0442 
0443     par->cmap_type = cmap_unknown;
0444     if (depth == 8)
0445         offb_init_palette_hacks(info, dp, address);
0446     else
0447         fix->visual = FB_VISUAL_TRUECOLOR;
0448 
0449     var->xoffset = var->yoffset = 0;
0450     switch (depth) {
0451     case 8:
0452         var->bits_per_pixel = 8;
0453         var->red.offset = 0;
0454         var->red.length = 8;
0455         var->green.offset = 0;
0456         var->green.length = 8;
0457         var->blue.offset = 0;
0458         var->blue.length = 8;
0459         var->transp.offset = 0;
0460         var->transp.length = 0;
0461         break;
0462     case 15:        /* RGB 555 */
0463         var->bits_per_pixel = 16;
0464         var->red.offset = 10;
0465         var->red.length = 5;
0466         var->green.offset = 5;
0467         var->green.length = 5;
0468         var->blue.offset = 0;
0469         var->blue.length = 5;
0470         var->transp.offset = 0;
0471         var->transp.length = 0;
0472         break;
0473     case 16:        /* RGB 565 */
0474         var->bits_per_pixel = 16;
0475         var->red.offset = 11;
0476         var->red.length = 5;
0477         var->green.offset = 5;
0478         var->green.length = 6;
0479         var->blue.offset = 0;
0480         var->blue.length = 5;
0481         var->transp.offset = 0;
0482         var->transp.length = 0;
0483         break;
0484     case 32:        /* RGB 888 */
0485         var->bits_per_pixel = 32;
0486         var->red.offset = 16;
0487         var->red.length = 8;
0488         var->green.offset = 8;
0489         var->green.length = 8;
0490         var->blue.offset = 0;
0491         var->blue.length = 8;
0492         var->transp.offset = 24;
0493         var->transp.length = 8;
0494         break;
0495     }
0496     var->red.msb_right = var->green.msb_right = var->blue.msb_right =
0497         var->transp.msb_right = 0;
0498     var->grayscale = 0;
0499     var->nonstd = 0;
0500     var->activate = 0;
0501     var->height = var->width = -1;
0502     var->pixclock = 10000;
0503     var->left_margin = var->right_margin = 16;
0504     var->upper_margin = var->lower_margin = 16;
0505     var->hsync_len = var->vsync_len = 8;
0506     var->sync = 0;
0507     var->vmode = FB_VMODE_NONINTERLACED;
0508 
0509     /* set offb aperture size for generic probing */
0510     info->apertures = alloc_apertures(1);
0511     if (!info->apertures)
0512         goto out_aper;
0513     info->apertures->ranges[0].base = address;
0514     info->apertures->ranges[0].size = fix->smem_len;
0515 
0516     info->fbops = &offb_ops;
0517     info->screen_base = ioremap(address, fix->smem_len);
0518     info->pseudo_palette = (void *) (info + 1);
0519     info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE | foreign_endian;
0520 
0521     fb_alloc_cmap(&info->cmap, 256, 0);
0522 
0523     if (register_framebuffer(info) < 0)
0524         goto out_err;
0525 
0526     fb_info(info, "Open Firmware frame buffer device on %pOF\n", dp);
0527     return;
0528 
0529 out_err:
0530     fb_dealloc_cmap(&info->cmap);
0531     iounmap(info->screen_base);
0532 out_aper:
0533     iounmap(par->cmap_adr);
0534     par->cmap_adr = NULL;
0535     framebuffer_release(info);
0536     release_mem_region(res_start, res_size);
0537 }
0538 
0539 
0540 static void offb_init_nodriver(struct platform_device *parent, struct device_node *dp,
0541                    int no_real_node)
0542 {
0543     unsigned int len;
0544     int i, width = 640, height = 480, depth = 8, pitch = 640;
0545     unsigned int flags, rsize, addr_prop = 0;
0546     unsigned long max_size = 0;
0547     u64 rstart, address = OF_BAD_ADDR;
0548     const __be32 *pp, *addrp, *up;
0549     u64 asize;
0550     int foreign_endian = 0;
0551 
0552 #ifdef __BIG_ENDIAN
0553     if (of_get_property(dp, "little-endian", NULL))
0554         foreign_endian = FBINFO_FOREIGN_ENDIAN;
0555 #else
0556     if (of_get_property(dp, "big-endian", NULL))
0557         foreign_endian = FBINFO_FOREIGN_ENDIAN;
0558 #endif
0559 
0560     pp = of_get_property(dp, "linux,bootx-depth", &len);
0561     if (pp == NULL)
0562         pp = of_get_property(dp, "depth", &len);
0563     if (pp && len == sizeof(u32))
0564         depth = be32_to_cpup(pp);
0565 
0566     pp = of_get_property(dp, "linux,bootx-width", &len);
0567     if (pp == NULL)
0568         pp = of_get_property(dp, "width", &len);
0569     if (pp && len == sizeof(u32))
0570         width = be32_to_cpup(pp);
0571 
0572     pp = of_get_property(dp, "linux,bootx-height", &len);
0573     if (pp == NULL)
0574         pp = of_get_property(dp, "height", &len);
0575     if (pp && len == sizeof(u32))
0576         height = be32_to_cpup(pp);
0577 
0578     pp = of_get_property(dp, "linux,bootx-linebytes", &len);
0579     if (pp == NULL)
0580         pp = of_get_property(dp, "linebytes", &len);
0581     if (pp && len == sizeof(u32) && (*pp != 0xffffffffu))
0582         pitch = be32_to_cpup(pp);
0583     else
0584         pitch = width * ((depth + 7) / 8);
0585 
0586     rsize = (unsigned long)pitch * (unsigned long)height;
0587 
0588     /* Ok, now we try to figure out the address of the framebuffer.
0589      *
0590      * Unfortunately, Open Firmware doesn't provide a standard way to do
0591      * so. All we can do is a dodgy heuristic that happens to work in
0592      * practice. On most machines, the "address" property contains what
0593      * we need, though not on Matrox cards found in IBM machines. What I've
0594      * found that appears to give good results is to go through the PCI
0595      * ranges and pick one that is both big enough and if possible encloses
0596      * the "address" property. If none match, we pick the biggest
0597      */
0598     up = of_get_property(dp, "linux,bootx-addr", &len);
0599     if (up == NULL)
0600         up = of_get_property(dp, "address", &len);
0601     if (up && len == sizeof(u32))
0602         addr_prop = *up;
0603 
0604     /* Hack for when BootX is passing us */
0605     if (no_real_node)
0606         goto skip_addr;
0607 
0608     for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags))
0609              != NULL; i++) {
0610         int match_addrp = 0;
0611 
0612         if (!(flags & IORESOURCE_MEM))
0613             continue;
0614         if (asize < rsize)
0615             continue;
0616         rstart = of_translate_address(dp, addrp);
0617         if (rstart == OF_BAD_ADDR)
0618             continue;
0619         if (addr_prop && (rstart <= addr_prop) &&
0620             ((rstart + asize) >= (addr_prop + rsize)))
0621             match_addrp = 1;
0622         if (match_addrp) {
0623             address = addr_prop;
0624             break;
0625         }
0626         if (rsize > max_size) {
0627             max_size = rsize;
0628             address = OF_BAD_ADDR;
0629         }
0630 
0631         if (address == OF_BAD_ADDR)
0632             address = rstart;
0633     }
0634  skip_addr:
0635     if (address == OF_BAD_ADDR && addr_prop)
0636         address = (u64)addr_prop;
0637     if (address != OF_BAD_ADDR) {
0638 #ifdef CONFIG_PCI
0639         const __be32 *vidp, *didp;
0640         u32 vid, did;
0641         struct pci_dev *pdev;
0642 
0643         vidp = of_get_property(dp, "vendor-id", NULL);
0644         didp = of_get_property(dp, "device-id", NULL);
0645         if (vidp && didp) {
0646             vid = be32_to_cpup(vidp);
0647             did = be32_to_cpup(didp);
0648             pdev = pci_get_device(vid, did, NULL);
0649             if (!pdev || pci_enable_device(pdev))
0650                 return;
0651         }
0652 #endif
0653         /* kludge for valkyrie */
0654         if (of_node_name_eq(dp, "valkyrie"))
0655             address += 0x1000;
0656         offb_init_fb(parent, no_real_node ? "bootx" : NULL,
0657                  width, height, depth, pitch, address,
0658                  foreign_endian, no_real_node ? NULL : dp);
0659     }
0660 }
0661 
0662 static int offb_remove(struct platform_device *pdev)
0663 {
0664     struct fb_info *info = platform_get_drvdata(pdev);
0665 
0666     if (info)
0667         unregister_framebuffer(info);
0668 
0669     return 0;
0670 }
0671 
0672 static int offb_probe_bootx_noscreen(struct platform_device *pdev)
0673 {
0674     offb_init_nodriver(pdev, of_chosen, 1);
0675 
0676     return 0;
0677 }
0678 
0679 static struct platform_driver offb_driver_bootx_noscreen = {
0680     .driver = {
0681         .name = "bootx-noscreen",
0682     },
0683     .probe = offb_probe_bootx_noscreen,
0684     .remove = offb_remove,
0685 };
0686 
0687 static int offb_probe_display(struct platform_device *pdev)
0688 {
0689     offb_init_nodriver(pdev, pdev->dev.of_node, 0);
0690 
0691     return 0;
0692 }
0693 
0694 static const struct of_device_id offb_of_match_display[] = {
0695     { .compatible = "display", },
0696     { },
0697 };
0698 MODULE_DEVICE_TABLE(of, offb_of_match_display);
0699 
0700 static struct platform_driver offb_driver_display = {
0701     .driver = {
0702         .name = "of-display",
0703         .of_match_table = offb_of_match_display,
0704     },
0705     .probe = offb_probe_display,
0706     .remove = offb_remove,
0707 };
0708 
0709 static int __init offb_init(void)
0710 {
0711     if (fb_get_options("offb", NULL))
0712         return -ENODEV;
0713 
0714     platform_driver_register(&offb_driver_bootx_noscreen);
0715     platform_driver_register(&offb_driver_display);
0716 
0717     return 0;
0718 }
0719 module_init(offb_init);
0720 
0721 static void __exit offb_exit(void)
0722 {
0723     platform_driver_unregister(&offb_driver_display);
0724     platform_driver_unregister(&offb_driver_bootx_noscreen);
0725 }
0726 module_exit(offb_exit);
0727 
0728 MODULE_LICENSE("GPL");