This patch fixes various issues with offb (the default fbdev used on powerpc when no proper fbdev is supported). It was broken when using BootX under some circumstances and would fail to properly get the framebuffer base address in others. Also restores the palette hacks that were there. Signed-off-by: Benjamin Herrenschmidt Index: linux-irq-work/arch/powerpc/kernel/btext.c =================================================================== --- linux-irq-work.orig/arch/powerpc/kernel/btext.c 2006-07-02 19:46:58.000000000 +1000 +++ linux-irq-work/arch/powerpc/kernel/btext.c 2006-07-02 19:47:25.000000000 +1000 @@ -111,7 +111,7 @@ logicalDisplayBase = (unsigned char *)address; dispDeviceBase = (unsigned char *)address; dispDeviceRowBytes = pitch; - dispDeviceDepth = depth; + dispDeviceDepth = depth == 15 ? 16 : depth; dispDeviceRect[0] = dispDeviceRect[1] = 0; dispDeviceRect[2] = width; dispDeviceRect[3] = height; @@ -160,20 +160,28 @@ unsigned long address = 0; u32 *prop; - prop = (u32 *)get_property(np, "width", NULL); + prop = (u32 *)get_property(np, "linux,bootx-width", NULL); + if (prop == NULL) + prop = (u32 *)get_property(np, "width", NULL); if (prop == NULL) return -EINVAL; width = *prop; - prop = (u32 *)get_property(np, "height", NULL); + prop = (u32 *)get_property(np, "linux,bootx-height", NULL); + if (prop == NULL) + prop = (u32 *)get_property(np, "height", NULL); if (prop == NULL) return -EINVAL; height = *prop; - prop = (u32 *)get_property(np, "depth", NULL); + prop = (u32 *)get_property(np, "linux,bootx-depth", NULL); + if (prop == NULL) + prop = (u32 *)get_property(np, "depth", NULL); if (prop == NULL) return -EINVAL; depth = *prop; pitch = width * ((depth + 7) / 8); - prop = (u32 *)get_property(np, "linebytes", NULL); + prop = (u32 *)get_property(np, "linux,bootx-linebytes", NULL); + if (prop == NULL) + prop = (u32 *)get_property(np, "linebytes", NULL); if (prop) pitch = *prop; if (pitch == 1) @@ -194,7 +202,7 @@ g_max_loc_Y = height / 16; dispDeviceBase = (unsigned char *)address; dispDeviceRowBytes = pitch; - dispDeviceDepth = depth; + dispDeviceDepth = depth == 15 ? 16 : depth; dispDeviceRect[0] = dispDeviceRect[1] = 0; dispDeviceRect[2] = width; dispDeviceRect[3] = height; Index: linux-irq-work/arch/powerpc/kernel/setup_32.c =================================================================== --- linux-irq-work.orig/arch/powerpc/kernel/setup_32.c 2006-07-02 19:47:15.000000000 +1000 +++ linux-irq-work/arch/powerpc/kernel/setup_32.c 2006-07-02 19:47:25.000000000 +1000 @@ -51,7 +51,6 @@ extern void bootx_init(unsigned long r4, unsigned long phys); -boot_infos_t *boot_infos; struct ide_machdep_calls ppc_ide_md; int boot_cpuid; Index: linux-irq-work/arch/powerpc/platforms/powermac/bootx_init.c =================================================================== --- linux-irq-work.orig/arch/powerpc/platforms/powermac/bootx_init.c 2006-07-02 19:47:25.000000000 +1000 +++ linux-irq-work/arch/powerpc/platforms/powermac/bootx_init.c 2006-07-02 19:59:01.000000000 +1000 @@ -183,8 +183,25 @@ static void __init bootx_add_display_props(unsigned long base, unsigned long *mem_end) { + boot_infos_t *bi = bootx_info; + u32 tmp; + bootx_dt_add_prop("linux,boot-display", NULL, 0, mem_end); bootx_dt_add_prop("linux,opened", NULL, 0, mem_end); + tmp = bi->dispDeviceDepth; + bootx_dt_add_prop("linux,bootx-depth", &tmp, 4, mem_end); + tmp = bi->dispDeviceRect[2] - bi->dispDeviceRect[0]; + bootx_dt_add_prop("linux,bootx-width", &tmp, 4, mem_end); + tmp = bi->dispDeviceRect[3] - bi->dispDeviceRect[1]; + bootx_dt_add_prop("linux,bootx-height", &tmp, 4, mem_end); + tmp = bi->dispDeviceRowBytes; + bootx_dt_add_prop("linux,bootx-linebytes", &tmp, 4, mem_end); + tmp = (u32)bi->dispDeviceBase; + if (tmp == 0) + tmp = (u32)bi->logicalDisplayBase; + tmp += bi->dispDeviceRect[1] * bi->dispDeviceRowBytes; + tmp += bi->dispDeviceRect[0] * ((bi->dispDeviceDepth + 7) / 8); + bootx_dt_add_prop("linux,bootx-addr", &tmp, 4, mem_end); } static void __init bootx_dt_add_string(char *s, unsigned long *mem_end) @@ -224,6 +241,11 @@ DBG(" detected display ! adding properties names !\n"); bootx_dt_add_string("linux,boot-display", mem_end); bootx_dt_add_string("linux,opened", mem_end); + bootx_dt_add_string("linux,bootx-depth", mem_end); + bootx_dt_add_string("linux,bootx-width", mem_end); + bootx_dt_add_string("linux,bootx-height", mem_end); + bootx_dt_add_string("linux,bootx-linebytes", mem_end); + bootx_dt_add_string("linux,bootx-addr", mem_end); strncpy(bootx_disp_path, namep, 255); } @@ -445,7 +467,16 @@ if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) bi->logicalDisplayBase = bi->dispDeviceBase; +#if 0 + /* Fixup depth 16 -> 15 as that's what MacOS calls 16bpp */ + if (bi->dispDeviceDepth == 16) + bi->dispDeviceDepth = 15; +#endif + #ifdef CONFIG_BOOTX_TEXT + ptr = (unsigned long)bi->logicalDisplayBase; + ptr += bi->dispDeviceRect[1] * bi->dispDeviceRowBytes; + ptr += bi->dispDeviceRect[0] * ((bi->dispDeviceDepth + 7) / 8); btext_setup_display(bi->dispDeviceRect[2] - bi->dispDeviceRect[0], bi->dispDeviceRect[3] - bi->dispDeviceRect[1], bi->dispDeviceDepth, bi->dispDeviceRowBytes, Index: linux-irq-work/drivers/video/offb.c =================================================================== --- linux-irq-work.orig/drivers/video/offb.c 2006-07-02 19:46:58.000000000 +1000 +++ linux-irq-work/drivers/video/offb.c 2006-07-02 20:14:31.000000000 +1000 @@ -223,81 +223,9 @@ { struct device_node *dp = NULL, *boot_disp = NULL; -#if defined(CONFIG_BOOTX_TEXT) && defined(CONFIG_PPC32) - struct device_node *macos_display = NULL; -#endif if (fb_get_options("offb", NULL)) return -ENODEV; -#if defined(CONFIG_BOOTX_TEXT) && defined(CONFIG_PPC32) - /* If we're booted from BootX... */ - if (boot_infos != 0) { - unsigned long addr = - (unsigned long) boot_infos->dispDeviceBase; - u32 *addrp; - u64 daddr, dsize; - unsigned int flags; - - /* find the device node corresponding to the macos display */ - while ((dp = of_find_node_by_type(dp, "display"))) { - int i; - - /* - * Look for an AAPL,address property first. - */ - unsigned int na; - unsigned int *ap = - (unsigned int *)get_property(dp, "AAPL,address", - &na); - if (ap != 0) { - for (na /= sizeof(unsigned int); na > 0; - --na, ++ap) - if (*ap <= addr && - addr < *ap + 0x1000000) { - macos_display = dp; - goto foundit; - } - } - - /* - * See if the display address is in one of the address - * ranges for this display. - */ - i = 0; - for (;;) { - addrp = of_get_address(dp, i++, &dsize, &flags); - if (addrp == NULL) - break; - if (!(flags & IORESOURCE_MEM)) - continue; - daddr = of_translate_address(dp, addrp); - if (daddr == OF_BAD_ADDR) - continue; - if (daddr <= addr && addr < (daddr + dsize)) { - macos_display = dp; - goto foundit; - } - } - foundit: - if (macos_display) { - printk(KERN_INFO "MacOS display is %s\n", - dp->full_name); - break; - } - } - - /* initialize it */ - offb_init_fb(macos_display ? macos_display-> - name : "MacOS display", - macos_display ? macos_display-> - full_name : "MacOS display", - boot_infos->dispDeviceRect[2], - boot_infos->dispDeviceRect[3], - boot_infos->dispDeviceDepth, - boot_infos->dispDeviceRowBytes, addr, NULL); - } -#endif /* defined(CONFIG_BOOTX_TEXT) && defined(CONFIG_PPC32) */ - for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { if (get_property(dp, "linux,opened", NULL) && get_property(dp, "linux,boot-display", NULL)) { @@ -317,94 +245,93 @@ static void __init offb_init_nodriver(struct device_node *dp) { - int *pp, i; unsigned int len; - int width = 640, height = 480, depth = 8, pitch; - unsigned int flags, rsize, *up; - u64 address = OF_BAD_ADDR; - u32 *addrp; + 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; + u32 *pp, *addrp, *up; u64 asize; - if ((pp = (int *) get_property(dp, "depth", &len)) != NULL - && len == sizeof(int)) + pp = (u32 *)get_property(dp, "linux,bootx-depth", &len); + if (pp == NULL) + pp = (u32 *)get_property(dp, "depth", &len); + if (pp && len == sizeof(u32)) depth = *pp; - if ((pp = (int *) get_property(dp, "width", &len)) != NULL - && len == sizeof(int)) + + pp = (u32 *)get_property(dp, "linux,bootx-width", &len); + if (pp == NULL) + pp = (u32 *)get_property(dp, "width", &len); + if (pp && len == sizeof(u32)) width = *pp; - if ((pp = (int *) get_property(dp, "height", &len)) != NULL - && len == sizeof(int)) + + pp = (u32 *)get_property(dp, "linux,bootx-height", &len); + if (pp == NULL) + pp = (u32 *)get_property(dp, "height", &len); + if (pp && len == sizeof(u32)) height = *pp; - if ((pp = (int *) get_property(dp, "linebytes", &len)) != NULL - && len == sizeof(int)) { + + pp = (u32 *)get_property(dp, "linux,bootx-linebytes", &len); + if (pp == NULL) + pp = (u32 *)get_property(dp, "linebytes", &len); + if (pp && len == sizeof(u32)) pitch = *pp; - if (pitch == 1) - pitch = 0x1000; - } else - pitch = width; + else + pitch = width * ((depth + 7) / 8); - rsize = (unsigned long)pitch * (unsigned long)height * - (unsigned long)(depth / 8); + rsize = (unsigned long)pitch * (unsigned long)height; - /* Try to match device to a PCI device in order to get a properly - * translated address rather then trying to decode the open firmware - * stuff in various incorrect ways - */ -#ifdef CONFIG_PCI - /* First try to locate the PCI device if any */ - { - struct pci_dev *pdev = NULL; - - for_each_pci_dev(pdev) { - if (dp == pci_device_to_OF_node(pdev)) - break; - } - if (pdev) { - for (i = 0; i < 6 && address == OF_BAD_ADDR; i++) { - if ((pci_resource_flags(pdev, i) & - IORESOURCE_MEM) && - (pci_resource_len(pdev, i) >= rsize)) - address = pci_resource_start(pdev, i); - } - pci_dev_put(pdev); - } - } -#endif /* CONFIG_PCI */ - - /* This one is dodgy, we may drop it ... */ - if (address == OF_BAD_ADDR && - (up = (unsigned *) get_property(dp, "address", &len)) != NULL && - len == sizeof(unsigned int)) - address = (u64) * up; - - if (address == OF_BAD_ADDR) { - for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags)) - != NULL; i++) { - if (!(flags & IORESOURCE_MEM)) - continue; - if (asize >= pitch * height * depth / 8) - break; - } - if (addrp == NULL) { - printk(KERN_ERR - "no framebuffer address found for %s\n", - dp->full_name); - return; - } - address = of_translate_address(dp, addrp); - if (address == OF_BAD_ADDR) { - printk(KERN_ERR - "can't translate framebuffer address for %s\n", - dp->full_name); - return; + /* 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 = (u32 *)get_property(dp, "linux,bootx-addr", &len); + if (up == NULL) + up = (u32 *)get_property(dp, "address", &len); + if (up && len == sizeof(u32)) + addr_prop = *up; + + 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; + } + if (address == OF_BAD_ADDR && addr_prop) + address = (u64)addr_prop; + if (address != OF_BAD_ADDR) { /* kludge for valkyrie */ if (strcmp(dp->name, "valkyrie") == 0) address += 0x1000; + offb_init_fb(dp->name, dp->full_name, width, height, depth, + pitch, address, dp); } - offb_init_fb(dp->name, dp->full_name, width, height, depth, - pitch, address, dp); - } static void __init offb_init_fb(const char *name, const char *full_name, @@ -412,7 +339,7 @@ int pitch, unsigned long address, struct device_node *dp) { - unsigned long res_size = pitch * height * depth / 8; + unsigned long res_size = pitch * height * (depth + 7) / 8; struct offb_par *par = &default_par; unsigned long res_start = address; struct fb_fix_screeninfo *fix; @@ -426,7 +353,7 @@ 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 != 16 && depth != 32) { + if (depth != 8 && depth != 15 && depth != 16 && depth != 32) { printk(KERN_ERR "%s: can't use depth = %d\n", full_name, depth); release_mem_region(res_start, res_size); @@ -502,7 +429,6 @@ : */ FB_VISUAL_TRUECOLOR; var->xoffset = var->yoffset = 0; - var->bits_per_pixel = depth; switch (depth) { case 8: var->bits_per_pixel = 8; @@ -515,7 +441,8 @@ var->transp.offset = 0; var->transp.length = 0; break; - case 16: /* RGB 555 */ + case 15: /* RGB 555 */ + case 16: var->bits_per_pixel = 16; var->red.offset = 10; var->red.length = 5;