summaryrefslogtreecommitdiffstats
path: root/drivers/video/fbdev/offb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/fbdev/offb.c')
-rw-r--r--drivers/video/fbdev/offb.c728
1 files changed, 728 insertions, 0 deletions
diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c
new file mode 100644
index 000000000..91001990e
--- /dev/null
+++ b/drivers/video/fbdev/offb.c
@@ -0,0 +1,728 @@
+/*
+ * linux/drivers/video/offb.c -- Open Firmware based frame buffer device
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * This driver is partly based on the PowerMac console driver:
+ *
+ * Copyright (C) 1996 Paul Mackerras
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_PPC32
+#include <asm/bootx.h>
+#endif
+
+#include "macmodes.h"
+
+/* Supported palette hacks */
+enum {
+ cmap_unknown,
+ cmap_simple, /* ATI Mach64 */
+ cmap_r128, /* ATI Rage128 */
+ cmap_M3A, /* ATI Rage Mobility M3 Head A */
+ cmap_M3B, /* ATI Rage Mobility M3 Head B */
+ cmap_radeon, /* ATI Radeon */
+ cmap_gxt2000, /* IBM GXT2000 */
+ cmap_avivo, /* ATI R5xx */
+ cmap_qemu, /* qemu vga */
+};
+
+struct offb_par {
+ volatile void __iomem *cmap_adr;
+ volatile void __iomem *cmap_data;
+ int cmap_type;
+ int blanked;
+};
+
+struct offb_par default_par;
+
+#ifdef CONFIG_PPC32
+extern boot_infos_t *boot_infos;
+#endif
+
+/* Definitions used by the Avivo palette hack */
+#define AVIVO_DC_LUT_RW_SELECT 0x6480
+#define AVIVO_DC_LUT_RW_MODE 0x6484
+#define AVIVO_DC_LUT_RW_INDEX 0x6488
+#define AVIVO_DC_LUT_SEQ_COLOR 0x648c
+#define AVIVO_DC_LUT_PWL_DATA 0x6490
+#define AVIVO_DC_LUT_30_COLOR 0x6494
+#define AVIVO_DC_LUT_READ_PIPE_SELECT 0x6498
+#define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c
+#define AVIVO_DC_LUT_AUTOFILL 0x64a0
+
+#define AVIVO_DC_LUTA_CONTROL 0x64c0
+#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c4
+#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c8
+#define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc
+#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d0
+#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d4
+#define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d8
+
+#define AVIVO_DC_LUTB_CONTROL 0x6cc0
+#define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE 0x6cc4
+#define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN 0x6cc8
+#define AVIVO_DC_LUTB_BLACK_OFFSET_RED 0x6ccc
+#define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE 0x6cd0
+#define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN 0x6cd4
+#define AVIVO_DC_LUTB_WHITE_OFFSET_RED 0x6cd8
+
+ /*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+{
+ struct offb_par *par = (struct offb_par *) info->par;
+
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+ u32 *pal = info->pseudo_palette;
+ u32 cr = red >> (16 - info->var.red.length);
+ u32 cg = green >> (16 - info->var.green.length);
+ u32 cb = blue >> (16 - info->var.blue.length);
+ u32 value;
+
+ if (regno >= 16)
+ return -EINVAL;
+
+ value = (cr << info->var.red.offset) |
+ (cg << info->var.green.offset) |
+ (cb << info->var.blue.offset);
+ if (info->var.transp.length > 0) {
+ u32 mask = (1 << info->var.transp.length) - 1;
+ mask <<= info->var.transp.offset;
+ value |= mask;
+ }
+ pal[regno] = value;
+ return 0;
+ }
+
+ if (regno > 255)
+ return -EINVAL;
+
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+
+ if (!par->cmap_adr)
+ return 0;
+
+ switch (par->cmap_type) {
+ case cmap_simple:
+ writeb(regno, par->cmap_adr);
+ writeb(red, par->cmap_data);
+ writeb(green, par->cmap_data);
+ writeb(blue, par->cmap_data);
+ break;
+ case cmap_M3A:
+ /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
+ out_le32(par->cmap_adr + 0x58,
+ in_le32(par->cmap_adr + 0x58) & ~0x20);
+ fallthrough;
+ case cmap_r128:
+ /* Set palette index & data */
+ out_8(par->cmap_adr + 0xb0, regno);
+ out_le32(par->cmap_adr + 0xb4,
+ (red << 16 | green << 8 | blue));
+ break;
+ case cmap_M3B:
+ /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
+ out_le32(par->cmap_adr + 0x58,
+ in_le32(par->cmap_adr + 0x58) | 0x20);
+ /* Set palette index & data */
+ out_8(par->cmap_adr + 0xb0, regno);
+ out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
+ break;
+ case cmap_radeon:
+ /* Set palette index & data (could be smarter) */
+ out_8(par->cmap_adr + 0xb0, regno);
+ out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
+ break;
+ case cmap_gxt2000:
+ out_le32(((unsigned __iomem *) par->cmap_adr) + regno,
+ (red << 16 | green << 8 | blue));
+ break;
+ case cmap_avivo:
+ /* Write to both LUTs for now */
+ writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
+ writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
+ writel(((red) << 22) | ((green) << 12) | ((blue) << 2),
+ par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
+ writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
+ writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
+ writel(((red) << 22) | ((green) << 12) | ((blue) << 2),
+ par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
+ break;
+ }
+
+ return 0;
+}
+
+ /*
+ * Blank the display.
+ */
+
+static int offb_blank(int blank, struct fb_info *info)
+{
+ struct offb_par *par = (struct offb_par *) info->par;
+ int i, j;
+
+ if (!par->cmap_adr)
+ return 0;
+
+ if (!par->blanked)
+ if (!blank)
+ return 0;
+
+ par->blanked = blank;
+
+ if (blank)
+ for (i = 0; i < 256; i++) {
+ switch (par->cmap_type) {
+ case cmap_simple:
+ writeb(i, par->cmap_adr);
+ for (j = 0; j < 3; j++)
+ writeb(0, par->cmap_data);
+ break;
+ case cmap_M3A:
+ /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
+ out_le32(par->cmap_adr + 0x58,
+ in_le32(par->cmap_adr + 0x58) & ~0x20);
+ fallthrough;
+ case cmap_r128:
+ /* Set palette index & data */
+ out_8(par->cmap_adr + 0xb0, i);
+ out_le32(par->cmap_adr + 0xb4, 0);
+ break;
+ case cmap_M3B:
+ /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
+ out_le32(par->cmap_adr + 0x58,
+ in_le32(par->cmap_adr + 0x58) | 0x20);
+ /* Set palette index & data */
+ out_8(par->cmap_adr + 0xb0, i);
+ out_le32(par->cmap_adr + 0xb4, 0);
+ break;
+ case cmap_radeon:
+ out_8(par->cmap_adr + 0xb0, i);
+ out_le32(par->cmap_adr + 0xb4, 0);
+ break;
+ case cmap_gxt2000:
+ out_le32(((unsigned __iomem *) par->cmap_adr) + i,
+ 0);
+ break;
+ case cmap_avivo:
+ writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
+ writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
+ writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
+ writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
+ writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
+ writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
+ break;
+ }
+ } else
+ fb_set_cmap(&info->cmap, info);
+ return 0;
+}
+
+static int offb_set_par(struct fb_info *info)
+{
+ struct offb_par *par = (struct offb_par *) info->par;
+
+ /* On avivo, initialize palette control */
+ if (par->cmap_type == cmap_avivo) {
+ writel(0, par->cmap_adr + AVIVO_DC_LUTA_CONTROL);
+ writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_BLUE);
+ writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_GREEN);
+ writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_RED);
+ writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_BLUE);
+ writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_GREEN);
+ writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_RED);
+ writel(0, par->cmap_adr + AVIVO_DC_LUTB_CONTROL);
+ writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_BLUE);
+ writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_GREEN);
+ writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_RED);
+ writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_BLUE);
+ writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_GREEN);
+ writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_RED);
+ writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
+ writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE);
+ writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK);
+ writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
+ writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE);
+ writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK);
+ }
+ return 0;
+}
+
+static void offb_destroy(struct fb_info *info)
+{
+ if (info->screen_base)
+ iounmap(info->screen_base);
+ release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size);
+ fb_dealloc_cmap(&info->cmap);
+ framebuffer_release(info);
+}
+
+static const struct fb_ops offb_ops = {
+ .owner = THIS_MODULE,
+ .fb_destroy = offb_destroy,
+ .fb_setcolreg = offb_setcolreg,
+ .fb_set_par = offb_set_par,
+ .fb_blank = offb_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static void __iomem *offb_map_reg(struct device_node *np, int index,
+ unsigned long offset, unsigned long size)
+{
+ const __be32 *addrp;
+ u64 asize, taddr;
+ unsigned int flags;
+
+ addrp = of_get_pci_address(np, index, &asize, &flags);
+ if (addrp == NULL)
+ addrp = of_get_address(np, index, &asize, &flags);
+ if (addrp == NULL)
+ return NULL;
+ if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
+ return NULL;
+ if ((offset + size) > asize)
+ return NULL;
+ taddr = of_translate_address(np, addrp);
+ if (taddr == OF_BAD_ADDR)
+ return NULL;
+ return ioremap(taddr + offset, size);
+}
+
+static void offb_init_palette_hacks(struct fb_info *info, struct device_node *dp,
+ unsigned long address)
+{
+ struct offb_par *par = (struct offb_par *) info->par;
+
+ if (of_node_name_prefix(dp, "ATY,Rage128")) {
+ par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
+ if (par->cmap_adr)
+ par->cmap_type = cmap_r128;
+ } else if (of_node_name_prefix(dp, "ATY,RageM3pA") ||
+ of_node_name_prefix(dp, "ATY,RageM3p12A")) {
+ par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
+ if (par->cmap_adr)
+ par->cmap_type = cmap_M3A;
+ } else if (of_node_name_prefix(dp, "ATY,RageM3pB")) {
+ par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
+ if (par->cmap_adr)
+ par->cmap_type = cmap_M3B;
+ } else if (of_node_name_prefix(dp, "ATY,Rage6")) {
+ par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff);
+ if (par->cmap_adr)
+ par->cmap_type = cmap_radeon;
+ } else if (of_node_name_prefix(dp, "ATY,")) {
+ unsigned long base = address & 0xff000000UL;
+ par->cmap_adr =
+ ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
+ par->cmap_data = par->cmap_adr + 1;
+ par->cmap_type = cmap_simple;
+ } else if (dp && (of_device_is_compatible(dp, "pci1014,b7") ||
+ of_device_is_compatible(dp, "pci1014,21c"))) {
+ par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000);
+ if (par->cmap_adr)
+ par->cmap_type = cmap_gxt2000;
+ } else if (of_node_name_prefix(dp, "vga,Display-")) {
+ /* Look for AVIVO initialized by SLOF */
+ struct device_node *pciparent = of_get_parent(dp);
+ const u32 *vid, *did;
+ vid = of_get_property(pciparent, "vendor-id", NULL);
+ did = of_get_property(pciparent, "device-id", NULL);
+ /* This will match most R5xx */
+ if (vid && did && *vid == 0x1002 &&
+ ((*did >= 0x7100 && *did < 0x7800) ||
+ (*did >= 0x9400))) {
+ par->cmap_adr = offb_map_reg(pciparent, 2, 0, 0x10000);
+ if (par->cmap_adr)
+ par->cmap_type = cmap_avivo;
+ }
+ of_node_put(pciparent);
+ } else if (dp && of_device_is_compatible(dp, "qemu,std-vga")) {
+#ifdef __BIG_ENDIAN
+ const __be32 io_of_addr[3] = { 0x01000000, 0x0, 0x0 };
+#else
+ const __be32 io_of_addr[3] = { 0x00000001, 0x0, 0x0 };
+#endif
+ u64 io_addr = of_translate_address(dp, io_of_addr);
+ if (io_addr != OF_BAD_ADDR) {
+ par->cmap_adr = ioremap(io_addr + 0x3c8, 2);
+ if (par->cmap_adr) {
+ par->cmap_type = cmap_simple;
+ par->cmap_data = par->cmap_adr + 1;
+ }
+ }
+ }
+ info->fix.visual = (par->cmap_type != cmap_unknown) ?
+ FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR;
+}
+
+static void offb_init_fb(struct platform_device *parent, const char *name,
+ int width, int height, int depth,
+ int pitch, unsigned long address,
+ int foreign_endian, struct device_node *dp)
+{
+ unsigned long res_size = pitch * height;
+ struct offb_par *par = &default_par;
+ unsigned long res_start = address;
+ struct fb_fix_screeninfo *fix;
+ struct fb_var_screeninfo *var;
+ struct fb_info *info;
+
+ if (!request_mem_region(res_start, res_size, "offb"))
+ return;
+
+ printk(KERN_INFO
+ "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",
+ width, height, name, address, depth, pitch);
+ if (depth != 8 && depth != 15 && depth != 16 && depth != 32) {
+ printk(KERN_ERR "%pOF: can't use depth = %d\n", dp, depth);
+ release_mem_region(res_start, res_size);
+ return;
+ }
+
+ info = framebuffer_alloc(sizeof(u32) * 16, &parent->dev);
+
+ if (!info) {
+ release_mem_region(res_start, res_size);
+ return;
+ }
+ platform_set_drvdata(parent, info);
+
+ fix = &info->fix;
+ var = &info->var;
+ info->par = par;
+
+ if (name) {
+ strcpy(fix->id, "OFfb ");
+ strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb "));
+ fix->id[sizeof(fix->id) - 1] = '\0';
+ } else
+ snprintf(fix->id, sizeof(fix->id), "OFfb %pOFn", dp);
+
+
+ var->xres = var->xres_virtual = width;
+ var->yres = var->yres_virtual = height;
+ fix->line_length = pitch;
+
+ fix->smem_start = address;
+ fix->smem_len = pitch * height;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+
+ par->cmap_type = cmap_unknown;
+ if (depth == 8)
+ offb_init_palette_hacks(info, dp, address);
+ else
+ fix->visual = FB_VISUAL_TRUECOLOR;
+
+ var->xoffset = var->yoffset = 0;
+ switch (depth) {
+ case 8:
+ var->bits_per_pixel = 8;
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 15: /* RGB 555 */
+ var->bits_per_pixel = 16;
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 16: /* RGB 565 */
+ var->bits_per_pixel = 16;
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 32: /* RGB 888 */
+ var->bits_per_pixel = 32;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ break;
+ }
+ var->red.msb_right = var->green.msb_right = var->blue.msb_right =
+ var->transp.msb_right = 0;
+ var->grayscale = 0;
+ var->nonstd = 0;
+ var->activate = 0;
+ var->height = var->width = -1;
+ var->pixclock = 10000;
+ var->left_margin = var->right_margin = 16;
+ var->upper_margin = var->lower_margin = 16;
+ var->hsync_len = var->vsync_len = 8;
+ var->sync = 0;
+ var->vmode = FB_VMODE_NONINTERLACED;
+
+ /* set offb aperture size for generic probing */
+ info->apertures = alloc_apertures(1);
+ if (!info->apertures)
+ goto out_aper;
+ info->apertures->ranges[0].base = address;
+ info->apertures->ranges[0].size = fix->smem_len;
+
+ info->fbops = &offb_ops;
+ info->screen_base = ioremap(address, fix->smem_len);
+ info->pseudo_palette = (void *) (info + 1);
+ info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE | foreign_endian;
+
+ fb_alloc_cmap(&info->cmap, 256, 0);
+
+ if (register_framebuffer(info) < 0)
+ goto out_err;
+
+ fb_info(info, "Open Firmware frame buffer device on %pOF\n", dp);
+ return;
+
+out_err:
+ fb_dealloc_cmap(&info->cmap);
+ iounmap(info->screen_base);
+out_aper:
+ iounmap(par->cmap_adr);
+ par->cmap_adr = NULL;
+ framebuffer_release(info);
+ release_mem_region(res_start, res_size);
+}
+
+
+static void offb_init_nodriver(struct platform_device *parent, struct device_node *dp,
+ int no_real_node)
+{
+ unsigned int len;
+ int i, width = 640, height = 480, depth = 8, pitch = 640;
+ unsigned int flags, rsize, addr_prop = 0;
+ unsigned long max_size = 0;
+ u64 rstart, address = OF_BAD_ADDR;
+ const __be32 *pp, *addrp, *up;
+ u64 asize;
+ int foreign_endian = 0;
+
+#ifdef __BIG_ENDIAN
+ if (of_get_property(dp, "little-endian", NULL))
+ foreign_endian = FBINFO_FOREIGN_ENDIAN;
+#else
+ if (of_get_property(dp, "big-endian", NULL))
+ foreign_endian = FBINFO_FOREIGN_ENDIAN;
+#endif
+
+ pp = of_get_property(dp, "linux,bootx-depth", &len);
+ if (pp == NULL)
+ pp = of_get_property(dp, "depth", &len);
+ if (pp && len == sizeof(u32))
+ depth = be32_to_cpup(pp);
+
+ pp = of_get_property(dp, "linux,bootx-width", &len);
+ if (pp == NULL)
+ pp = of_get_property(dp, "width", &len);
+ if (pp && len == sizeof(u32))
+ width = be32_to_cpup(pp);
+
+ pp = of_get_property(dp, "linux,bootx-height", &len);
+ if (pp == NULL)
+ pp = of_get_property(dp, "height", &len);
+ if (pp && len == sizeof(u32))
+ height = be32_to_cpup(pp);
+
+ pp = of_get_property(dp, "linux,bootx-linebytes", &len);
+ if (pp == NULL)
+ pp = of_get_property(dp, "linebytes", &len);
+ if (pp && len == sizeof(u32) && (*pp != 0xffffffffu))
+ pitch = be32_to_cpup(pp);
+ else
+ pitch = width * ((depth + 7) / 8);
+
+ rsize = (unsigned long)pitch * (unsigned long)height;
+
+ /* Ok, now we try to figure out the address of the framebuffer.
+ *
+ * Unfortunately, Open Firmware doesn't provide a standard way to do
+ * so. All we can do is a dodgy heuristic that happens to work in
+ * practice. On most machines, the "address" property contains what
+ * we need, though not on Matrox cards found in IBM machines. What I've
+ * found that appears to give good results is to go through the PCI
+ * ranges and pick one that is both big enough and if possible encloses
+ * the "address" property. If none match, we pick the biggest
+ */
+ up = of_get_property(dp, "linux,bootx-addr", &len);
+ if (up == NULL)
+ up = of_get_property(dp, "address", &len);
+ if (up && len == sizeof(u32))
+ addr_prop = *up;
+
+ /* Hack for when BootX is passing us */
+ if (no_real_node)
+ goto skip_addr;
+
+ for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags))
+ != NULL; i++) {
+ int match_addrp = 0;
+
+ if (!(flags & IORESOURCE_MEM))
+ continue;
+ if (asize < rsize)
+ continue;
+ rstart = of_translate_address(dp, addrp);
+ if (rstart == OF_BAD_ADDR)
+ continue;
+ if (addr_prop && (rstart <= addr_prop) &&
+ ((rstart + asize) >= (addr_prop + rsize)))
+ match_addrp = 1;
+ if (match_addrp) {
+ address = addr_prop;
+ break;
+ }
+ if (rsize > max_size) {
+ max_size = rsize;
+ address = OF_BAD_ADDR;
+ }
+
+ if (address == OF_BAD_ADDR)
+ address = rstart;
+ }
+ skip_addr:
+ if (address == OF_BAD_ADDR && addr_prop)
+ address = (u64)addr_prop;
+ if (address != OF_BAD_ADDR) {
+#ifdef CONFIG_PCI
+ const __be32 *vidp, *didp;
+ u32 vid, did;
+ struct pci_dev *pdev;
+
+ vidp = of_get_property(dp, "vendor-id", NULL);
+ didp = of_get_property(dp, "device-id", NULL);
+ if (vidp && didp) {
+ vid = be32_to_cpup(vidp);
+ did = be32_to_cpup(didp);
+ pdev = pci_get_device(vid, did, NULL);
+ if (!pdev || pci_enable_device(pdev))
+ return;
+ }
+#endif
+ /* kludge for valkyrie */
+ if (of_node_name_eq(dp, "valkyrie"))
+ address += 0x1000;
+ offb_init_fb(parent, no_real_node ? "bootx" : NULL,
+ width, height, depth, pitch, address,
+ foreign_endian, no_real_node ? NULL : dp);
+ }
+}
+
+static int offb_remove(struct platform_device *pdev)
+{
+ struct fb_info *info = platform_get_drvdata(pdev);
+
+ if (info)
+ unregister_framebuffer(info);
+
+ return 0;
+}
+
+static int offb_probe_bootx_noscreen(struct platform_device *pdev)
+{
+ offb_init_nodriver(pdev, of_chosen, 1);
+
+ return 0;
+}
+
+static struct platform_driver offb_driver_bootx_noscreen = {
+ .driver = {
+ .name = "bootx-noscreen",
+ },
+ .probe = offb_probe_bootx_noscreen,
+ .remove = offb_remove,
+};
+
+static int offb_probe_display(struct platform_device *pdev)
+{
+ offb_init_nodriver(pdev, pdev->dev.of_node, 0);
+
+ return 0;
+}
+
+static const struct of_device_id offb_of_match_display[] = {
+ { .compatible = "display", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, offb_of_match_display);
+
+static struct platform_driver offb_driver_display = {
+ .driver = {
+ .name = "of-display",
+ .of_match_table = offb_of_match_display,
+ },
+ .probe = offb_probe_display,
+ .remove = offb_remove,
+};
+
+static int __init offb_init(void)
+{
+ if (fb_get_options("offb", NULL))
+ return -ENODEV;
+
+ platform_driver_register(&offb_driver_bootx_noscreen);
+ platform_driver_register(&offb_driver_display);
+
+ return 0;
+}
+module_init(offb_init);
+
+static void __exit offb_exit(void)
+{
+ platform_driver_unregister(&offb_driver_display);
+ platform_driver_unregister(&offb_driver_bootx_noscreen);
+}
+module_exit(offb_exit);
+
+MODULE_LICENSE("GPL");