162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/drivers/video/offb.c -- Open Firmware based frame buffer device
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *	Copyright (C) 1997 Geert Uytterhoeven
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  This driver is partly based on the PowerMac console driver:
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *	Copyright (C) 1996 Paul Mackerras
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
1162306a36Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
1262306a36Sopenharmony_ci *  more details.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/aperture.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/kernel.h>
1862306a36Sopenharmony_ci#include <linux/errno.h>
1962306a36Sopenharmony_ci#include <linux/string.h>
2062306a36Sopenharmony_ci#include <linux/mm.h>
2162306a36Sopenharmony_ci#include <linux/vmalloc.h>
2262306a36Sopenharmony_ci#include <linux/delay.h>
2362306a36Sopenharmony_ci#include <linux/of.h>
2462306a36Sopenharmony_ci#include <linux/of_address.h>
2562306a36Sopenharmony_ci#include <linux/interrupt.h>
2662306a36Sopenharmony_ci#include <linux/fb.h>
2762306a36Sopenharmony_ci#include <linux/init.h>
2862306a36Sopenharmony_ci#include <linux/ioport.h>
2962306a36Sopenharmony_ci#include <linux/pci.h>
3062306a36Sopenharmony_ci#include <linux/platform_device.h>
3162306a36Sopenharmony_ci#include <asm/io.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#ifdef CONFIG_PPC32
3462306a36Sopenharmony_ci#include <asm/bootx.h>
3562306a36Sopenharmony_ci#endif
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "macmodes.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* Supported palette hacks */
4062306a36Sopenharmony_cienum {
4162306a36Sopenharmony_ci	cmap_unknown,
4262306a36Sopenharmony_ci	cmap_simple,		/* ATI Mach64 */
4362306a36Sopenharmony_ci	cmap_r128,		/* ATI Rage128 */
4462306a36Sopenharmony_ci	cmap_M3A,		/* ATI Rage Mobility M3 Head A */
4562306a36Sopenharmony_ci	cmap_M3B,		/* ATI Rage Mobility M3 Head B */
4662306a36Sopenharmony_ci	cmap_radeon,		/* ATI Radeon */
4762306a36Sopenharmony_ci	cmap_gxt2000,		/* IBM GXT2000 */
4862306a36Sopenharmony_ci	cmap_avivo,		/* ATI R5xx */
4962306a36Sopenharmony_ci	cmap_qemu,		/* qemu vga */
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct offb_par {
5362306a36Sopenharmony_ci	volatile void __iomem *cmap_adr;
5462306a36Sopenharmony_ci	volatile void __iomem *cmap_data;
5562306a36Sopenharmony_ci	int cmap_type;
5662306a36Sopenharmony_ci	int blanked;
5762306a36Sopenharmony_ci	u32 pseudo_palette[16];
5862306a36Sopenharmony_ci	resource_size_t base;
5962306a36Sopenharmony_ci	resource_size_t size;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#ifdef CONFIG_PPC32
6362306a36Sopenharmony_ciextern boot_infos_t *boot_infos;
6462306a36Sopenharmony_ci#endif
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* Definitions used by the Avivo palette hack */
6762306a36Sopenharmony_ci#define AVIVO_DC_LUT_RW_SELECT                  0x6480
6862306a36Sopenharmony_ci#define AVIVO_DC_LUT_RW_MODE                    0x6484
6962306a36Sopenharmony_ci#define AVIVO_DC_LUT_RW_INDEX                   0x6488
7062306a36Sopenharmony_ci#define AVIVO_DC_LUT_SEQ_COLOR                  0x648c
7162306a36Sopenharmony_ci#define AVIVO_DC_LUT_PWL_DATA                   0x6490
7262306a36Sopenharmony_ci#define AVIVO_DC_LUT_30_COLOR                   0x6494
7362306a36Sopenharmony_ci#define AVIVO_DC_LUT_READ_PIPE_SELECT           0x6498
7462306a36Sopenharmony_ci#define AVIVO_DC_LUT_WRITE_EN_MASK              0x649c
7562306a36Sopenharmony_ci#define AVIVO_DC_LUT_AUTOFILL                   0x64a0
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define AVIVO_DC_LUTA_CONTROL                   0x64c0
7862306a36Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE         0x64c4
7962306a36Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN        0x64c8
8062306a36Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_RED          0x64cc
8162306a36Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE         0x64d0
8262306a36Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN        0x64d4
8362306a36Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_RED          0x64d8
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define AVIVO_DC_LUTB_CONTROL                   0x6cc0
8662306a36Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE         0x6cc4
8762306a36Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN        0x6cc8
8862306a36Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_RED          0x6ccc
8962306a36Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE         0x6cd0
9062306a36Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN        0x6cd4
9162306a36Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_RED          0x6cd8
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci    /*
9462306a36Sopenharmony_ci     *  Set a single color register. The values supplied are already
9562306a36Sopenharmony_ci     *  rounded down to the hardware's capabilities (according to the
9662306a36Sopenharmony_ci     *  entries in the var structure). Return != 0 for invalid regno.
9762306a36Sopenharmony_ci     */
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
10062306a36Sopenharmony_ci			  u_int transp, struct fb_info *info)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct offb_par *par = (struct offb_par *) info->par;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
10562306a36Sopenharmony_ci		u32 *pal = info->pseudo_palette;
10662306a36Sopenharmony_ci		u32 cr = red >> (16 - info->var.red.length);
10762306a36Sopenharmony_ci		u32 cg = green >> (16 - info->var.green.length);
10862306a36Sopenharmony_ci		u32 cb = blue >> (16 - info->var.blue.length);
10962306a36Sopenharmony_ci		u32 value;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		if (regno >= 16)
11262306a36Sopenharmony_ci			return -EINVAL;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		value = (cr << info->var.red.offset) |
11562306a36Sopenharmony_ci			(cg << info->var.green.offset) |
11662306a36Sopenharmony_ci			(cb << info->var.blue.offset);
11762306a36Sopenharmony_ci		if (info->var.transp.length > 0) {
11862306a36Sopenharmony_ci			u32 mask = (1 << info->var.transp.length) - 1;
11962306a36Sopenharmony_ci			mask <<= info->var.transp.offset;
12062306a36Sopenharmony_ci			value |= mask;
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci		pal[regno] = value;
12362306a36Sopenharmony_ci		return 0;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (regno > 255)
12762306a36Sopenharmony_ci		return -EINVAL;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	red >>= 8;
13062306a36Sopenharmony_ci	green >>= 8;
13162306a36Sopenharmony_ci	blue >>= 8;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (!par->cmap_adr)
13462306a36Sopenharmony_ci		return 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	switch (par->cmap_type) {
13762306a36Sopenharmony_ci	case cmap_simple:
13862306a36Sopenharmony_ci		writeb(regno, par->cmap_adr);
13962306a36Sopenharmony_ci		writeb(red, par->cmap_data);
14062306a36Sopenharmony_ci		writeb(green, par->cmap_data);
14162306a36Sopenharmony_ci		writeb(blue, par->cmap_data);
14262306a36Sopenharmony_ci		break;
14362306a36Sopenharmony_ci	case cmap_M3A:
14462306a36Sopenharmony_ci		/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
14562306a36Sopenharmony_ci		out_le32(par->cmap_adr + 0x58,
14662306a36Sopenharmony_ci			 in_le32(par->cmap_adr + 0x58) & ~0x20);
14762306a36Sopenharmony_ci		fallthrough;
14862306a36Sopenharmony_ci	case cmap_r128:
14962306a36Sopenharmony_ci		/* Set palette index & data */
15062306a36Sopenharmony_ci		out_8(par->cmap_adr + 0xb0, regno);
15162306a36Sopenharmony_ci		out_le32(par->cmap_adr + 0xb4,
15262306a36Sopenharmony_ci			 (red << 16 | green << 8 | blue));
15362306a36Sopenharmony_ci		break;
15462306a36Sopenharmony_ci	case cmap_M3B:
15562306a36Sopenharmony_ci		/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
15662306a36Sopenharmony_ci		out_le32(par->cmap_adr + 0x58,
15762306a36Sopenharmony_ci			 in_le32(par->cmap_adr + 0x58) | 0x20);
15862306a36Sopenharmony_ci		/* Set palette index & data */
15962306a36Sopenharmony_ci		out_8(par->cmap_adr + 0xb0, regno);
16062306a36Sopenharmony_ci		out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
16162306a36Sopenharmony_ci		break;
16262306a36Sopenharmony_ci	case cmap_radeon:
16362306a36Sopenharmony_ci		/* Set palette index & data (could be smarter) */
16462306a36Sopenharmony_ci		out_8(par->cmap_adr + 0xb0, regno);
16562306a36Sopenharmony_ci		out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
16662306a36Sopenharmony_ci		break;
16762306a36Sopenharmony_ci	case cmap_gxt2000:
16862306a36Sopenharmony_ci		out_le32(((unsigned __iomem *) par->cmap_adr) + regno,
16962306a36Sopenharmony_ci			 (red << 16 | green << 8 | blue));
17062306a36Sopenharmony_ci		break;
17162306a36Sopenharmony_ci	case cmap_avivo:
17262306a36Sopenharmony_ci		/* Write to both LUTs for now */
17362306a36Sopenharmony_ci		writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
17462306a36Sopenharmony_ci		writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
17562306a36Sopenharmony_ci		writel(((red) << 22) | ((green) << 12) | ((blue) << 2),
17662306a36Sopenharmony_ci		       par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
17762306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
17862306a36Sopenharmony_ci		writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
17962306a36Sopenharmony_ci		writel(((red) << 22) | ((green) << 12) | ((blue) << 2),
18062306a36Sopenharmony_ci		       par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
18162306a36Sopenharmony_ci		break;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return 0;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci    /*
18862306a36Sopenharmony_ci     *  Blank the display.
18962306a36Sopenharmony_ci     */
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int offb_blank(int blank, struct fb_info *info)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct offb_par *par = (struct offb_par *) info->par;
19462306a36Sopenharmony_ci	int i, j;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (!par->cmap_adr)
19762306a36Sopenharmony_ci		return 0;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!par->blanked)
20062306a36Sopenharmony_ci		if (!blank)
20162306a36Sopenharmony_ci			return 0;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	par->blanked = blank;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (blank)
20662306a36Sopenharmony_ci		for (i = 0; i < 256; i++) {
20762306a36Sopenharmony_ci			switch (par->cmap_type) {
20862306a36Sopenharmony_ci			case cmap_simple:
20962306a36Sopenharmony_ci				writeb(i, par->cmap_adr);
21062306a36Sopenharmony_ci				for (j = 0; j < 3; j++)
21162306a36Sopenharmony_ci					writeb(0, par->cmap_data);
21262306a36Sopenharmony_ci				break;
21362306a36Sopenharmony_ci			case cmap_M3A:
21462306a36Sopenharmony_ci				/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
21562306a36Sopenharmony_ci				out_le32(par->cmap_adr + 0x58,
21662306a36Sopenharmony_ci					 in_le32(par->cmap_adr + 0x58) & ~0x20);
21762306a36Sopenharmony_ci				fallthrough;
21862306a36Sopenharmony_ci			case cmap_r128:
21962306a36Sopenharmony_ci				/* Set palette index & data */
22062306a36Sopenharmony_ci				out_8(par->cmap_adr + 0xb0, i);
22162306a36Sopenharmony_ci				out_le32(par->cmap_adr + 0xb4, 0);
22262306a36Sopenharmony_ci				break;
22362306a36Sopenharmony_ci			case cmap_M3B:
22462306a36Sopenharmony_ci				/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
22562306a36Sopenharmony_ci				out_le32(par->cmap_adr + 0x58,
22662306a36Sopenharmony_ci					 in_le32(par->cmap_adr + 0x58) | 0x20);
22762306a36Sopenharmony_ci				/* Set palette index & data */
22862306a36Sopenharmony_ci				out_8(par->cmap_adr + 0xb0, i);
22962306a36Sopenharmony_ci				out_le32(par->cmap_adr + 0xb4, 0);
23062306a36Sopenharmony_ci				break;
23162306a36Sopenharmony_ci			case cmap_radeon:
23262306a36Sopenharmony_ci				out_8(par->cmap_adr + 0xb0, i);
23362306a36Sopenharmony_ci				out_le32(par->cmap_adr + 0xb4, 0);
23462306a36Sopenharmony_ci				break;
23562306a36Sopenharmony_ci			case cmap_gxt2000:
23662306a36Sopenharmony_ci				out_le32(((unsigned __iomem *) par->cmap_adr) + i,
23762306a36Sopenharmony_ci					 0);
23862306a36Sopenharmony_ci				break;
23962306a36Sopenharmony_ci			case cmap_avivo:
24062306a36Sopenharmony_ci				writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
24162306a36Sopenharmony_ci				writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
24262306a36Sopenharmony_ci				writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
24362306a36Sopenharmony_ci				writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
24462306a36Sopenharmony_ci				writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX);
24562306a36Sopenharmony_ci				writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR);
24662306a36Sopenharmony_ci				break;
24762306a36Sopenharmony_ci			}
24862306a36Sopenharmony_ci	} else
24962306a36Sopenharmony_ci		fb_set_cmap(&info->cmap, info);
25062306a36Sopenharmony_ci	return 0;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic int offb_set_par(struct fb_info *info)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct offb_par *par = (struct offb_par *) info->par;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* On avivo, initialize palette control */
25862306a36Sopenharmony_ci	if (par->cmap_type == cmap_avivo) {
25962306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTA_CONTROL);
26062306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_BLUE);
26162306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_GREEN);
26262306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_RED);
26362306a36Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_BLUE);
26462306a36Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_GREEN);
26562306a36Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_RED);
26662306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTB_CONTROL);
26762306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_BLUE);
26862306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_GREEN);
26962306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_RED);
27062306a36Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_BLUE);
27162306a36Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_GREEN);
27262306a36Sopenharmony_ci		writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_RED);
27362306a36Sopenharmony_ci		writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
27462306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE);
27562306a36Sopenharmony_ci		writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK);
27662306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT);
27762306a36Sopenharmony_ci		writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE);
27862306a36Sopenharmony_ci		writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK);
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci	return 0;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic void offb_destroy(struct fb_info *info)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct offb_par *par = info->par;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (info->screen_base)
28862306a36Sopenharmony_ci		iounmap(info->screen_base);
28962306a36Sopenharmony_ci	release_mem_region(par->base, par->size);
29062306a36Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
29162306a36Sopenharmony_ci	framebuffer_release(info);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic const struct fb_ops offb_ops = {
29562306a36Sopenharmony_ci	.owner		= THIS_MODULE,
29662306a36Sopenharmony_ci	FB_DEFAULT_IOMEM_OPS,
29762306a36Sopenharmony_ci	.fb_destroy	= offb_destroy,
29862306a36Sopenharmony_ci	.fb_setcolreg	= offb_setcolreg,
29962306a36Sopenharmony_ci	.fb_set_par	= offb_set_par,
30062306a36Sopenharmony_ci	.fb_blank	= offb_blank,
30162306a36Sopenharmony_ci};
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic void __iomem *offb_map_reg(struct device_node *np, int index,
30462306a36Sopenharmony_ci				  unsigned long offset, unsigned long size)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	const __be32 *addrp;
30762306a36Sopenharmony_ci	u64 asize, taddr;
30862306a36Sopenharmony_ci	unsigned int flags;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	addrp = of_get_pci_address(np, index, &asize, &flags);
31162306a36Sopenharmony_ci	if (addrp == NULL)
31262306a36Sopenharmony_ci		addrp = of_get_address(np, index, &asize, &flags);
31362306a36Sopenharmony_ci	if (addrp == NULL)
31462306a36Sopenharmony_ci		return NULL;
31562306a36Sopenharmony_ci	if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
31662306a36Sopenharmony_ci		return NULL;
31762306a36Sopenharmony_ci	if ((offset + size) > asize)
31862306a36Sopenharmony_ci		return NULL;
31962306a36Sopenharmony_ci	taddr = of_translate_address(np, addrp);
32062306a36Sopenharmony_ci	if (taddr == OF_BAD_ADDR)
32162306a36Sopenharmony_ci		return NULL;
32262306a36Sopenharmony_ci	return ioremap(taddr + offset, size);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void offb_init_palette_hacks(struct fb_info *info, struct device_node *dp,
32662306a36Sopenharmony_ci				    unsigned long address)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct offb_par *par = (struct offb_par *) info->par;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (of_node_name_prefix(dp, "ATY,Rage128")) {
33162306a36Sopenharmony_ci		par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
33262306a36Sopenharmony_ci		if (par->cmap_adr)
33362306a36Sopenharmony_ci			par->cmap_type = cmap_r128;
33462306a36Sopenharmony_ci	} else if (of_node_name_prefix(dp, "ATY,RageM3pA") ||
33562306a36Sopenharmony_ci		   of_node_name_prefix(dp, "ATY,RageM3p12A")) {
33662306a36Sopenharmony_ci		par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
33762306a36Sopenharmony_ci		if (par->cmap_adr)
33862306a36Sopenharmony_ci			par->cmap_type = cmap_M3A;
33962306a36Sopenharmony_ci	} else if (of_node_name_prefix(dp, "ATY,RageM3pB")) {
34062306a36Sopenharmony_ci		par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
34162306a36Sopenharmony_ci		if (par->cmap_adr)
34262306a36Sopenharmony_ci			par->cmap_type = cmap_M3B;
34362306a36Sopenharmony_ci	} else if (of_node_name_prefix(dp, "ATY,Rage6")) {
34462306a36Sopenharmony_ci		par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff);
34562306a36Sopenharmony_ci		if (par->cmap_adr)
34662306a36Sopenharmony_ci			par->cmap_type = cmap_radeon;
34762306a36Sopenharmony_ci	} else if (of_node_name_prefix(dp, "ATY,")) {
34862306a36Sopenharmony_ci		unsigned long base = address & 0xff000000UL;
34962306a36Sopenharmony_ci		par->cmap_adr =
35062306a36Sopenharmony_ci			ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
35162306a36Sopenharmony_ci		par->cmap_data = par->cmap_adr + 1;
35262306a36Sopenharmony_ci		par->cmap_type = cmap_simple;
35362306a36Sopenharmony_ci	} else if (dp && (of_device_is_compatible(dp, "pci1014,b7") ||
35462306a36Sopenharmony_ci			  of_device_is_compatible(dp, "pci1014,21c"))) {
35562306a36Sopenharmony_ci		par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000);
35662306a36Sopenharmony_ci		if (par->cmap_adr)
35762306a36Sopenharmony_ci			par->cmap_type = cmap_gxt2000;
35862306a36Sopenharmony_ci	} else if (of_node_name_prefix(dp, "vga,Display-")) {
35962306a36Sopenharmony_ci		/* Look for AVIVO initialized by SLOF */
36062306a36Sopenharmony_ci		struct device_node *pciparent = of_get_parent(dp);
36162306a36Sopenharmony_ci		const u32 *vid, *did;
36262306a36Sopenharmony_ci		vid = of_get_property(pciparent, "vendor-id", NULL);
36362306a36Sopenharmony_ci		did = of_get_property(pciparent, "device-id", NULL);
36462306a36Sopenharmony_ci		/* This will match most R5xx */
36562306a36Sopenharmony_ci		if (vid && did && *vid == 0x1002 &&
36662306a36Sopenharmony_ci		    ((*did >= 0x7100 && *did < 0x7800) ||
36762306a36Sopenharmony_ci		     (*did >= 0x9400))) {
36862306a36Sopenharmony_ci			par->cmap_adr = offb_map_reg(pciparent, 2, 0, 0x10000);
36962306a36Sopenharmony_ci			if (par->cmap_adr)
37062306a36Sopenharmony_ci				par->cmap_type = cmap_avivo;
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci		of_node_put(pciparent);
37362306a36Sopenharmony_ci	} else if (dp && of_device_is_compatible(dp, "qemu,std-vga")) {
37462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
37562306a36Sopenharmony_ci		const __be32 io_of_addr[3] = { 0x01000000, 0x0, 0x0 };
37662306a36Sopenharmony_ci#else
37762306a36Sopenharmony_ci		const __be32 io_of_addr[3] = { 0x00000001, 0x0, 0x0 };
37862306a36Sopenharmony_ci#endif
37962306a36Sopenharmony_ci		u64 io_addr = of_translate_address(dp, io_of_addr);
38062306a36Sopenharmony_ci		if (io_addr != OF_BAD_ADDR) {
38162306a36Sopenharmony_ci			par->cmap_adr = ioremap(io_addr + 0x3c8, 2);
38262306a36Sopenharmony_ci			if (par->cmap_adr) {
38362306a36Sopenharmony_ci				par->cmap_type = cmap_simple;
38462306a36Sopenharmony_ci				par->cmap_data = par->cmap_adr + 1;
38562306a36Sopenharmony_ci			}
38662306a36Sopenharmony_ci		}
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci	info->fix.visual = (par->cmap_type != cmap_unknown) ?
38962306a36Sopenharmony_ci		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic void offb_init_fb(struct platform_device *parent, const char *name,
39362306a36Sopenharmony_ci			 int width, int height, int depth,
39462306a36Sopenharmony_ci			 int pitch, unsigned long address,
39562306a36Sopenharmony_ci			 int foreign_endian, struct device_node *dp)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	unsigned long res_size = pitch * height;
39862306a36Sopenharmony_ci	unsigned long res_start = address;
39962306a36Sopenharmony_ci	struct fb_fix_screeninfo *fix;
40062306a36Sopenharmony_ci	struct fb_var_screeninfo *var;
40162306a36Sopenharmony_ci	struct fb_info *info;
40262306a36Sopenharmony_ci	struct offb_par *par;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (!request_mem_region(res_start, res_size, "offb"))
40562306a36Sopenharmony_ci		return;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	printk(KERN_INFO
40862306a36Sopenharmony_ci	       "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",
40962306a36Sopenharmony_ci	       width, height, name, address, depth, pitch);
41062306a36Sopenharmony_ci	if (depth != 8 && depth != 15 && depth != 16 && depth != 32) {
41162306a36Sopenharmony_ci		printk(KERN_ERR "%pOF: can't use depth = %d\n", dp, depth);
41262306a36Sopenharmony_ci		release_mem_region(res_start, res_size);
41362306a36Sopenharmony_ci		return;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(*par), &parent->dev);
41762306a36Sopenharmony_ci	if (!info) {
41862306a36Sopenharmony_ci		release_mem_region(res_start, res_size);
41962306a36Sopenharmony_ci		return;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci	platform_set_drvdata(parent, info);
42262306a36Sopenharmony_ci	par = info->par;
42362306a36Sopenharmony_ci	fix = &info->fix;
42462306a36Sopenharmony_ci	var = &info->var;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (name) {
42762306a36Sopenharmony_ci		strcpy(fix->id, "OFfb ");
42862306a36Sopenharmony_ci		strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb "));
42962306a36Sopenharmony_ci		fix->id[sizeof(fix->id) - 1] = '\0';
43062306a36Sopenharmony_ci	} else
43162306a36Sopenharmony_ci		snprintf(fix->id, sizeof(fix->id), "OFfb %pOFn", dp);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	var->xres = var->xres_virtual = width;
43562306a36Sopenharmony_ci	var->yres = var->yres_virtual = height;
43662306a36Sopenharmony_ci	fix->line_length = pitch;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	fix->smem_start = address;
43962306a36Sopenharmony_ci	fix->smem_len = pitch * height;
44062306a36Sopenharmony_ci	fix->type = FB_TYPE_PACKED_PIXELS;
44162306a36Sopenharmony_ci	fix->type_aux = 0;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	par->cmap_type = cmap_unknown;
44462306a36Sopenharmony_ci	if (depth == 8)
44562306a36Sopenharmony_ci		offb_init_palette_hacks(info, dp, address);
44662306a36Sopenharmony_ci	else
44762306a36Sopenharmony_ci		fix->visual = FB_VISUAL_TRUECOLOR;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	var->xoffset = var->yoffset = 0;
45062306a36Sopenharmony_ci	switch (depth) {
45162306a36Sopenharmony_ci	case 8:
45262306a36Sopenharmony_ci		var->bits_per_pixel = 8;
45362306a36Sopenharmony_ci		var->red.offset = 0;
45462306a36Sopenharmony_ci		var->red.length = 8;
45562306a36Sopenharmony_ci		var->green.offset = 0;
45662306a36Sopenharmony_ci		var->green.length = 8;
45762306a36Sopenharmony_ci		var->blue.offset = 0;
45862306a36Sopenharmony_ci		var->blue.length = 8;
45962306a36Sopenharmony_ci		var->transp.offset = 0;
46062306a36Sopenharmony_ci		var->transp.length = 0;
46162306a36Sopenharmony_ci		break;
46262306a36Sopenharmony_ci	case 15:		/* RGB 555 */
46362306a36Sopenharmony_ci		var->bits_per_pixel = 16;
46462306a36Sopenharmony_ci		var->red.offset = 10;
46562306a36Sopenharmony_ci		var->red.length = 5;
46662306a36Sopenharmony_ci		var->green.offset = 5;
46762306a36Sopenharmony_ci		var->green.length = 5;
46862306a36Sopenharmony_ci		var->blue.offset = 0;
46962306a36Sopenharmony_ci		var->blue.length = 5;
47062306a36Sopenharmony_ci		var->transp.offset = 0;
47162306a36Sopenharmony_ci		var->transp.length = 0;
47262306a36Sopenharmony_ci		break;
47362306a36Sopenharmony_ci	case 16:		/* RGB 565 */
47462306a36Sopenharmony_ci		var->bits_per_pixel = 16;
47562306a36Sopenharmony_ci		var->red.offset = 11;
47662306a36Sopenharmony_ci		var->red.length = 5;
47762306a36Sopenharmony_ci		var->green.offset = 5;
47862306a36Sopenharmony_ci		var->green.length = 6;
47962306a36Sopenharmony_ci		var->blue.offset = 0;
48062306a36Sopenharmony_ci		var->blue.length = 5;
48162306a36Sopenharmony_ci		var->transp.offset = 0;
48262306a36Sopenharmony_ci		var->transp.length = 0;
48362306a36Sopenharmony_ci		break;
48462306a36Sopenharmony_ci	case 32:		/* RGB 888 */
48562306a36Sopenharmony_ci		var->bits_per_pixel = 32;
48662306a36Sopenharmony_ci		var->red.offset = 16;
48762306a36Sopenharmony_ci		var->red.length = 8;
48862306a36Sopenharmony_ci		var->green.offset = 8;
48962306a36Sopenharmony_ci		var->green.length = 8;
49062306a36Sopenharmony_ci		var->blue.offset = 0;
49162306a36Sopenharmony_ci		var->blue.length = 8;
49262306a36Sopenharmony_ci		var->transp.offset = 24;
49362306a36Sopenharmony_ci		var->transp.length = 8;
49462306a36Sopenharmony_ci		break;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci	var->red.msb_right = var->green.msb_right = var->blue.msb_right =
49762306a36Sopenharmony_ci	    var->transp.msb_right = 0;
49862306a36Sopenharmony_ci	var->grayscale = 0;
49962306a36Sopenharmony_ci	var->nonstd = 0;
50062306a36Sopenharmony_ci	var->activate = 0;
50162306a36Sopenharmony_ci	var->height = var->width = -1;
50262306a36Sopenharmony_ci	var->pixclock = 10000;
50362306a36Sopenharmony_ci	var->left_margin = var->right_margin = 16;
50462306a36Sopenharmony_ci	var->upper_margin = var->lower_margin = 16;
50562306a36Sopenharmony_ci	var->hsync_len = var->vsync_len = 8;
50662306a36Sopenharmony_ci	var->sync = 0;
50762306a36Sopenharmony_ci	var->vmode = FB_VMODE_NONINTERLACED;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	par->base = address;
51062306a36Sopenharmony_ci	par->size = fix->smem_len;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	info->fbops = &offb_ops;
51362306a36Sopenharmony_ci	info->screen_base = ioremap(address, fix->smem_len);
51462306a36Sopenharmony_ci	info->pseudo_palette = par->pseudo_palette;
51562306a36Sopenharmony_ci	info->flags = foreign_endian;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	fb_alloc_cmap(&info->cmap, 256, 0);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (devm_aperture_acquire_for_platform_device(parent, par->base, par->size) < 0)
52062306a36Sopenharmony_ci		goto out_err;
52162306a36Sopenharmony_ci	if (register_framebuffer(info) < 0)
52262306a36Sopenharmony_ci		goto out_err;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	fb_info(info, "Open Firmware frame buffer device on %pOF\n", dp);
52562306a36Sopenharmony_ci	return;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ciout_err:
52862306a36Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
52962306a36Sopenharmony_ci	iounmap(info->screen_base);
53062306a36Sopenharmony_ci	iounmap(par->cmap_adr);
53162306a36Sopenharmony_ci	par->cmap_adr = NULL;
53262306a36Sopenharmony_ci	framebuffer_release(info);
53362306a36Sopenharmony_ci	release_mem_region(res_start, res_size);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic void offb_init_nodriver(struct platform_device *parent, struct device_node *dp,
53862306a36Sopenharmony_ci			       int no_real_node)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	unsigned int len;
54162306a36Sopenharmony_ci	int i, width = 640, height = 480, depth = 8, pitch = 640;
54262306a36Sopenharmony_ci	unsigned int flags, rsize, addr_prop = 0;
54362306a36Sopenharmony_ci	unsigned long max_size = 0;
54462306a36Sopenharmony_ci	u64 rstart, address = OF_BAD_ADDR;
54562306a36Sopenharmony_ci	const __be32 *pp, *addrp, *up;
54662306a36Sopenharmony_ci	u64 asize;
54762306a36Sopenharmony_ci	int foreign_endian = 0;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
55062306a36Sopenharmony_ci	if (of_property_read_bool(dp, "little-endian"))
55162306a36Sopenharmony_ci		foreign_endian = FBINFO_FOREIGN_ENDIAN;
55262306a36Sopenharmony_ci#else
55362306a36Sopenharmony_ci	if (of_property_read_bool(dp, "big-endian"))
55462306a36Sopenharmony_ci		foreign_endian = FBINFO_FOREIGN_ENDIAN;
55562306a36Sopenharmony_ci#endif
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	pp = of_get_property(dp, "linux,bootx-depth", &len);
55862306a36Sopenharmony_ci	if (pp == NULL)
55962306a36Sopenharmony_ci		pp = of_get_property(dp, "depth", &len);
56062306a36Sopenharmony_ci	if (pp && len == sizeof(u32))
56162306a36Sopenharmony_ci		depth = be32_to_cpup(pp);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	pp = of_get_property(dp, "linux,bootx-width", &len);
56462306a36Sopenharmony_ci	if (pp == NULL)
56562306a36Sopenharmony_ci		pp = of_get_property(dp, "width", &len);
56662306a36Sopenharmony_ci	if (pp && len == sizeof(u32))
56762306a36Sopenharmony_ci		width = be32_to_cpup(pp);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	pp = of_get_property(dp, "linux,bootx-height", &len);
57062306a36Sopenharmony_ci	if (pp == NULL)
57162306a36Sopenharmony_ci		pp = of_get_property(dp, "height", &len);
57262306a36Sopenharmony_ci	if (pp && len == sizeof(u32))
57362306a36Sopenharmony_ci		height = be32_to_cpup(pp);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	pp = of_get_property(dp, "linux,bootx-linebytes", &len);
57662306a36Sopenharmony_ci	if (pp == NULL)
57762306a36Sopenharmony_ci		pp = of_get_property(dp, "linebytes", &len);
57862306a36Sopenharmony_ci	if (pp && len == sizeof(u32) && (*pp != 0xffffffffu))
57962306a36Sopenharmony_ci		pitch = be32_to_cpup(pp);
58062306a36Sopenharmony_ci	else
58162306a36Sopenharmony_ci		pitch = width * ((depth + 7) / 8);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	rsize = (unsigned long)pitch * (unsigned long)height;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* Ok, now we try to figure out the address of the framebuffer.
58662306a36Sopenharmony_ci	 *
58762306a36Sopenharmony_ci	 * Unfortunately, Open Firmware doesn't provide a standard way to do
58862306a36Sopenharmony_ci	 * so. All we can do is a dodgy heuristic that happens to work in
58962306a36Sopenharmony_ci	 * practice. On most machines, the "address" property contains what
59062306a36Sopenharmony_ci	 * we need, though not on Matrox cards found in IBM machines. What I've
59162306a36Sopenharmony_ci	 * found that appears to give good results is to go through the PCI
59262306a36Sopenharmony_ci	 * ranges and pick one that is both big enough and if possible encloses
59362306a36Sopenharmony_ci	 * the "address" property. If none match, we pick the biggest
59462306a36Sopenharmony_ci	 */
59562306a36Sopenharmony_ci	up = of_get_property(dp, "linux,bootx-addr", &len);
59662306a36Sopenharmony_ci	if (up == NULL)
59762306a36Sopenharmony_ci		up = of_get_property(dp, "address", &len);
59862306a36Sopenharmony_ci	if (up && len == sizeof(u32))
59962306a36Sopenharmony_ci		addr_prop = *up;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/* Hack for when BootX is passing us */
60262306a36Sopenharmony_ci	if (no_real_node)
60362306a36Sopenharmony_ci		goto skip_addr;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags))
60662306a36Sopenharmony_ci		     != NULL; i++) {
60762306a36Sopenharmony_ci		int match_addrp = 0;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		if (!(flags & IORESOURCE_MEM))
61062306a36Sopenharmony_ci			continue;
61162306a36Sopenharmony_ci		if (asize < rsize)
61262306a36Sopenharmony_ci			continue;
61362306a36Sopenharmony_ci		rstart = of_translate_address(dp, addrp);
61462306a36Sopenharmony_ci		if (rstart == OF_BAD_ADDR)
61562306a36Sopenharmony_ci			continue;
61662306a36Sopenharmony_ci		if (addr_prop && (rstart <= addr_prop) &&
61762306a36Sopenharmony_ci		    ((rstart + asize) >= (addr_prop + rsize)))
61862306a36Sopenharmony_ci			match_addrp = 1;
61962306a36Sopenharmony_ci		if (match_addrp) {
62062306a36Sopenharmony_ci			address = addr_prop;
62162306a36Sopenharmony_ci			break;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci		if (rsize > max_size) {
62462306a36Sopenharmony_ci			max_size = rsize;
62562306a36Sopenharmony_ci			address = OF_BAD_ADDR;
62662306a36Sopenharmony_ci 		}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		if (address == OF_BAD_ADDR)
62962306a36Sopenharmony_ci			address = rstart;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci skip_addr:
63262306a36Sopenharmony_ci	if (address == OF_BAD_ADDR && addr_prop)
63362306a36Sopenharmony_ci		address = (u64)addr_prop;
63462306a36Sopenharmony_ci	if (address != OF_BAD_ADDR) {
63562306a36Sopenharmony_ci#ifdef CONFIG_PCI
63662306a36Sopenharmony_ci		const __be32 *vidp, *didp;
63762306a36Sopenharmony_ci		u32 vid, did;
63862306a36Sopenharmony_ci		struct pci_dev *pdev;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		vidp = of_get_property(dp, "vendor-id", NULL);
64162306a36Sopenharmony_ci		didp = of_get_property(dp, "device-id", NULL);
64262306a36Sopenharmony_ci		if (vidp && didp) {
64362306a36Sopenharmony_ci			vid = be32_to_cpup(vidp);
64462306a36Sopenharmony_ci			did = be32_to_cpup(didp);
64562306a36Sopenharmony_ci			pdev = pci_get_device(vid, did, NULL);
64662306a36Sopenharmony_ci			if (!pdev || pci_enable_device(pdev))
64762306a36Sopenharmony_ci				return;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci#endif
65062306a36Sopenharmony_ci		/* kludge for valkyrie */
65162306a36Sopenharmony_ci		if (of_node_name_eq(dp, "valkyrie"))
65262306a36Sopenharmony_ci			address += 0x1000;
65362306a36Sopenharmony_ci		offb_init_fb(parent, no_real_node ? "bootx" : NULL,
65462306a36Sopenharmony_ci			     width, height, depth, pitch, address,
65562306a36Sopenharmony_ci			     foreign_endian, no_real_node ? NULL : dp);
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic void offb_remove(struct platform_device *pdev)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	struct fb_info *info = platform_get_drvdata(pdev);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (info)
66462306a36Sopenharmony_ci		unregister_framebuffer(info);
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic int offb_probe_bootx_noscreen(struct platform_device *pdev)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	offb_init_nodriver(pdev, of_chosen, 1);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	return 0;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic struct platform_driver offb_driver_bootx_noscreen = {
67562306a36Sopenharmony_ci	.driver = {
67662306a36Sopenharmony_ci		.name = "bootx-noscreen",
67762306a36Sopenharmony_ci	},
67862306a36Sopenharmony_ci	.probe = offb_probe_bootx_noscreen,
67962306a36Sopenharmony_ci	.remove_new = offb_remove,
68062306a36Sopenharmony_ci};
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic int offb_probe_display(struct platform_device *pdev)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	offb_init_nodriver(pdev, pdev->dev.of_node, 0);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic const struct of_device_id offb_of_match_display[] = {
69062306a36Sopenharmony_ci	{ .compatible = "display", },
69162306a36Sopenharmony_ci	{ },
69262306a36Sopenharmony_ci};
69362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, offb_of_match_display);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic struct platform_driver offb_driver_display = {
69662306a36Sopenharmony_ci	.driver = {
69762306a36Sopenharmony_ci		.name = "of-display",
69862306a36Sopenharmony_ci		.of_match_table = offb_of_match_display,
69962306a36Sopenharmony_ci	},
70062306a36Sopenharmony_ci	.probe = offb_probe_display,
70162306a36Sopenharmony_ci	.remove_new = offb_remove,
70262306a36Sopenharmony_ci};
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int __init offb_init(void)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	if (fb_get_options("offb", NULL))
70762306a36Sopenharmony_ci		return -ENODEV;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	platform_driver_register(&offb_driver_bootx_noscreen);
71062306a36Sopenharmony_ci	platform_driver_register(&offb_driver_display);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return 0;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_cimodule_init(offb_init);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic void __exit offb_exit(void)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	platform_driver_unregister(&offb_driver_display);
71962306a36Sopenharmony_ci	platform_driver_unregister(&offb_driver_bootx_noscreen);
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_cimodule_exit(offb_exit);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
724