18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  linux/drivers/video/offb.c -- Open Firmware based frame buffer device
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *	Copyright (C) 1997 Geert Uytterhoeven
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  This driver is partly based on the PowerMac console driver:
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *	Copyright (C) 1996 Paul Mackerras
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
118c2ecf20Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
128c2ecf20Sopenharmony_ci *  more details.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/string.h>
198c2ecf20Sopenharmony_ci#include <linux/mm.h>
208c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
218c2ecf20Sopenharmony_ci#include <linux/delay.h>
228c2ecf20Sopenharmony_ci#include <linux/of.h>
238c2ecf20Sopenharmony_ci#include <linux/of_address.h>
248c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
258c2ecf20Sopenharmony_ci#include <linux/fb.h>
268c2ecf20Sopenharmony_ci#include <linux/init.h>
278c2ecf20Sopenharmony_ci#include <linux/ioport.h>
288c2ecf20Sopenharmony_ci#include <linux/pci.h>
298c2ecf20Sopenharmony_ci#include <asm/io.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32
328c2ecf20Sopenharmony_ci#include <asm/bootx.h>
338c2ecf20Sopenharmony_ci#endif
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include "macmodes.h"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Supported palette hacks */
388c2ecf20Sopenharmony_cienum {
398c2ecf20Sopenharmony_ci	cmap_unknown,
408c2ecf20Sopenharmony_ci	cmap_simple,		/* ATI Mach64 */
418c2ecf20Sopenharmony_ci	cmap_r128,		/* ATI Rage128 */
428c2ecf20Sopenharmony_ci	cmap_M3A,		/* ATI Rage Mobility M3 Head A */
438c2ecf20Sopenharmony_ci	cmap_M3B,		/* ATI Rage Mobility M3 Head B */
448c2ecf20Sopenharmony_ci	cmap_radeon,		/* ATI Radeon */
458c2ecf20Sopenharmony_ci	cmap_gxt2000,		/* IBM GXT2000 */
468c2ecf20Sopenharmony_ci	cmap_avivo,		/* ATI R5xx */
478c2ecf20Sopenharmony_ci	cmap_qemu,		/* qemu vga */
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct offb_par {
518c2ecf20Sopenharmony_ci	volatile void __iomem *cmap_adr;
528c2ecf20Sopenharmony_ci	volatile void __iomem *cmap_data;
538c2ecf20Sopenharmony_ci	int cmap_type;
548c2ecf20Sopenharmony_ci	int blanked;
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistruct offb_par default_par;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32
608c2ecf20Sopenharmony_ciextern boot_infos_t *boot_infos;
618c2ecf20Sopenharmony_ci#endif
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* Definitions used by the Avivo palette hack */
648c2ecf20Sopenharmony_ci#define AVIVO_DC_LUT_RW_SELECT                  0x6480
658c2ecf20Sopenharmony_ci#define AVIVO_DC_LUT_RW_MODE                    0x6484
668c2ecf20Sopenharmony_ci#define AVIVO_DC_LUT_RW_INDEX                   0x6488
678c2ecf20Sopenharmony_ci#define AVIVO_DC_LUT_SEQ_COLOR                  0x648c
688c2ecf20Sopenharmony_ci#define AVIVO_DC_LUT_PWL_DATA                   0x6490
698c2ecf20Sopenharmony_ci#define AVIVO_DC_LUT_30_COLOR                   0x6494
708c2ecf20Sopenharmony_ci#define AVIVO_DC_LUT_READ_PIPE_SELECT           0x6498
718c2ecf20Sopenharmony_ci#define AVIVO_DC_LUT_WRITE_EN_MASK              0x649c
728c2ecf20Sopenharmony_ci#define AVIVO_DC_LUT_AUTOFILL                   0x64a0
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTA_CONTROL                   0x64c0
758c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE         0x64c4
768c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN        0x64c8
778c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_RED          0x64cc
788c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE         0x64d0
798c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN        0x64d4
808c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_RED          0x64d8
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTB_CONTROL                   0x6cc0
838c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE         0x6cc4
848c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN        0x6cc8
858c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_RED          0x6ccc
868c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE         0x6cd0
878c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN        0x6cd4
888c2ecf20Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_RED          0x6cd8
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci    /*
918c2ecf20Sopenharmony_ci     *  Set a single color register. The values supplied are already
928c2ecf20Sopenharmony_ci     *  rounded down to the hardware's capabilities (according to the
938c2ecf20Sopenharmony_ci     *  entries in the var structure). Return != 0 for invalid regno.
948c2ecf20Sopenharmony_ci     */
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
978c2ecf20Sopenharmony_ci			  u_int transp, struct fb_info *info)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct offb_par *par = (struct offb_par *) info->par;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1028c2ecf20Sopenharmony_ci		u32 *pal = info->pseudo_palette;
1038c2ecf20Sopenharmony_ci		u32 cr = red >> (16 - info->var.red.length);
1048c2ecf20Sopenharmony_ci		u32 cg = green >> (16 - info->var.green.length);
1058c2ecf20Sopenharmony_ci		u32 cb = blue >> (16 - info->var.blue.length);
1068c2ecf20Sopenharmony_ci		u32 value;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		if (regno >= 16)
1098c2ecf20Sopenharmony_ci			return -EINVAL;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		value = (cr << info->var.red.offset) |
1128c2ecf20Sopenharmony_ci			(cg << info->var.green.offset) |
1138c2ecf20Sopenharmony_ci			(cb << info->var.blue.offset);
1148c2ecf20Sopenharmony_ci		if (info->var.transp.length > 0) {
1158c2ecf20Sopenharmony_ci			u32 mask = (1 << info->var.transp.length) - 1;
1168c2ecf20Sopenharmony_ci			mask <<= info->var.transp.offset;
1178c2ecf20Sopenharmony_ci			value |= mask;
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci		pal[regno] = value;
1208c2ecf20Sopenharmony_ci		return 0;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (regno > 255)
1248c2ecf20Sopenharmony_ci		return -EINVAL;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	red >>= 8;
1278c2ecf20Sopenharmony_ci	green >>= 8;
1288c2ecf20Sopenharmony_ci	blue >>= 8;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (!par->cmap_adr)
1318c2ecf20Sopenharmony_ci		return 0;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	switch (par->cmap_type) {
1348c2ecf20Sopenharmony_ci	case cmap_simple:
1358c2ecf20Sopenharmony_ci		writeb(regno, par->cmap_adr);
1368c2ecf20Sopenharmony_ci		writeb(red, par->cmap_data);
1378c2ecf20Sopenharmony_ci		writeb(green, par->cmap_data);
1388c2ecf20Sopenharmony_ci		writeb(blue, par->cmap_data);
1398c2ecf20Sopenharmony_ci		break;
1408c2ecf20Sopenharmony_ci	case cmap_M3A:
1418c2ecf20Sopenharmony_ci		/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
1428c2ecf20Sopenharmony_ci		out_le32(par->cmap_adr + 0x58,
1438c2ecf20Sopenharmony_ci			 in_le32(par->cmap_adr + 0x58) & ~0x20);
1448c2ecf20Sopenharmony_ci		fallthrough;
1458c2ecf20Sopenharmony_ci	case cmap_r128:
1468c2ecf20Sopenharmony_ci		/* Set palette index & data */
1478c2ecf20Sopenharmony_ci		out_8(par->cmap_adr + 0xb0, regno);
1488c2ecf20Sopenharmony_ci		out_le32(par->cmap_adr + 0xb4,
1498c2ecf20Sopenharmony_ci			 (red << 16 | green << 8 | blue));
1508c2ecf20Sopenharmony_ci		break;
1518c2ecf20Sopenharmony_ci	case cmap_M3B:
1528c2ecf20Sopenharmony_ci		/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
1538c2ecf20Sopenharmony_ci		out_le32(par->cmap_adr + 0x58,
1548c2ecf20Sopenharmony_ci			 in_le32(par->cmap_adr + 0x58) | 0x20);
1558c2ecf20Sopenharmony_ci		/* Set palette index & data */
1568c2ecf20Sopenharmony_ci		out_8(par->cmap_adr + 0xb0, regno);
1578c2ecf20Sopenharmony_ci		out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	case cmap_radeon:
1608c2ecf20Sopenharmony_ci		/* Set palette index & data (could be smarter) */
1618c2ecf20Sopenharmony_ci		out_8(par->cmap_adr + 0xb0, regno);
1628c2ecf20Sopenharmony_ci		out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
1638c2ecf20Sopenharmony_ci		break;
1648c2ecf20Sopenharmony_ci	case cmap_gxt2000:
1658c2ecf20Sopenharmony_ci		out_le32(((unsigned __iomem *) par->cmap_adr) + regno,
1668c2ecf20Sopenharmony_ci			 (red << 16 | green << 8 | blue));
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci	case cmap_avivo:
1698c2ecf20Sopenharmony_ci		/* Write to both LUTs for now */
1708c2ecf20Sopenharmony_ci		writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
1718c2ecf20Sopenharmony_ci		writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
1728c2ecf20Sopenharmony_ci		writel(((red) << 22) | ((green) << 12) | ((blue) << 2),
1738c2ecf20Sopenharmony_ci		       par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
1748c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
1758c2ecf20Sopenharmony_ci		writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
1768c2ecf20Sopenharmony_ci		writel(((red) << 22) | ((green) << 12) | ((blue) << 2),
1778c2ecf20Sopenharmony_ci		       par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
1788c2ecf20Sopenharmony_ci		break;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return 0;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci    /*
1858c2ecf20Sopenharmony_ci     *  Blank the display.
1868c2ecf20Sopenharmony_ci     */
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int offb_blank(int blank, struct fb_info *info)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct offb_par *par = (struct offb_par *) info->par;
1918c2ecf20Sopenharmony_ci	int i, j;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (!par->cmap_adr)
1948c2ecf20Sopenharmony_ci		return 0;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (!par->blanked)
1978c2ecf20Sopenharmony_ci		if (!blank)
1988c2ecf20Sopenharmony_ci			return 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	par->blanked = blank;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (blank)
2038c2ecf20Sopenharmony_ci		for (i = 0; i < 256; i++) {
2048c2ecf20Sopenharmony_ci			switch (par->cmap_type) {
2058c2ecf20Sopenharmony_ci			case cmap_simple:
2068c2ecf20Sopenharmony_ci				writeb(i, par->cmap_adr);
2078c2ecf20Sopenharmony_ci				for (j = 0; j < 3; j++)
2088c2ecf20Sopenharmony_ci					writeb(0, par->cmap_data);
2098c2ecf20Sopenharmony_ci				break;
2108c2ecf20Sopenharmony_ci			case cmap_M3A:
2118c2ecf20Sopenharmony_ci				/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
2128c2ecf20Sopenharmony_ci				out_le32(par->cmap_adr + 0x58,
2138c2ecf20Sopenharmony_ci					 in_le32(par->cmap_adr + 0x58) & ~0x20);
2148c2ecf20Sopenharmony_ci				fallthrough;
2158c2ecf20Sopenharmony_ci			case cmap_r128:
2168c2ecf20Sopenharmony_ci				/* Set palette index & data */
2178c2ecf20Sopenharmony_ci				out_8(par->cmap_adr + 0xb0, i);
2188c2ecf20Sopenharmony_ci				out_le32(par->cmap_adr + 0xb4, 0);
2198c2ecf20Sopenharmony_ci				break;
2208c2ecf20Sopenharmony_ci			case cmap_M3B:
2218c2ecf20Sopenharmony_ci				/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
2228c2ecf20Sopenharmony_ci				out_le32(par->cmap_adr + 0x58,
2238c2ecf20Sopenharmony_ci					 in_le32(par->cmap_adr + 0x58) | 0x20);
2248c2ecf20Sopenharmony_ci				/* Set palette index & data */
2258c2ecf20Sopenharmony_ci				out_8(par->cmap_adr + 0xb0, i);
2268c2ecf20Sopenharmony_ci				out_le32(par->cmap_adr + 0xb4, 0);
2278c2ecf20Sopenharmony_ci				break;
2288c2ecf20Sopenharmony_ci			case cmap_radeon:
2298c2ecf20Sopenharmony_ci				out_8(par->cmap_adr + 0xb0, i);
2308c2ecf20Sopenharmony_ci				out_le32(par->cmap_adr + 0xb4, 0);
2318c2ecf20Sopenharmony_ci				break;
2328c2ecf20Sopenharmony_ci			case cmap_gxt2000:
2338c2ecf20Sopenharmony_ci				out_le32(((unsigned __iomem *) par->cmap_adr) + i,
2348c2ecf20Sopenharmony_ci					 0);
2358c2ecf20Sopenharmony_ci				break;
2368c2ecf20Sopenharmony_ci			case cmap_avivo:
2378c2ecf20Sopenharmony_ci				writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
2388c2ecf20Sopenharmony_ci				writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
2398c2ecf20Sopenharmony_ci				writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
2408c2ecf20Sopenharmony_ci				writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
2418c2ecf20Sopenharmony_ci				writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
2428c2ecf20Sopenharmony_ci				writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
2438c2ecf20Sopenharmony_ci				break;
2448c2ecf20Sopenharmony_ci			}
2458c2ecf20Sopenharmony_ci	} else
2468c2ecf20Sopenharmony_ci		fb_set_cmap(&info->cmap, info);
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int offb_set_par(struct fb_info *info)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct offb_par *par = (struct offb_par *) info->par;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* On avivo, initialize palette control */
2558c2ecf20Sopenharmony_ci	if (par->cmap_type == cmap_avivo) {
2568c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTA_CONTROL);
2578c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_BLUE);
2588c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_GREEN);
2598c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_RED);
2608c2ecf20Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_BLUE);
2618c2ecf20Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_GREEN);
2628c2ecf20Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_RED);
2638c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTB_CONTROL);
2648c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_BLUE);
2658c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_GREEN);
2668c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_RED);
2678c2ecf20Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_BLUE);
2688c2ecf20Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_GREEN);
2698c2ecf20Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_RED);
2708c2ecf20Sopenharmony_ci		writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
2718c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE);
2728c2ecf20Sopenharmony_ci		writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK);
2738c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
2748c2ecf20Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE);
2758c2ecf20Sopenharmony_ci		writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK);
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci	return 0;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic void offb_destroy(struct fb_info *info)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	if (info->screen_base)
2838c2ecf20Sopenharmony_ci		iounmap(info->screen_base);
2848c2ecf20Sopenharmony_ci	release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size);
2858c2ecf20Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
2868c2ecf20Sopenharmony_ci	framebuffer_release(info);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic const struct fb_ops offb_ops = {
2908c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2918c2ecf20Sopenharmony_ci	.fb_destroy	= offb_destroy,
2928c2ecf20Sopenharmony_ci	.fb_setcolreg	= offb_setcolreg,
2938c2ecf20Sopenharmony_ci	.fb_set_par	= offb_set_par,
2948c2ecf20Sopenharmony_ci	.fb_blank	= offb_blank,
2958c2ecf20Sopenharmony_ci	.fb_fillrect	= cfb_fillrect,
2968c2ecf20Sopenharmony_ci	.fb_copyarea	= cfb_copyarea,
2978c2ecf20Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
2988c2ecf20Sopenharmony_ci};
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic void __iomem *offb_map_reg(struct device_node *np, int index,
3018c2ecf20Sopenharmony_ci				  unsigned long offset, unsigned long size)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	const __be32 *addrp;
3048c2ecf20Sopenharmony_ci	u64 asize, taddr;
3058c2ecf20Sopenharmony_ci	unsigned int flags;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	addrp = of_get_pci_address(np, index, &asize, &flags);
3088c2ecf20Sopenharmony_ci	if (addrp == NULL)
3098c2ecf20Sopenharmony_ci		addrp = of_get_address(np, index, &asize, &flags);
3108c2ecf20Sopenharmony_ci	if (addrp == NULL)
3118c2ecf20Sopenharmony_ci		return NULL;
3128c2ecf20Sopenharmony_ci	if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
3138c2ecf20Sopenharmony_ci		return NULL;
3148c2ecf20Sopenharmony_ci	if ((offset + size) > asize)
3158c2ecf20Sopenharmony_ci		return NULL;
3168c2ecf20Sopenharmony_ci	taddr = of_translate_address(np, addrp);
3178c2ecf20Sopenharmony_ci	if (taddr == OF_BAD_ADDR)
3188c2ecf20Sopenharmony_ci		return NULL;
3198c2ecf20Sopenharmony_ci	return ioremap(taddr + offset, size);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void offb_init_palette_hacks(struct fb_info *info, struct device_node *dp,
3238c2ecf20Sopenharmony_ci				    unsigned long address)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct offb_par *par = (struct offb_par *) info->par;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (of_node_name_prefix(dp, "ATY,Rage128")) {
3288c2ecf20Sopenharmony_ci		par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
3298c2ecf20Sopenharmony_ci		if (par->cmap_adr)
3308c2ecf20Sopenharmony_ci			par->cmap_type = cmap_r128;
3318c2ecf20Sopenharmony_ci	} else if (of_node_name_prefix(dp, "ATY,RageM3pA") ||
3328c2ecf20Sopenharmony_ci		   of_node_name_prefix(dp, "ATY,RageM3p12A")) {
3338c2ecf20Sopenharmony_ci		par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
3348c2ecf20Sopenharmony_ci		if (par->cmap_adr)
3358c2ecf20Sopenharmony_ci			par->cmap_type = cmap_M3A;
3368c2ecf20Sopenharmony_ci	} else if (of_node_name_prefix(dp, "ATY,RageM3pB")) {
3378c2ecf20Sopenharmony_ci		par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
3388c2ecf20Sopenharmony_ci		if (par->cmap_adr)
3398c2ecf20Sopenharmony_ci			par->cmap_type = cmap_M3B;
3408c2ecf20Sopenharmony_ci	} else if (of_node_name_prefix(dp, "ATY,Rage6")) {
3418c2ecf20Sopenharmony_ci		par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff);
3428c2ecf20Sopenharmony_ci		if (par->cmap_adr)
3438c2ecf20Sopenharmony_ci			par->cmap_type = cmap_radeon;
3448c2ecf20Sopenharmony_ci	} else if (of_node_name_prefix(dp, "ATY,")) {
3458c2ecf20Sopenharmony_ci		unsigned long base = address & 0xff000000UL;
3468c2ecf20Sopenharmony_ci		par->cmap_adr =
3478c2ecf20Sopenharmony_ci			ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
3488c2ecf20Sopenharmony_ci		par->cmap_data = par->cmap_adr + 1;
3498c2ecf20Sopenharmony_ci		par->cmap_type = cmap_simple;
3508c2ecf20Sopenharmony_ci	} else if (dp && (of_device_is_compatible(dp, "pci1014,b7") ||
3518c2ecf20Sopenharmony_ci			  of_device_is_compatible(dp, "pci1014,21c"))) {
3528c2ecf20Sopenharmony_ci		par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000);
3538c2ecf20Sopenharmony_ci		if (par->cmap_adr)
3548c2ecf20Sopenharmony_ci			par->cmap_type = cmap_gxt2000;
3558c2ecf20Sopenharmony_ci	} else if (of_node_name_prefix(dp, "vga,Display-")) {
3568c2ecf20Sopenharmony_ci		/* Look for AVIVO initialized by SLOF */
3578c2ecf20Sopenharmony_ci		struct device_node *pciparent = of_get_parent(dp);
3588c2ecf20Sopenharmony_ci		const u32 *vid, *did;
3598c2ecf20Sopenharmony_ci		vid = of_get_property(pciparent, "vendor-id", NULL);
3608c2ecf20Sopenharmony_ci		did = of_get_property(pciparent, "device-id", NULL);
3618c2ecf20Sopenharmony_ci		/* This will match most R5xx */
3628c2ecf20Sopenharmony_ci		if (vid && did && *vid == 0x1002 &&
3638c2ecf20Sopenharmony_ci		    ((*did >= 0x7100 && *did < 0x7800) ||
3648c2ecf20Sopenharmony_ci		     (*did >= 0x9400))) {
3658c2ecf20Sopenharmony_ci			par->cmap_adr = offb_map_reg(pciparent, 2, 0, 0x10000);
3668c2ecf20Sopenharmony_ci			if (par->cmap_adr)
3678c2ecf20Sopenharmony_ci				par->cmap_type = cmap_avivo;
3688c2ecf20Sopenharmony_ci		}
3698c2ecf20Sopenharmony_ci		of_node_put(pciparent);
3708c2ecf20Sopenharmony_ci	} else if (dp && of_device_is_compatible(dp, "qemu,std-vga")) {
3718c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
3728c2ecf20Sopenharmony_ci		const __be32 io_of_addr[3] = { 0x01000000, 0x0, 0x0 };
3738c2ecf20Sopenharmony_ci#else
3748c2ecf20Sopenharmony_ci		const __be32 io_of_addr[3] = { 0x00000001, 0x0, 0x0 };
3758c2ecf20Sopenharmony_ci#endif
3768c2ecf20Sopenharmony_ci		u64 io_addr = of_translate_address(dp, io_of_addr);
3778c2ecf20Sopenharmony_ci		if (io_addr != OF_BAD_ADDR) {
3788c2ecf20Sopenharmony_ci			par->cmap_adr = ioremap(io_addr + 0x3c8, 2);
3798c2ecf20Sopenharmony_ci			if (par->cmap_adr) {
3808c2ecf20Sopenharmony_ci				par->cmap_type = cmap_simple;
3818c2ecf20Sopenharmony_ci				par->cmap_data = par->cmap_adr + 1;
3828c2ecf20Sopenharmony_ci			}
3838c2ecf20Sopenharmony_ci		}
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci	info->fix.visual = (par->cmap_type != cmap_unknown) ?
3868c2ecf20Sopenharmony_ci		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic void __init offb_init_fb(const char *name,
3908c2ecf20Sopenharmony_ci				int width, int height, int depth,
3918c2ecf20Sopenharmony_ci				int pitch, unsigned long address,
3928c2ecf20Sopenharmony_ci				int foreign_endian, struct device_node *dp)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	unsigned long res_size = pitch * height;
3958c2ecf20Sopenharmony_ci	struct offb_par *par = &default_par;
3968c2ecf20Sopenharmony_ci	unsigned long res_start = address;
3978c2ecf20Sopenharmony_ci	struct fb_fix_screeninfo *fix;
3988c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var;
3998c2ecf20Sopenharmony_ci	struct fb_info *info;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (!request_mem_region(res_start, res_size, "offb"))
4028c2ecf20Sopenharmony_ci		return;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	printk(KERN_INFO
4058c2ecf20Sopenharmony_ci	       "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",
4068c2ecf20Sopenharmony_ci	       width, height, name, address, depth, pitch);
4078c2ecf20Sopenharmony_ci	if (depth != 8 && depth != 15 && depth != 16 && depth != 32) {
4088c2ecf20Sopenharmony_ci		printk(KERN_ERR "%pOF: can't use depth = %d\n", dp, depth);
4098c2ecf20Sopenharmony_ci		release_mem_region(res_start, res_size);
4108c2ecf20Sopenharmony_ci		return;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	info = framebuffer_alloc(sizeof(u32) * 16, NULL);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (info == 0) {
4168c2ecf20Sopenharmony_ci		release_mem_region(res_start, res_size);
4178c2ecf20Sopenharmony_ci		return;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	fix = &info->fix;
4218c2ecf20Sopenharmony_ci	var = &info->var;
4228c2ecf20Sopenharmony_ci	info->par = par;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (name) {
4258c2ecf20Sopenharmony_ci		strcpy(fix->id, "OFfb ");
4268c2ecf20Sopenharmony_ci		strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb "));
4278c2ecf20Sopenharmony_ci		fix->id[sizeof(fix->id) - 1] = '\0';
4288c2ecf20Sopenharmony_ci	} else
4298c2ecf20Sopenharmony_ci		snprintf(fix->id, sizeof(fix->id), "OFfb %pOFn", dp);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	var->xres = var->xres_virtual = width;
4338c2ecf20Sopenharmony_ci	var->yres = var->yres_virtual = height;
4348c2ecf20Sopenharmony_ci	fix->line_length = pitch;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	fix->smem_start = address;
4378c2ecf20Sopenharmony_ci	fix->smem_len = pitch * height;
4388c2ecf20Sopenharmony_ci	fix->type = FB_TYPE_PACKED_PIXELS;
4398c2ecf20Sopenharmony_ci	fix->type_aux = 0;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	par->cmap_type = cmap_unknown;
4428c2ecf20Sopenharmony_ci	if (depth == 8)
4438c2ecf20Sopenharmony_ci		offb_init_palette_hacks(info, dp, address);
4448c2ecf20Sopenharmony_ci	else
4458c2ecf20Sopenharmony_ci		fix->visual = FB_VISUAL_TRUECOLOR;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	var->xoffset = var->yoffset = 0;
4488c2ecf20Sopenharmony_ci	switch (depth) {
4498c2ecf20Sopenharmony_ci	case 8:
4508c2ecf20Sopenharmony_ci		var->bits_per_pixel = 8;
4518c2ecf20Sopenharmony_ci		var->red.offset = 0;
4528c2ecf20Sopenharmony_ci		var->red.length = 8;
4538c2ecf20Sopenharmony_ci		var->green.offset = 0;
4548c2ecf20Sopenharmony_ci		var->green.length = 8;
4558c2ecf20Sopenharmony_ci		var->blue.offset = 0;
4568c2ecf20Sopenharmony_ci		var->blue.length = 8;
4578c2ecf20Sopenharmony_ci		var->transp.offset = 0;
4588c2ecf20Sopenharmony_ci		var->transp.length = 0;
4598c2ecf20Sopenharmony_ci		break;
4608c2ecf20Sopenharmony_ci	case 15:		/* RGB 555 */
4618c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
4628c2ecf20Sopenharmony_ci		var->red.offset = 10;
4638c2ecf20Sopenharmony_ci		var->red.length = 5;
4648c2ecf20Sopenharmony_ci		var->green.offset = 5;
4658c2ecf20Sopenharmony_ci		var->green.length = 5;
4668c2ecf20Sopenharmony_ci		var->blue.offset = 0;
4678c2ecf20Sopenharmony_ci		var->blue.length = 5;
4688c2ecf20Sopenharmony_ci		var->transp.offset = 0;
4698c2ecf20Sopenharmony_ci		var->transp.length = 0;
4708c2ecf20Sopenharmony_ci		break;
4718c2ecf20Sopenharmony_ci	case 16:		/* RGB 565 */
4728c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
4738c2ecf20Sopenharmony_ci		var->red.offset = 11;
4748c2ecf20Sopenharmony_ci		var->red.length = 5;
4758c2ecf20Sopenharmony_ci		var->green.offset = 5;
4768c2ecf20Sopenharmony_ci		var->green.length = 6;
4778c2ecf20Sopenharmony_ci		var->blue.offset = 0;
4788c2ecf20Sopenharmony_ci		var->blue.length = 5;
4798c2ecf20Sopenharmony_ci		var->transp.offset = 0;
4808c2ecf20Sopenharmony_ci		var->transp.length = 0;
4818c2ecf20Sopenharmony_ci		break;
4828c2ecf20Sopenharmony_ci	case 32:		/* RGB 888 */
4838c2ecf20Sopenharmony_ci		var->bits_per_pixel = 32;
4848c2ecf20Sopenharmony_ci		var->red.offset = 16;
4858c2ecf20Sopenharmony_ci		var->red.length = 8;
4868c2ecf20Sopenharmony_ci		var->green.offset = 8;
4878c2ecf20Sopenharmony_ci		var->green.length = 8;
4888c2ecf20Sopenharmony_ci		var->blue.offset = 0;
4898c2ecf20Sopenharmony_ci		var->blue.length = 8;
4908c2ecf20Sopenharmony_ci		var->transp.offset = 24;
4918c2ecf20Sopenharmony_ci		var->transp.length = 8;
4928c2ecf20Sopenharmony_ci		break;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci	var->red.msb_right = var->green.msb_right = var->blue.msb_right =
4958c2ecf20Sopenharmony_ci	    var->transp.msb_right = 0;
4968c2ecf20Sopenharmony_ci	var->grayscale = 0;
4978c2ecf20Sopenharmony_ci	var->nonstd = 0;
4988c2ecf20Sopenharmony_ci	var->activate = 0;
4998c2ecf20Sopenharmony_ci	var->height = var->width = -1;
5008c2ecf20Sopenharmony_ci	var->pixclock = 10000;
5018c2ecf20Sopenharmony_ci	var->left_margin = var->right_margin = 16;
5028c2ecf20Sopenharmony_ci	var->upper_margin = var->lower_margin = 16;
5038c2ecf20Sopenharmony_ci	var->hsync_len = var->vsync_len = 8;
5048c2ecf20Sopenharmony_ci	var->sync = 0;
5058c2ecf20Sopenharmony_ci	var->vmode = FB_VMODE_NONINTERLACED;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	/* set offb aperture size for generic probing */
5088c2ecf20Sopenharmony_ci	info->apertures = alloc_apertures(1);
5098c2ecf20Sopenharmony_ci	if (!info->apertures)
5108c2ecf20Sopenharmony_ci		goto out_aper;
5118c2ecf20Sopenharmony_ci	info->apertures->ranges[0].base = address;
5128c2ecf20Sopenharmony_ci	info->apertures->ranges[0].size = fix->smem_len;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	info->fbops = &offb_ops;
5158c2ecf20Sopenharmony_ci	info->screen_base = ioremap(address, fix->smem_len);
5168c2ecf20Sopenharmony_ci	info->pseudo_palette = (void *) (info + 1);
5178c2ecf20Sopenharmony_ci	info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE | foreign_endian;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	fb_alloc_cmap(&info->cmap, 256, 0);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (register_framebuffer(info) < 0)
5228c2ecf20Sopenharmony_ci		goto out_err;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	fb_info(info, "Open Firmware frame buffer device on %pOF\n", dp);
5258c2ecf20Sopenharmony_ci	return;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ciout_err:
5288c2ecf20Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
5298c2ecf20Sopenharmony_ci	iounmap(info->screen_base);
5308c2ecf20Sopenharmony_ciout_aper:
5318c2ecf20Sopenharmony_ci	iounmap(par->cmap_adr);
5328c2ecf20Sopenharmony_ci	par->cmap_adr = NULL;
5338c2ecf20Sopenharmony_ci	framebuffer_release(info);
5348c2ecf20Sopenharmony_ci	release_mem_region(res_start, res_size);
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic void __init offb_init_nodriver(struct device_node *dp, int no_real_node)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	unsigned int len;
5418c2ecf20Sopenharmony_ci	int i, width = 640, height = 480, depth = 8, pitch = 640;
5428c2ecf20Sopenharmony_ci	unsigned int flags, rsize, addr_prop = 0;
5438c2ecf20Sopenharmony_ci	unsigned long max_size = 0;
5448c2ecf20Sopenharmony_ci	u64 rstart, address = OF_BAD_ADDR;
5458c2ecf20Sopenharmony_ci	const __be32 *pp, *addrp, *up;
5468c2ecf20Sopenharmony_ci	u64 asize;
5478c2ecf20Sopenharmony_ci	int foreign_endian = 0;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
5508c2ecf20Sopenharmony_ci	if (of_get_property(dp, "little-endian", NULL))
5518c2ecf20Sopenharmony_ci		foreign_endian = FBINFO_FOREIGN_ENDIAN;
5528c2ecf20Sopenharmony_ci#else
5538c2ecf20Sopenharmony_ci	if (of_get_property(dp, "big-endian", NULL))
5548c2ecf20Sopenharmony_ci		foreign_endian = FBINFO_FOREIGN_ENDIAN;
5558c2ecf20Sopenharmony_ci#endif
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	pp = of_get_property(dp, "linux,bootx-depth", &len);
5588c2ecf20Sopenharmony_ci	if (pp == NULL)
5598c2ecf20Sopenharmony_ci		pp = of_get_property(dp, "depth", &len);
5608c2ecf20Sopenharmony_ci	if (pp && len == sizeof(u32))
5618c2ecf20Sopenharmony_ci		depth = be32_to_cpup(pp);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	pp = of_get_property(dp, "linux,bootx-width", &len);
5648c2ecf20Sopenharmony_ci	if (pp == NULL)
5658c2ecf20Sopenharmony_ci		pp = of_get_property(dp, "width", &len);
5668c2ecf20Sopenharmony_ci	if (pp && len == sizeof(u32))
5678c2ecf20Sopenharmony_ci		width = be32_to_cpup(pp);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	pp = of_get_property(dp, "linux,bootx-height", &len);
5708c2ecf20Sopenharmony_ci	if (pp == NULL)
5718c2ecf20Sopenharmony_ci		pp = of_get_property(dp, "height", &len);
5728c2ecf20Sopenharmony_ci	if (pp && len == sizeof(u32))
5738c2ecf20Sopenharmony_ci		height = be32_to_cpup(pp);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	pp = of_get_property(dp, "linux,bootx-linebytes", &len);
5768c2ecf20Sopenharmony_ci	if (pp == NULL)
5778c2ecf20Sopenharmony_ci		pp = of_get_property(dp, "linebytes", &len);
5788c2ecf20Sopenharmony_ci	if (pp && len == sizeof(u32) && (*pp != 0xffffffffu))
5798c2ecf20Sopenharmony_ci		pitch = be32_to_cpup(pp);
5808c2ecf20Sopenharmony_ci	else
5818c2ecf20Sopenharmony_ci		pitch = width * ((depth + 7) / 8);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	rsize = (unsigned long)pitch * (unsigned long)height;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	/* Ok, now we try to figure out the address of the framebuffer.
5868c2ecf20Sopenharmony_ci	 *
5878c2ecf20Sopenharmony_ci	 * Unfortunately, Open Firmware doesn't provide a standard way to do
5888c2ecf20Sopenharmony_ci	 * so. All we can do is a dodgy heuristic that happens to work in
5898c2ecf20Sopenharmony_ci	 * practice. On most machines, the "address" property contains what
5908c2ecf20Sopenharmony_ci	 * we need, though not on Matrox cards found in IBM machines. What I've
5918c2ecf20Sopenharmony_ci	 * found that appears to give good results is to go through the PCI
5928c2ecf20Sopenharmony_ci	 * ranges and pick one that is both big enough and if possible encloses
5938c2ecf20Sopenharmony_ci	 * the "address" property. If none match, we pick the biggest
5948c2ecf20Sopenharmony_ci	 */
5958c2ecf20Sopenharmony_ci	up = of_get_property(dp, "linux,bootx-addr", &len);
5968c2ecf20Sopenharmony_ci	if (up == NULL)
5978c2ecf20Sopenharmony_ci		up = of_get_property(dp, "address", &len);
5988c2ecf20Sopenharmony_ci	if (up && len == sizeof(u32))
5998c2ecf20Sopenharmony_ci		addr_prop = *up;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/* Hack for when BootX is passing us */
6028c2ecf20Sopenharmony_ci	if (no_real_node)
6038c2ecf20Sopenharmony_ci		goto skip_addr;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags))
6068c2ecf20Sopenharmony_ci		     != NULL; i++) {
6078c2ecf20Sopenharmony_ci		int match_addrp = 0;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci		if (!(flags & IORESOURCE_MEM))
6108c2ecf20Sopenharmony_ci			continue;
6118c2ecf20Sopenharmony_ci		if (asize < rsize)
6128c2ecf20Sopenharmony_ci			continue;
6138c2ecf20Sopenharmony_ci		rstart = of_translate_address(dp, addrp);
6148c2ecf20Sopenharmony_ci		if (rstart == OF_BAD_ADDR)
6158c2ecf20Sopenharmony_ci			continue;
6168c2ecf20Sopenharmony_ci		if (addr_prop && (rstart <= addr_prop) &&
6178c2ecf20Sopenharmony_ci		    ((rstart + asize) >= (addr_prop + rsize)))
6188c2ecf20Sopenharmony_ci			match_addrp = 1;
6198c2ecf20Sopenharmony_ci		if (match_addrp) {
6208c2ecf20Sopenharmony_ci			address = addr_prop;
6218c2ecf20Sopenharmony_ci			break;
6228c2ecf20Sopenharmony_ci		}
6238c2ecf20Sopenharmony_ci		if (rsize > max_size) {
6248c2ecf20Sopenharmony_ci			max_size = rsize;
6258c2ecf20Sopenharmony_ci			address = OF_BAD_ADDR;
6268c2ecf20Sopenharmony_ci 		}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci		if (address == OF_BAD_ADDR)
6298c2ecf20Sopenharmony_ci			address = rstart;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci skip_addr:
6328c2ecf20Sopenharmony_ci	if (address == OF_BAD_ADDR && addr_prop)
6338c2ecf20Sopenharmony_ci		address = (u64)addr_prop;
6348c2ecf20Sopenharmony_ci	if (address != OF_BAD_ADDR) {
6358c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
6368c2ecf20Sopenharmony_ci		const __be32 *vidp, *didp;
6378c2ecf20Sopenharmony_ci		u32 vid, did;
6388c2ecf20Sopenharmony_ci		struct pci_dev *pdev;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci		vidp = of_get_property(dp, "vendor-id", NULL);
6418c2ecf20Sopenharmony_ci		didp = of_get_property(dp, "device-id", NULL);
6428c2ecf20Sopenharmony_ci		if (vidp && didp) {
6438c2ecf20Sopenharmony_ci			vid = be32_to_cpup(vidp);
6448c2ecf20Sopenharmony_ci			did = be32_to_cpup(didp);
6458c2ecf20Sopenharmony_ci			pdev = pci_get_device(vid, did, NULL);
6468c2ecf20Sopenharmony_ci			if (!pdev || pci_enable_device(pdev))
6478c2ecf20Sopenharmony_ci				return;
6488c2ecf20Sopenharmony_ci		}
6498c2ecf20Sopenharmony_ci#endif
6508c2ecf20Sopenharmony_ci		/* kludge for valkyrie */
6518c2ecf20Sopenharmony_ci		if (of_node_name_eq(dp, "valkyrie"))
6528c2ecf20Sopenharmony_ci			address += 0x1000;
6538c2ecf20Sopenharmony_ci		offb_init_fb(no_real_node ? "bootx" : NULL,
6548c2ecf20Sopenharmony_ci			     width, height, depth, pitch, address,
6558c2ecf20Sopenharmony_ci			     foreign_endian, no_real_node ? NULL : dp);
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic int __init offb_init(void)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct device_node *dp = NULL, *boot_disp = NULL;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	if (fb_get_options("offb", NULL))
6648c2ecf20Sopenharmony_ci		return -ENODEV;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	/* Check if we have a MacOS display without a node spec */
6678c2ecf20Sopenharmony_ci	if (of_get_property(of_chosen, "linux,bootx-noscreen", NULL) != NULL) {
6688c2ecf20Sopenharmony_ci		/* The old code tried to work out which node was the MacOS
6698c2ecf20Sopenharmony_ci		 * display based on the address. I'm dropping that since the
6708c2ecf20Sopenharmony_ci		 * lack of a node spec only happens with old BootX versions
6718c2ecf20Sopenharmony_ci		 * (users can update) and with this code, they'll still get
6728c2ecf20Sopenharmony_ci		 * a display (just not the palette hacks).
6738c2ecf20Sopenharmony_ci		 */
6748c2ecf20Sopenharmony_ci		offb_init_nodriver(of_chosen, 1);
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	for_each_node_by_type(dp, "display") {
6788c2ecf20Sopenharmony_ci		if (of_get_property(dp, "linux,opened", NULL) &&
6798c2ecf20Sopenharmony_ci		    of_get_property(dp, "linux,boot-display", NULL)) {
6808c2ecf20Sopenharmony_ci			boot_disp = dp;
6818c2ecf20Sopenharmony_ci			offb_init_nodriver(dp, 0);
6828c2ecf20Sopenharmony_ci		}
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci	for_each_node_by_type(dp, "display") {
6858c2ecf20Sopenharmony_ci		if (of_get_property(dp, "linux,opened", NULL) &&
6868c2ecf20Sopenharmony_ci		    dp != boot_disp)
6878c2ecf20Sopenharmony_ci			offb_init_nodriver(dp, 0);
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	return 0;
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cimodule_init(offb_init);
6958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
696