162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  platinumfb.c -- frame buffer device for the PowerMac 'platinum' display
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Copyright (C) 1998 Franz Sirl
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Frame buffer structure from:
762306a36Sopenharmony_ci *    drivers/video/controlfb.c -- frame buffer device for
862306a36Sopenharmony_ci *    Apple 'control' display chip.
962306a36Sopenharmony_ci *    Copyright (C) 1998 Dan Jacobowitz
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *  Hardware information from:
1262306a36Sopenharmony_ci *    platinum.c: Console support for PowerMac "platinum" display adaptor.
1362306a36Sopenharmony_ci *    Copyright (C) 1996 Paul Mackerras and Mark Abene
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
1662306a36Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
1762306a36Sopenharmony_ci *  more details.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#undef DEBUG
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/kernel.h>
2462306a36Sopenharmony_ci#include <linux/errno.h>
2562306a36Sopenharmony_ci#include <linux/string.h>
2662306a36Sopenharmony_ci#include <linux/mm.h>
2762306a36Sopenharmony_ci#include <linux/vmalloc.h>
2862306a36Sopenharmony_ci#include <linux/delay.h>
2962306a36Sopenharmony_ci#include <linux/interrupt.h>
3062306a36Sopenharmony_ci#include <linux/fb.h>
3162306a36Sopenharmony_ci#include <linux/init.h>
3262306a36Sopenharmony_ci#include <linux/nvram.h>
3362306a36Sopenharmony_ci#include <linux/of.h>
3462306a36Sopenharmony_ci#include <linux/of_address.h>
3562306a36Sopenharmony_ci#include <linux/platform_device.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "macmodes.h"
3862306a36Sopenharmony_ci#include "platinumfb.h"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int default_vmode = VMODE_NVRAM;
4162306a36Sopenharmony_cistatic int default_cmode = CMODE_NVRAM;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct fb_info_platinum {
4462306a36Sopenharmony_ci	struct fb_info			*info;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	int				vmode, cmode;
4762306a36Sopenharmony_ci	int				xres, yres;
4862306a36Sopenharmony_ci	int				vxres, vyres;
4962306a36Sopenharmony_ci	int				xoffset, yoffset;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	struct {
5262306a36Sopenharmony_ci		__u8 red, green, blue;
5362306a36Sopenharmony_ci	}				palette[256];
5462306a36Sopenharmony_ci	u32				pseudo_palette[16];
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	volatile struct cmap_regs	__iomem *cmap_regs;
5762306a36Sopenharmony_ci	unsigned long			cmap_regs_phys;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	volatile struct platinum_regs	__iomem *platinum_regs;
6062306a36Sopenharmony_ci	unsigned long			platinum_regs_phys;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	__u8				__iomem *frame_buffer;
6362306a36Sopenharmony_ci	volatile __u8			__iomem *base_frame_buffer;
6462306a36Sopenharmony_ci	unsigned long			frame_buffer_phys;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	unsigned long			total_vram;
6762306a36Sopenharmony_ci	int				clktype;
6862306a36Sopenharmony_ci	int				dactype;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	struct resource			rsrc_fb, rsrc_reg;
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/*
7462306a36Sopenharmony_ci * Frame buffer device API
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
7862306a36Sopenharmony_ci	u_int transp, struct fb_info *info);
7962306a36Sopenharmony_cistatic int platinumfb_blank(int blank_mode, struct fb_info *info);
8062306a36Sopenharmony_cistatic int platinumfb_set_par (struct fb_info *info);
8162306a36Sopenharmony_cistatic int platinumfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * internal functions
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline int platinum_vram_reqd(int video_mode, int color_mode);
8862306a36Sopenharmony_cistatic int read_platinum_sense(struct fb_info_platinum *pinfo);
8962306a36Sopenharmony_cistatic void set_platinum_clock(struct fb_info_platinum *pinfo);
9062306a36Sopenharmony_cistatic void platinum_set_hardware(struct fb_info_platinum *pinfo);
9162306a36Sopenharmony_cistatic int platinum_var_to_par(struct fb_var_screeninfo *var,
9262306a36Sopenharmony_ci			       struct fb_info_platinum *pinfo,
9362306a36Sopenharmony_ci			       int check_only);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/*
9662306a36Sopenharmony_ci * Interface used by the world
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const struct fb_ops platinumfb_ops = {
10062306a36Sopenharmony_ci	.owner =	THIS_MODULE,
10162306a36Sopenharmony_ci	FB_DEFAULT_IOMEM_OPS,
10262306a36Sopenharmony_ci	.fb_check_var	= platinumfb_check_var,
10362306a36Sopenharmony_ci	.fb_set_par	= platinumfb_set_par,
10462306a36Sopenharmony_ci	.fb_setcolreg	= platinumfb_setcolreg,
10562306a36Sopenharmony_ci	.fb_blank	= platinumfb_blank,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * Checks a var structure
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic int platinumfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	return platinum_var_to_par(var, info->par, 1);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/*
11762306a36Sopenharmony_ci * Applies current var to display
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_cistatic int platinumfb_set_par (struct fb_info *info)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct fb_info_platinum *pinfo = info->par;
12262306a36Sopenharmony_ci	struct platinum_regvals *init;
12362306a36Sopenharmony_ci	int err, offset = 0x20;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if((err = platinum_var_to_par(&info->var, pinfo, 0))) {
12662306a36Sopenharmony_ci		printk (KERN_ERR "platinumfb_set_par: error calling"
12762306a36Sopenharmony_ci				 " platinum_var_to_par: %d.\n", err);
12862306a36Sopenharmony_ci		return err;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	platinum_set_hardware(pinfo);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	init = platinum_reg_init[pinfo->vmode-1];
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci 	if ((pinfo->vmode == VMODE_832_624_75) && (pinfo->cmode > CMODE_8))
13662306a36Sopenharmony_ci  		offset = 0x10;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	info->screen_base = pinfo->frame_buffer + init->fb_offset + offset;
13962306a36Sopenharmony_ci	mutex_lock(&info->mm_lock);
14062306a36Sopenharmony_ci	info->fix.smem_start = (pinfo->frame_buffer_phys) + init->fb_offset + offset;
14162306a36Sopenharmony_ci	mutex_unlock(&info->mm_lock);
14262306a36Sopenharmony_ci	info->fix.visual = (pinfo->cmode == CMODE_8) ?
14362306a36Sopenharmony_ci		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
14462306a36Sopenharmony_ci 	info->fix.line_length = vmode_attrs[pinfo->vmode-1].hres * (1<<pinfo->cmode)
14562306a36Sopenharmony_ci		+ offset;
14662306a36Sopenharmony_ci	printk("line_length: %x\n", info->fix.line_length);
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int platinumfb_blank(int blank,  struct fb_info *fb)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci/*
15362306a36Sopenharmony_ci *  Blank the screen if blank_mode != 0, else unblank. If blank == NULL
15462306a36Sopenharmony_ci *  then the caller blanks by setting the CLUT (Color Look Up Table) to all
15562306a36Sopenharmony_ci *  black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
15662306a36Sopenharmony_ci *  to e.g. a video mode which doesn't support it. Implements VESA suspend
15762306a36Sopenharmony_ci *  and powerdown modes on hardware that supports disabling hsync/vsync:
15862306a36Sopenharmony_ci *    blank_mode == 2: suspend vsync
15962306a36Sopenharmony_ci *    blank_mode == 3: suspend hsync
16062306a36Sopenharmony_ci *    blank_mode == 4: powerdown
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_ci/* [danj] I think there's something fishy about those constants... */
16362306a36Sopenharmony_ci/*
16462306a36Sopenharmony_ci	struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
16562306a36Sopenharmony_ci	int	ctrl;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	ctrl = le32_to_cpup(&info->platinum_regs->ctrl.r) | 0x33;
16862306a36Sopenharmony_ci	if (blank)
16962306a36Sopenharmony_ci		--blank_mode;
17062306a36Sopenharmony_ci	if (blank & VESA_VSYNC_SUSPEND)
17162306a36Sopenharmony_ci		ctrl &= ~3;
17262306a36Sopenharmony_ci	if (blank & VESA_HSYNC_SUSPEND)
17362306a36Sopenharmony_ci		ctrl &= ~0x30;
17462306a36Sopenharmony_ci	out_le32(&info->platinum_regs->ctrl.r, ctrl);
17562306a36Sopenharmony_ci*/
17662306a36Sopenharmony_ci/* TODO: Figure out how the heck to powerdown this thing! */
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
18162306a36Sopenharmony_ci			      u_int transp, struct fb_info *info)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct fb_info_platinum *pinfo = info->par;
18462306a36Sopenharmony_ci	volatile struct cmap_regs __iomem *cmap_regs = pinfo->cmap_regs;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (regno > 255)
18762306a36Sopenharmony_ci		return 1;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	red >>= 8;
19062306a36Sopenharmony_ci	green >>= 8;
19162306a36Sopenharmony_ci	blue >>= 8;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	pinfo->palette[regno].red = red;
19462306a36Sopenharmony_ci	pinfo->palette[regno].green = green;
19562306a36Sopenharmony_ci	pinfo->palette[regno].blue = blue;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	out_8(&cmap_regs->addr, regno);		/* tell clut what addr to fill	*/
19862306a36Sopenharmony_ci	out_8(&cmap_regs->lut, red);		/* send one color channel at	*/
19962306a36Sopenharmony_ci	out_8(&cmap_regs->lut, green);		/* a time...			*/
20062306a36Sopenharmony_ci	out_8(&cmap_regs->lut, blue);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (regno < 16) {
20362306a36Sopenharmony_ci		int i;
20462306a36Sopenharmony_ci		u32 *pal = info->pseudo_palette;
20562306a36Sopenharmony_ci		switch (pinfo->cmode) {
20662306a36Sopenharmony_ci		case CMODE_16:
20762306a36Sopenharmony_ci			pal[regno] = (regno << 10) | (regno << 5) | regno;
20862306a36Sopenharmony_ci			break;
20962306a36Sopenharmony_ci		case CMODE_32:
21062306a36Sopenharmony_ci			i = (regno << 8) | regno;
21162306a36Sopenharmony_ci			pal[regno] = (i << 16) | i;
21262306a36Sopenharmony_ci			break;
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return 0;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic inline int platinum_vram_reqd(int video_mode, int color_mode)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	int baseval = vmode_attrs[video_mode-1].hres * (1<<color_mode);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if ((video_mode == VMODE_832_624_75) && (color_mode > CMODE_8))
22462306a36Sopenharmony_ci		baseval += 0x10;
22562306a36Sopenharmony_ci	else
22662306a36Sopenharmony_ci		baseval += 0x20;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return vmode_attrs[video_mode-1].vres * baseval + 0x1000;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci#define STORE_D2(a, d) { \
23262306a36Sopenharmony_ci	out_8(&cmap_regs->addr, (a+32)); \
23362306a36Sopenharmony_ci	out_8(&cmap_regs->d2, (d)); \
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic void set_platinum_clock(struct fb_info_platinum *pinfo)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	volatile struct cmap_regs __iomem *cmap_regs = pinfo->cmap_regs;
23962306a36Sopenharmony_ci	struct platinum_regvals	*init;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	init = platinum_reg_init[pinfo->vmode-1];
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	STORE_D2(6, 0xc6);
24462306a36Sopenharmony_ci	out_8(&cmap_regs->addr,3+32);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (in_8(&cmap_regs->d2) == 2) {
24762306a36Sopenharmony_ci		STORE_D2(7, init->clock_params[pinfo->clktype][0]);
24862306a36Sopenharmony_ci		STORE_D2(8, init->clock_params[pinfo->clktype][1]);
24962306a36Sopenharmony_ci		STORE_D2(3, 3);
25062306a36Sopenharmony_ci	} else {
25162306a36Sopenharmony_ci		STORE_D2(4, init->clock_params[pinfo->clktype][0]);
25262306a36Sopenharmony_ci		STORE_D2(5, init->clock_params[pinfo->clktype][1]);
25362306a36Sopenharmony_ci		STORE_D2(3, 2);
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	__delay(5000);
25762306a36Sopenharmony_ci	STORE_D2(9, 0xa6);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci/* Now how about actually saying, Make it so! */
26262306a36Sopenharmony_ci/* Some things in here probably don't need to be done each time. */
26362306a36Sopenharmony_cistatic void platinum_set_hardware(struct fb_info_platinum *pinfo)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	volatile struct platinum_regs	__iomem *platinum_regs = pinfo->platinum_regs;
26662306a36Sopenharmony_ci	volatile struct cmap_regs	__iomem *cmap_regs = pinfo->cmap_regs;
26762306a36Sopenharmony_ci	struct platinum_regvals		*init;
26862306a36Sopenharmony_ci	int				i;
26962306a36Sopenharmony_ci	int				vmode, cmode;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	vmode = pinfo->vmode;
27262306a36Sopenharmony_ci	cmode = pinfo->cmode;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	init = platinum_reg_init[vmode - 1];
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* Initialize display timing registers */
27762306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[24].r, 7);	/* turn display off */
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	for (i = 0; i < 26; ++i)
28062306a36Sopenharmony_ci		out_be32(&platinum_regs->reg[i+32].r, init->regs[i]);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[26+32].r, (pinfo->total_vram == 0x100000 ?
28362306a36Sopenharmony_ci						init->offset[cmode] + 4 - cmode :
28462306a36Sopenharmony_ci						init->offset[cmode]));
28562306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[16].r, (unsigned) pinfo->frame_buffer_phys+init->fb_offset+0x10);
28662306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[18].r, init->pitch[cmode]);
28762306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[19].r, (pinfo->total_vram == 0x100000 ?
28862306a36Sopenharmony_ci					     init->mode[cmode+1] :
28962306a36Sopenharmony_ci					     init->mode[cmode]));
29062306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[20].r, (pinfo->total_vram == 0x100000 ? 0x11 : 0x1011));
29162306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[21].r, 0x100);
29262306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[22].r, 1);
29362306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[23].r, 1);
29462306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[26].r, 0xc00);
29562306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[27].r, 0x235);
29662306a36Sopenharmony_ci	/* out_be32(&platinum_regs->reg[27].r, 0x2aa); */
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	STORE_D2(0, (pinfo->total_vram == 0x100000 ?
29962306a36Sopenharmony_ci		     init->dacula_ctrl[cmode] & 0xf :
30062306a36Sopenharmony_ci		     init->dacula_ctrl[cmode]));
30162306a36Sopenharmony_ci	STORE_D2(1, 4);
30262306a36Sopenharmony_ci	STORE_D2(2, 0);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	set_platinum_clock(pinfo);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[24].r, 0);	/* turn display on */
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci/*
31062306a36Sopenharmony_ci * Set misc info vars for this driver
31162306a36Sopenharmony_ci */
31262306a36Sopenharmony_cistatic void platinum_init_info(struct fb_info *info,
31362306a36Sopenharmony_ci			       struct fb_info_platinum *pinfo)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	/* Fill fb_info */
31662306a36Sopenharmony_ci	info->fbops = &platinumfb_ops;
31762306a36Sopenharmony_ci	info->pseudo_palette = pinfo->pseudo_palette;
31862306a36Sopenharmony_ci	info->screen_base = pinfo->frame_buffer + 0x20;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	fb_alloc_cmap(&info->cmap, 256, 0);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* Fill fix common fields */
32362306a36Sopenharmony_ci	strcpy(info->fix.id, "platinum");
32462306a36Sopenharmony_ci	info->fix.mmio_start = pinfo->platinum_regs_phys;
32562306a36Sopenharmony_ci	info->fix.mmio_len = 0x1000;
32662306a36Sopenharmony_ci	info->fix.type = FB_TYPE_PACKED_PIXELS;
32762306a36Sopenharmony_ci	info->fix.smem_start = pinfo->frame_buffer_phys + 0x20; /* will be updated later */
32862306a36Sopenharmony_ci	info->fix.smem_len = pinfo->total_vram - 0x20;
32962306a36Sopenharmony_ci        info->fix.ywrapstep = 0;
33062306a36Sopenharmony_ci	info->fix.xpanstep = 0;
33162306a36Sopenharmony_ci	info->fix.ypanstep = 0;
33262306a36Sopenharmony_ci        info->fix.type_aux = 0;
33362306a36Sopenharmony_ci        info->fix.accel = FB_ACCEL_NONE;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int platinum_init_fb(struct fb_info *info)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct fb_info_platinum *pinfo = info->par;
34062306a36Sopenharmony_ci	struct fb_var_screeninfo var;
34162306a36Sopenharmony_ci	int sense, rc;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	sense = read_platinum_sense(pinfo);
34462306a36Sopenharmony_ci	printk(KERN_INFO "platinumfb: Monitor sense value = 0x%x, ", sense);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (IS_REACHABLE(CONFIG_NVRAM) && default_vmode == VMODE_NVRAM)
34762306a36Sopenharmony_ci		default_vmode = nvram_read_byte(NV_VMODE);
34862306a36Sopenharmony_ci	if (default_vmode <= 0 || default_vmode > VMODE_MAX ||
34962306a36Sopenharmony_ci	    !platinum_reg_init[default_vmode - 1]) {
35062306a36Sopenharmony_ci		default_vmode = mac_map_monitor_sense(sense);
35162306a36Sopenharmony_ci		if (!platinum_reg_init[default_vmode - 1])
35262306a36Sopenharmony_ci			default_vmode = VMODE_640_480_60;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (IS_REACHABLE(CONFIG_NVRAM) && default_cmode == CMODE_NVRAM)
35662306a36Sopenharmony_ci		default_cmode = nvram_read_byte(NV_CMODE);
35762306a36Sopenharmony_ci	if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
35862306a36Sopenharmony_ci		default_cmode = CMODE_8;
35962306a36Sopenharmony_ci	/*
36062306a36Sopenharmony_ci	 * Reduce the pixel size if we don't have enough VRAM.
36162306a36Sopenharmony_ci	 */
36262306a36Sopenharmony_ci	while(default_cmode > CMODE_8 &&
36362306a36Sopenharmony_ci	      platinum_vram_reqd(default_vmode, default_cmode) > pinfo->total_vram)
36462306a36Sopenharmony_ci		default_cmode--;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	printk("platinumfb:  Using video mode %d and color mode %d.\n", default_vmode, default_cmode);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* Setup default var */
36962306a36Sopenharmony_ci	if (mac_vmode_to_var(default_vmode, default_cmode, &var) < 0) {
37062306a36Sopenharmony_ci		/* This shouldn't happen! */
37162306a36Sopenharmony_ci		printk("mac_vmode_to_var(%d, %d,) failed\n", default_vmode, default_cmode);
37262306a36Sopenharmony_citry_again:
37362306a36Sopenharmony_ci		default_vmode = VMODE_640_480_60;
37462306a36Sopenharmony_ci		default_cmode = CMODE_8;
37562306a36Sopenharmony_ci		if (mac_vmode_to_var(default_vmode, default_cmode, &var) < 0) {
37662306a36Sopenharmony_ci			printk(KERN_ERR "platinumfb: mac_vmode_to_var() failed\n");
37762306a36Sopenharmony_ci			return -ENXIO;
37862306a36Sopenharmony_ci		}
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/* Initialize info structure */
38262306a36Sopenharmony_ci	platinum_init_info(info, pinfo);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/* Apply default var */
38562306a36Sopenharmony_ci	info->var = var;
38662306a36Sopenharmony_ci	var.activate = FB_ACTIVATE_NOW;
38762306a36Sopenharmony_ci	rc = fb_set_var(info, &var);
38862306a36Sopenharmony_ci	if (rc && (default_vmode != VMODE_640_480_60 || default_cmode != CMODE_8))
38962306a36Sopenharmony_ci		goto try_again;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* Register with fbdev layer */
39262306a36Sopenharmony_ci	rc = register_framebuffer(info);
39362306a36Sopenharmony_ci	if (rc < 0)
39462306a36Sopenharmony_ci		return rc;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	fb_info(info, "Apple Platinum frame buffer device\n");
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/*
40262306a36Sopenharmony_ci * Get the monitor sense value.
40362306a36Sopenharmony_ci * Note that this can be called before calibrate_delay,
40462306a36Sopenharmony_ci * so we can't use udelay.
40562306a36Sopenharmony_ci */
40662306a36Sopenharmony_cistatic int read_platinum_sense(struct fb_info_platinum *info)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	volatile struct platinum_regs __iomem *platinum_regs = info->platinum_regs;
40962306a36Sopenharmony_ci	int sense;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[23].r, 7);	/* turn off drivers */
41262306a36Sopenharmony_ci	__delay(2000);
41362306a36Sopenharmony_ci	sense = (~in_be32(&platinum_regs->reg[23].r) & 7) << 8;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* drive each sense line low in turn and collect the other 2 */
41662306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[23].r, 3);	/* drive A low */
41762306a36Sopenharmony_ci	__delay(2000);
41862306a36Sopenharmony_ci	sense |= (~in_be32(&platinum_regs->reg[23].r) & 3) << 4;
41962306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[23].r, 5);	/* drive B low */
42062306a36Sopenharmony_ci	__delay(2000);
42162306a36Sopenharmony_ci	sense |= (~in_be32(&platinum_regs->reg[23].r) & 4) << 1;
42262306a36Sopenharmony_ci	sense |= (~in_be32(&platinum_regs->reg[23].r) & 1) << 2;
42362306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[23].r, 6);	/* drive C low */
42462306a36Sopenharmony_ci	__delay(2000);
42562306a36Sopenharmony_ci	sense |= (~in_be32(&platinum_regs->reg[23].r) & 6) >> 1;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	out_be32(&platinum_regs->reg[23].r, 7);	/* turn off drivers */
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	return sense;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci/*
43362306a36Sopenharmony_ci * This routine takes a user-supplied var, and picks the best vmode/cmode from it.
43462306a36Sopenharmony_ci * It also updates the var structure to the actual mode data obtained
43562306a36Sopenharmony_ci */
43662306a36Sopenharmony_cistatic int platinum_var_to_par(struct fb_var_screeninfo *var,
43762306a36Sopenharmony_ci			       struct fb_info_platinum *pinfo,
43862306a36Sopenharmony_ci			       int check_only)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	int vmode, cmode;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (mac_var_to_vmode(var, &vmode, &cmode) != 0) {
44362306a36Sopenharmony_ci		printk(KERN_ERR "platinum_var_to_par: mac_var_to_vmode unsuccessful.\n");
44462306a36Sopenharmony_ci		printk(KERN_ERR "platinum_var_to_par: var->xres = %d\n", var->xres);
44562306a36Sopenharmony_ci		printk(KERN_ERR "platinum_var_to_par: var->yres = %d\n", var->yres);
44662306a36Sopenharmony_ci		printk(KERN_ERR "platinum_var_to_par: var->xres_virtual = %d\n", var->xres_virtual);
44762306a36Sopenharmony_ci		printk(KERN_ERR "platinum_var_to_par: var->yres_virtual = %d\n", var->yres_virtual);
44862306a36Sopenharmony_ci		printk(KERN_ERR "platinum_var_to_par: var->bits_per_pixel = %d\n", var->bits_per_pixel);
44962306a36Sopenharmony_ci		printk(KERN_ERR "platinum_var_to_par: var->pixclock = %d\n", var->pixclock);
45062306a36Sopenharmony_ci		printk(KERN_ERR "platinum_var_to_par: var->vmode = %d\n", var->vmode);
45162306a36Sopenharmony_ci		return -EINVAL;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (!platinum_reg_init[vmode-1]) {
45562306a36Sopenharmony_ci		printk(KERN_ERR "platinum_var_to_par, vmode %d not valid.\n", vmode);
45662306a36Sopenharmony_ci		return -EINVAL;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (platinum_vram_reqd(vmode, cmode) > pinfo->total_vram) {
46062306a36Sopenharmony_ci		printk(KERN_ERR "platinum_var_to_par, not enough ram for vmode %d, cmode %d.\n", vmode, cmode);
46162306a36Sopenharmony_ci		return -EINVAL;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (mac_vmode_to_var(vmode, cmode, var))
46562306a36Sopenharmony_ci		return -EINVAL;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (check_only)
46862306a36Sopenharmony_ci		return 0;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	pinfo->vmode = vmode;
47162306a36Sopenharmony_ci	pinfo->cmode = cmode;
47262306a36Sopenharmony_ci	pinfo->xres = vmode_attrs[vmode-1].hres;
47362306a36Sopenharmony_ci	pinfo->yres = vmode_attrs[vmode-1].vres;
47462306a36Sopenharmony_ci	pinfo->xoffset = 0;
47562306a36Sopenharmony_ci	pinfo->yoffset = 0;
47662306a36Sopenharmony_ci	pinfo->vxres = pinfo->xres;
47762306a36Sopenharmony_ci	pinfo->vyres = pinfo->yres;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	return 0;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci/*
48462306a36Sopenharmony_ci * Parse user specified options (`video=platinumfb:')
48562306a36Sopenharmony_ci */
48662306a36Sopenharmony_cistatic int __init platinumfb_setup(char *options)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	char *this_opt;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (!options || !*options)
49162306a36Sopenharmony_ci		return 0;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
49462306a36Sopenharmony_ci		if (!strncmp(this_opt, "vmode:", 6)) {
49562306a36Sopenharmony_ci	    		int vmode = simple_strtoul(this_opt+6, NULL, 0);
49662306a36Sopenharmony_ci			if (vmode > 0 && vmode <= VMODE_MAX)
49762306a36Sopenharmony_ci				default_vmode = vmode;
49862306a36Sopenharmony_ci		} else if (!strncmp(this_opt, "cmode:", 6)) {
49962306a36Sopenharmony_ci			int depth = simple_strtoul(this_opt+6, NULL, 0);
50062306a36Sopenharmony_ci			switch (depth) {
50162306a36Sopenharmony_ci			 case 0:
50262306a36Sopenharmony_ci			 case 8:
50362306a36Sopenharmony_ci			    default_cmode = CMODE_8;
50462306a36Sopenharmony_ci			    break;
50562306a36Sopenharmony_ci			 case 15:
50662306a36Sopenharmony_ci			 case 16:
50762306a36Sopenharmony_ci			    default_cmode = CMODE_16;
50862306a36Sopenharmony_ci			    break;
50962306a36Sopenharmony_ci			 case 24:
51062306a36Sopenharmony_ci			 case 32:
51162306a36Sopenharmony_ci			    default_cmode = CMODE_32;
51262306a36Sopenharmony_ci			    break;
51362306a36Sopenharmony_ci			}
51462306a36Sopenharmony_ci		}
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci	return 0;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci#ifdef __powerpc__
52062306a36Sopenharmony_ci#define invalidate_cache(addr) \
52162306a36Sopenharmony_ci	asm volatile("eieio; dcbf 0,%1" \
52262306a36Sopenharmony_ci	: "=m" (*(addr)) : "r" (addr) : "memory");
52362306a36Sopenharmony_ci#else
52462306a36Sopenharmony_ci#define invalidate_cache(addr)
52562306a36Sopenharmony_ci#endif
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic int platinumfb_probe(struct platform_device* odev)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct device_node	*dp = odev->dev.of_node;
53062306a36Sopenharmony_ci	struct fb_info		*info;
53162306a36Sopenharmony_ci	struct fb_info_platinum	*pinfo;
53262306a36Sopenharmony_ci	volatile __u8		*fbuffer;
53362306a36Sopenharmony_ci	int			bank0, bank1, bank2, bank3, rc;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	dev_info(&odev->dev, "Found Apple Platinum video hardware\n");
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(*pinfo), &odev->dev);
53862306a36Sopenharmony_ci	if (!info)
53962306a36Sopenharmony_ci		return -ENOMEM;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	pinfo = info->par;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (of_address_to_resource(dp, 0, &pinfo->rsrc_reg) ||
54462306a36Sopenharmony_ci	    of_address_to_resource(dp, 1, &pinfo->rsrc_fb)) {
54562306a36Sopenharmony_ci		dev_err(&odev->dev, "Can't get resources\n");
54662306a36Sopenharmony_ci		framebuffer_release(info);
54762306a36Sopenharmony_ci		return -ENXIO;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci	dev_dbg(&odev->dev, " registers  : 0x%llx...0x%llx\n",
55062306a36Sopenharmony_ci		(unsigned long long)pinfo->rsrc_reg.start,
55162306a36Sopenharmony_ci		(unsigned long long)pinfo->rsrc_reg.end);
55262306a36Sopenharmony_ci	dev_dbg(&odev->dev, " framebuffer: 0x%llx...0x%llx\n",
55362306a36Sopenharmony_ci		(unsigned long long)pinfo->rsrc_fb.start,
55462306a36Sopenharmony_ci		(unsigned long long)pinfo->rsrc_fb.end);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* Do not try to request register space, they overlap with the
55762306a36Sopenharmony_ci	 * northbridge and that can fail. Only request framebuffer
55862306a36Sopenharmony_ci	 */
55962306a36Sopenharmony_ci	if (!request_mem_region(pinfo->rsrc_fb.start,
56062306a36Sopenharmony_ci				resource_size(&pinfo->rsrc_fb),
56162306a36Sopenharmony_ci				"platinumfb framebuffer")) {
56262306a36Sopenharmony_ci		printk(KERN_ERR "platinumfb: Can't request framebuffer !\n");
56362306a36Sopenharmony_ci		framebuffer_release(info);
56462306a36Sopenharmony_ci		return -ENXIO;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* frame buffer - map only 4MB */
56862306a36Sopenharmony_ci	pinfo->frame_buffer_phys = pinfo->rsrc_fb.start;
56962306a36Sopenharmony_ci	pinfo->frame_buffer = ioremap_wt(pinfo->rsrc_fb.start, 0x400000);
57062306a36Sopenharmony_ci	pinfo->base_frame_buffer = pinfo->frame_buffer;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* registers */
57362306a36Sopenharmony_ci	pinfo->platinum_regs_phys = pinfo->rsrc_reg.start;
57462306a36Sopenharmony_ci	pinfo->platinum_regs = ioremap(pinfo->rsrc_reg.start, 0x1000);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	pinfo->cmap_regs_phys = 0xf301b000;	/* XXX not in prom? */
57762306a36Sopenharmony_ci	request_mem_region(pinfo->cmap_regs_phys, 0x1000, "platinumfb cmap");
57862306a36Sopenharmony_ci	pinfo->cmap_regs = ioremap(pinfo->cmap_regs_phys, 0x1000);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/* Grok total video ram */
58162306a36Sopenharmony_ci	out_be32(&pinfo->platinum_regs->reg[16].r, (unsigned)pinfo->frame_buffer_phys);
58262306a36Sopenharmony_ci	out_be32(&pinfo->platinum_regs->reg[20].r, 0x1011);	/* select max vram */
58362306a36Sopenharmony_ci	out_be32(&pinfo->platinum_regs->reg[24].r, 0);	/* switch in vram */
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	fbuffer = pinfo->base_frame_buffer;
58662306a36Sopenharmony_ci	fbuffer[0x100000] = 0x34;
58762306a36Sopenharmony_ci	fbuffer[0x100008] = 0x0;
58862306a36Sopenharmony_ci	invalidate_cache(&fbuffer[0x100000]);
58962306a36Sopenharmony_ci	fbuffer[0x200000] = 0x56;
59062306a36Sopenharmony_ci	fbuffer[0x200008] = 0x0;
59162306a36Sopenharmony_ci	invalidate_cache(&fbuffer[0x200000]);
59262306a36Sopenharmony_ci	fbuffer[0x300000] = 0x78;
59362306a36Sopenharmony_ci	fbuffer[0x300008] = 0x0;
59462306a36Sopenharmony_ci	invalidate_cache(&fbuffer[0x300000]);
59562306a36Sopenharmony_ci	bank0 = 1; /* builtin 1MB vram, always there */
59662306a36Sopenharmony_ci	bank1 = fbuffer[0x100000] == 0x34;
59762306a36Sopenharmony_ci	bank2 = fbuffer[0x200000] == 0x56;
59862306a36Sopenharmony_ci	bank3 = fbuffer[0x300000] == 0x78;
59962306a36Sopenharmony_ci	pinfo->total_vram = (bank0 + bank1 + bank2 + bank3) * 0x100000;
60062306a36Sopenharmony_ci	printk(KERN_INFO "platinumfb: Total VRAM = %dMB (%d%d%d%d)\n",
60162306a36Sopenharmony_ci	       (unsigned int) (pinfo->total_vram / 1024 / 1024),
60262306a36Sopenharmony_ci	       bank3, bank2, bank1, bank0);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	/*
60562306a36Sopenharmony_ci	 * Try to determine whether we have an old or a new DACula.
60662306a36Sopenharmony_ci	 */
60762306a36Sopenharmony_ci	out_8(&pinfo->cmap_regs->addr, 0x40);
60862306a36Sopenharmony_ci	pinfo->dactype = in_8(&pinfo->cmap_regs->d2);
60962306a36Sopenharmony_ci	switch (pinfo->dactype) {
61062306a36Sopenharmony_ci	case 0x3c:
61162306a36Sopenharmony_ci		pinfo->clktype = 1;
61262306a36Sopenharmony_ci		printk(KERN_INFO "platinumfb: DACula type 0x3c\n");
61362306a36Sopenharmony_ci		break;
61462306a36Sopenharmony_ci	case 0x84:
61562306a36Sopenharmony_ci		pinfo->clktype = 0;
61662306a36Sopenharmony_ci		printk(KERN_INFO "platinumfb: DACula type 0x84\n");
61762306a36Sopenharmony_ci		break;
61862306a36Sopenharmony_ci	default:
61962306a36Sopenharmony_ci		pinfo->clktype = 0;
62062306a36Sopenharmony_ci		printk(KERN_INFO "platinumfb: Unknown DACula type: %x\n", pinfo->dactype);
62162306a36Sopenharmony_ci		break;
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci	dev_set_drvdata(&odev->dev, info);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	rc = platinum_init_fb(info);
62662306a36Sopenharmony_ci	if (rc != 0) {
62762306a36Sopenharmony_ci		iounmap(pinfo->frame_buffer);
62862306a36Sopenharmony_ci		iounmap(pinfo->platinum_regs);
62962306a36Sopenharmony_ci		iounmap(pinfo->cmap_regs);
63062306a36Sopenharmony_ci		framebuffer_release(info);
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	return rc;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic void platinumfb_remove(struct platform_device* odev)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	struct fb_info		*info = dev_get_drvdata(&odev->dev);
63962306a36Sopenharmony_ci	struct fb_info_platinum	*pinfo = info->par;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci        unregister_framebuffer (info);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	/* Unmap frame buffer and registers */
64462306a36Sopenharmony_ci	iounmap(pinfo->frame_buffer);
64562306a36Sopenharmony_ci	iounmap(pinfo->platinum_regs);
64662306a36Sopenharmony_ci	iounmap(pinfo->cmap_regs);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	release_mem_region(pinfo->rsrc_fb.start,
64962306a36Sopenharmony_ci			   resource_size(&pinfo->rsrc_fb));
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	release_mem_region(pinfo->cmap_regs_phys, 0x1000);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	framebuffer_release(info);
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic struct of_device_id platinumfb_match[] =
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	{
65962306a36Sopenharmony_ci	.name 		= "platinum",
66062306a36Sopenharmony_ci	},
66162306a36Sopenharmony_ci	{},
66262306a36Sopenharmony_ci};
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic struct platform_driver platinum_driver =
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	.driver = {
66762306a36Sopenharmony_ci		.name = "platinumfb",
66862306a36Sopenharmony_ci		.of_match_table = platinumfb_match,
66962306a36Sopenharmony_ci	},
67062306a36Sopenharmony_ci	.probe		= platinumfb_probe,
67162306a36Sopenharmony_ci	.remove_new	= platinumfb_remove,
67262306a36Sopenharmony_ci};
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic int __init platinumfb_init(void)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci#ifndef MODULE
67762306a36Sopenharmony_ci	char *option = NULL;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (fb_get_options("platinumfb", &option))
68062306a36Sopenharmony_ci		return -ENODEV;
68162306a36Sopenharmony_ci	platinumfb_setup(option);
68262306a36Sopenharmony_ci#endif
68362306a36Sopenharmony_ci	platform_driver_register(&platinum_driver);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return 0;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic void __exit platinumfb_exit(void)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	platform_driver_unregister(&platinum_driver);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
69462306a36Sopenharmony_ciMODULE_DESCRIPTION("framebuffer driver for Apple Platinum video");
69562306a36Sopenharmony_cimodule_init(platinumfb_init);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci#ifdef MODULE
69862306a36Sopenharmony_cimodule_exit(platinumfb_exit);
69962306a36Sopenharmony_ci#endif
700