162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  SGI GBE frame buffer driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Copyright (C) 1999 Silicon Graphics, Inc. - Jeffrey Newquist
562306a36Sopenharmony_ci *  Copyright (C) 2002 Vivien Chappelier <vivien.chappelier@linux-mips.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
862306a36Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
962306a36Sopenharmony_ci *  more details.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/gfp.h>
1762306a36Sopenharmony_ci#include <linux/fb.h>
1862306a36Sopenharmony_ci#include <linux/init.h>
1962306a36Sopenharmony_ci#include <linux/interrupt.h>
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/mm.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/io.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#ifdef CONFIG_MIPS
2662306a36Sopenharmony_ci#include <asm/addrspace.h>
2762306a36Sopenharmony_ci#endif
2862306a36Sopenharmony_ci#include <asm/byteorder.h>
2962306a36Sopenharmony_ci#include <asm/tlbflush.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <video/gbe.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic struct sgi_gbe *gbe;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct gbefb_par {
3662306a36Sopenharmony_ci	struct fb_var_screeninfo var;
3762306a36Sopenharmony_ci	struct gbe_timing_info timing;
3862306a36Sopenharmony_ci	int wc_cookie;
3962306a36Sopenharmony_ci	int valid;
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define GBE_BASE	0x16000000 /* SGI O2 */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* macro for fastest write-though access to the framebuffer */
4562306a36Sopenharmony_ci#ifdef CONFIG_MIPS
4662306a36Sopenharmony_ci#ifdef CONFIG_CPU_R10000
4762306a36Sopenharmony_ci#define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_UNCACHED_ACCELERATED)
4862306a36Sopenharmony_ci#else
4962306a36Sopenharmony_ci#define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_CACHABLE_NO_WA)
5062306a36Sopenharmony_ci#endif
5162306a36Sopenharmony_ci#endif
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/*
5462306a36Sopenharmony_ci *  RAM we reserve for the frame buffer. This defines the maximum screen
5562306a36Sopenharmony_ci *  size
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_ci#if CONFIG_FB_GBE_MEM > 8
5862306a36Sopenharmony_ci#error GBE Framebuffer cannot use more than 8MB of memory
5962306a36Sopenharmony_ci#endif
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define TILE_SHIFT 16
6262306a36Sopenharmony_ci#define TILE_SIZE (1 << TILE_SHIFT)
6362306a36Sopenharmony_ci#define TILE_MASK (TILE_SIZE - 1)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic unsigned int gbe_mem_size = CONFIG_FB_GBE_MEM * 1024*1024;
6662306a36Sopenharmony_cistatic void *gbe_mem;
6762306a36Sopenharmony_cistatic dma_addr_t gbe_dma_addr;
6862306a36Sopenharmony_cistatic unsigned long gbe_mem_phys;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic struct {
7162306a36Sopenharmony_ci	uint16_t *cpu;
7262306a36Sopenharmony_ci	dma_addr_t dma;
7362306a36Sopenharmony_ci} gbe_tiles;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int gbe_revision;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int ypan, ywrap;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic uint32_t pseudo_palette[16];
8062306a36Sopenharmony_cistatic uint32_t gbe_cmap[256];
8162306a36Sopenharmony_cistatic int gbe_turned_on; /* 0 turned off, 1 turned on */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic char *mode_option = NULL;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* default CRT mode */
8662306a36Sopenharmony_cistatic struct fb_var_screeninfo default_var_CRT = {
8762306a36Sopenharmony_ci	/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
8862306a36Sopenharmony_ci	.xres		= 640,
8962306a36Sopenharmony_ci	.yres		= 480,
9062306a36Sopenharmony_ci	.xres_virtual	= 640,
9162306a36Sopenharmony_ci	.yres_virtual	= 480,
9262306a36Sopenharmony_ci	.xoffset	= 0,
9362306a36Sopenharmony_ci	.yoffset	= 0,
9462306a36Sopenharmony_ci	.bits_per_pixel	= 8,
9562306a36Sopenharmony_ci	.grayscale	= 0,
9662306a36Sopenharmony_ci	.red		= { 0, 8, 0 },
9762306a36Sopenharmony_ci	.green		= { 0, 8, 0 },
9862306a36Sopenharmony_ci	.blue		= { 0, 8, 0 },
9962306a36Sopenharmony_ci	.transp		= { 0, 0, 0 },
10062306a36Sopenharmony_ci	.nonstd		= 0,
10162306a36Sopenharmony_ci	.activate	= 0,
10262306a36Sopenharmony_ci	.height		= -1,
10362306a36Sopenharmony_ci	.width		= -1,
10462306a36Sopenharmony_ci	.accel_flags	= 0,
10562306a36Sopenharmony_ci	.pixclock	= 39722,	/* picoseconds */
10662306a36Sopenharmony_ci	.left_margin	= 48,
10762306a36Sopenharmony_ci	.right_margin	= 16,
10862306a36Sopenharmony_ci	.upper_margin	= 33,
10962306a36Sopenharmony_ci	.lower_margin	= 10,
11062306a36Sopenharmony_ci	.hsync_len	= 96,
11162306a36Sopenharmony_ci	.vsync_len	= 2,
11262306a36Sopenharmony_ci	.sync		= 0,
11362306a36Sopenharmony_ci	.vmode		= FB_VMODE_NONINTERLACED,
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/* default LCD mode */
11762306a36Sopenharmony_cistatic struct fb_var_screeninfo default_var_LCD = {
11862306a36Sopenharmony_ci	/* 1600x1024, 8 bpp */
11962306a36Sopenharmony_ci	.xres		= 1600,
12062306a36Sopenharmony_ci	.yres		= 1024,
12162306a36Sopenharmony_ci	.xres_virtual	= 1600,
12262306a36Sopenharmony_ci	.yres_virtual	= 1024,
12362306a36Sopenharmony_ci	.xoffset	= 0,
12462306a36Sopenharmony_ci	.yoffset	= 0,
12562306a36Sopenharmony_ci	.bits_per_pixel	= 8,
12662306a36Sopenharmony_ci	.grayscale	= 0,
12762306a36Sopenharmony_ci	.red		= { 0, 8, 0 },
12862306a36Sopenharmony_ci	.green		= { 0, 8, 0 },
12962306a36Sopenharmony_ci	.blue		= { 0, 8, 0 },
13062306a36Sopenharmony_ci	.transp		= { 0, 0, 0 },
13162306a36Sopenharmony_ci	.nonstd		= 0,
13262306a36Sopenharmony_ci	.activate	= 0,
13362306a36Sopenharmony_ci	.height		= -1,
13462306a36Sopenharmony_ci	.width		= -1,
13562306a36Sopenharmony_ci	.accel_flags	= 0,
13662306a36Sopenharmony_ci	.pixclock	= 9353,
13762306a36Sopenharmony_ci	.left_margin	= 20,
13862306a36Sopenharmony_ci	.right_margin	= 30,
13962306a36Sopenharmony_ci	.upper_margin	= 37,
14062306a36Sopenharmony_ci	.lower_margin	= 3,
14162306a36Sopenharmony_ci	.hsync_len	= 20,
14262306a36Sopenharmony_ci	.vsync_len	= 3,
14362306a36Sopenharmony_ci	.sync		= 0,
14462306a36Sopenharmony_ci	.vmode		= FB_VMODE_NONINTERLACED
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* default modedb mode */
14862306a36Sopenharmony_ci/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */
14962306a36Sopenharmony_cistatic struct fb_videomode default_mode_CRT = {
15062306a36Sopenharmony_ci	.refresh	= 60,
15162306a36Sopenharmony_ci	.xres		= 640,
15262306a36Sopenharmony_ci	.yres		= 480,
15362306a36Sopenharmony_ci	.pixclock	= 39722,
15462306a36Sopenharmony_ci	.left_margin	= 48,
15562306a36Sopenharmony_ci	.right_margin	= 16,
15662306a36Sopenharmony_ci	.upper_margin	= 33,
15762306a36Sopenharmony_ci	.lower_margin	= 10,
15862306a36Sopenharmony_ci	.hsync_len	= 96,
15962306a36Sopenharmony_ci	.vsync_len	= 2,
16062306a36Sopenharmony_ci	.sync		= 0,
16162306a36Sopenharmony_ci	.vmode		= FB_VMODE_NONINTERLACED,
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci/* 1600x1024 SGI flatpanel 1600sw */
16462306a36Sopenharmony_cistatic struct fb_videomode default_mode_LCD = {
16562306a36Sopenharmony_ci	/* 1600x1024, 8 bpp */
16662306a36Sopenharmony_ci	.xres		= 1600,
16762306a36Sopenharmony_ci	.yres		= 1024,
16862306a36Sopenharmony_ci	.pixclock	= 9353,
16962306a36Sopenharmony_ci	.left_margin	= 20,
17062306a36Sopenharmony_ci	.right_margin	= 30,
17162306a36Sopenharmony_ci	.upper_margin	= 37,
17262306a36Sopenharmony_ci	.lower_margin	= 3,
17362306a36Sopenharmony_ci	.hsync_len	= 20,
17462306a36Sopenharmony_ci	.vsync_len	= 3,
17562306a36Sopenharmony_ci	.vmode		= FB_VMODE_NONINTERLACED,
17662306a36Sopenharmony_ci};
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic struct fb_videomode *default_mode = &default_mode_CRT;
17962306a36Sopenharmony_cistatic struct fb_var_screeninfo *default_var = &default_var_CRT;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int flat_panel_enabled = 0;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic void gbe_reset(void)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	/* Turn on dotclock PLL */
18662306a36Sopenharmony_ci	gbe->ctrlstat = 0x300aa000;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/*
19162306a36Sopenharmony_ci * Function:	gbe_turn_off
19262306a36Sopenharmony_ci * Parameters:	(None)
19362306a36Sopenharmony_ci * Description:	This should turn off the monitor and gbe.  This is used
19462306a36Sopenharmony_ci *              when switching between the serial console and the graphics
19562306a36Sopenharmony_ci *              console.
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic void gbe_turn_off(void)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	int i;
20162306a36Sopenharmony_ci	unsigned int val, y, vpixen_off;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	gbe_turned_on = 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* check if pixel counter is on */
20662306a36Sopenharmony_ci	val = gbe->vt_xy;
20762306a36Sopenharmony_ci	if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 1)
20862306a36Sopenharmony_ci		return;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* turn off DMA */
21162306a36Sopenharmony_ci	val = gbe->ovr_control;
21262306a36Sopenharmony_ci	SET_GBE_FIELD(OVR_CONTROL, OVR_DMA_ENABLE, val, 0);
21362306a36Sopenharmony_ci	gbe->ovr_control = val;
21462306a36Sopenharmony_ci	udelay(1000);
21562306a36Sopenharmony_ci	val = gbe->frm_control;
21662306a36Sopenharmony_ci	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0);
21762306a36Sopenharmony_ci	gbe->frm_control = val;
21862306a36Sopenharmony_ci	udelay(1000);
21962306a36Sopenharmony_ci	val = gbe->did_control;
22062306a36Sopenharmony_ci	SET_GBE_FIELD(DID_CONTROL, DID_DMA_ENABLE, val, 0);
22162306a36Sopenharmony_ci	gbe->did_control = val;
22262306a36Sopenharmony_ci	udelay(1000);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* We have to wait through two vertical retrace periods before
22562306a36Sopenharmony_ci	 * the pixel DMA is turned off for sure. */
22662306a36Sopenharmony_ci	for (i = 0; i < 10000; i++) {
22762306a36Sopenharmony_ci		val = gbe->frm_inhwctrl;
22862306a36Sopenharmony_ci		if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val)) {
22962306a36Sopenharmony_ci			udelay(10);
23062306a36Sopenharmony_ci		} else {
23162306a36Sopenharmony_ci			val = gbe->ovr_inhwctrl;
23262306a36Sopenharmony_ci			if (GET_GBE_FIELD(OVR_INHWCTRL, OVR_DMA_ENABLE, val)) {
23362306a36Sopenharmony_ci				udelay(10);
23462306a36Sopenharmony_ci			} else {
23562306a36Sopenharmony_ci				val = gbe->did_inhwctrl;
23662306a36Sopenharmony_ci				if (GET_GBE_FIELD(DID_INHWCTRL, DID_DMA_ENABLE, val)) {
23762306a36Sopenharmony_ci					udelay(10);
23862306a36Sopenharmony_ci				} else
23962306a36Sopenharmony_ci					break;
24062306a36Sopenharmony_ci			}
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci	if (i == 10000)
24462306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: turn off DMA timed out\n");
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* wait for vpixen_off */
24762306a36Sopenharmony_ci	val = gbe->vt_vpixen;
24862306a36Sopenharmony_ci	vpixen_off = GET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	for (i = 0; i < 100000; i++) {
25162306a36Sopenharmony_ci		val = gbe->vt_xy;
25262306a36Sopenharmony_ci		y = GET_GBE_FIELD(VT_XY, Y, val);
25362306a36Sopenharmony_ci		if (y < vpixen_off)
25462306a36Sopenharmony_ci			break;
25562306a36Sopenharmony_ci		udelay(1);
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci	if (i == 100000)
25862306a36Sopenharmony_ci		printk(KERN_ERR
25962306a36Sopenharmony_ci		       "gbefb: wait for vpixen_off timed out\n");
26062306a36Sopenharmony_ci	for (i = 0; i < 10000; i++) {
26162306a36Sopenharmony_ci		val = gbe->vt_xy;
26262306a36Sopenharmony_ci		y = GET_GBE_FIELD(VT_XY, Y, val);
26362306a36Sopenharmony_ci		if (y > vpixen_off)
26462306a36Sopenharmony_ci			break;
26562306a36Sopenharmony_ci		udelay(1);
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci	if (i == 10000)
26862306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: wait for vpixen_off timed out\n");
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* turn off pixel counter */
27162306a36Sopenharmony_ci	val = 0;
27262306a36Sopenharmony_ci	SET_GBE_FIELD(VT_XY, FREEZE, val, 1);
27362306a36Sopenharmony_ci	gbe->vt_xy = val;
27462306a36Sopenharmony_ci	mdelay(10);
27562306a36Sopenharmony_ci	for (i = 0; i < 10000; i++) {
27662306a36Sopenharmony_ci		val = gbe->vt_xy;
27762306a36Sopenharmony_ci		if (GET_GBE_FIELD(VT_XY, FREEZE, val) != 1)
27862306a36Sopenharmony_ci			udelay(10);
27962306a36Sopenharmony_ci		else
28062306a36Sopenharmony_ci			break;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci	if (i == 10000)
28362306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: turn off pixel clock timed out\n");
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* turn off dot clock */
28662306a36Sopenharmony_ci	val = gbe->dotclock;
28762306a36Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, RUN, val, 0);
28862306a36Sopenharmony_ci	gbe->dotclock = val;
28962306a36Sopenharmony_ci	mdelay(10);
29062306a36Sopenharmony_ci	for (i = 0; i < 10000; i++) {
29162306a36Sopenharmony_ci		val = gbe->dotclock;
29262306a36Sopenharmony_ci		if (GET_GBE_FIELD(DOTCLK, RUN, val))
29362306a36Sopenharmony_ci			udelay(10);
29462306a36Sopenharmony_ci		else
29562306a36Sopenharmony_ci			break;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci	if (i == 10000)
29862306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: turn off dotclock timed out\n");
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* reset the frame DMA FIFO */
30162306a36Sopenharmony_ci	val = gbe->frm_size_tile;
30262306a36Sopenharmony_ci	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 1);
30362306a36Sopenharmony_ci	gbe->frm_size_tile = val;
30462306a36Sopenharmony_ci	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 0);
30562306a36Sopenharmony_ci	gbe->frm_size_tile = val;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void gbe_turn_on(void)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	unsigned int val, i;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/*
31362306a36Sopenharmony_ci	 * Check if pixel counter is off, for unknown reason this
31462306a36Sopenharmony_ci	 * code hangs Visual Workstations
31562306a36Sopenharmony_ci	 */
31662306a36Sopenharmony_ci	if (gbe_revision < 2) {
31762306a36Sopenharmony_ci		val = gbe->vt_xy;
31862306a36Sopenharmony_ci		if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 0)
31962306a36Sopenharmony_ci			return;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* turn on dot clock */
32362306a36Sopenharmony_ci	val = gbe->dotclock;
32462306a36Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, RUN, val, 1);
32562306a36Sopenharmony_ci	gbe->dotclock = val;
32662306a36Sopenharmony_ci	mdelay(10);
32762306a36Sopenharmony_ci	for (i = 0; i < 10000; i++) {
32862306a36Sopenharmony_ci		val = gbe->dotclock;
32962306a36Sopenharmony_ci		if (GET_GBE_FIELD(DOTCLK, RUN, val) != 1)
33062306a36Sopenharmony_ci			udelay(10);
33162306a36Sopenharmony_ci		else
33262306a36Sopenharmony_ci			break;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci	if (i == 10000)
33562306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: turn on dotclock timed out\n");
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* turn on pixel counter */
33862306a36Sopenharmony_ci	val = 0;
33962306a36Sopenharmony_ci	SET_GBE_FIELD(VT_XY, FREEZE, val, 0);
34062306a36Sopenharmony_ci	gbe->vt_xy = val;
34162306a36Sopenharmony_ci	mdelay(10);
34262306a36Sopenharmony_ci	for (i = 0; i < 10000; i++) {
34362306a36Sopenharmony_ci		val = gbe->vt_xy;
34462306a36Sopenharmony_ci		if (GET_GBE_FIELD(VT_XY, FREEZE, val))
34562306a36Sopenharmony_ci			udelay(10);
34662306a36Sopenharmony_ci		else
34762306a36Sopenharmony_ci			break;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	if (i == 10000)
35062306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: turn on pixel clock timed out\n");
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* turn on DMA */
35362306a36Sopenharmony_ci	val = gbe->frm_control;
35462306a36Sopenharmony_ci	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 1);
35562306a36Sopenharmony_ci	gbe->frm_control = val;
35662306a36Sopenharmony_ci	udelay(1000);
35762306a36Sopenharmony_ci	for (i = 0; i < 10000; i++) {
35862306a36Sopenharmony_ci		val = gbe->frm_inhwctrl;
35962306a36Sopenharmony_ci		if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val) != 1)
36062306a36Sopenharmony_ci			udelay(10);
36162306a36Sopenharmony_ci		else
36262306a36Sopenharmony_ci			break;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci	if (i == 10000)
36562306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: turn on DMA timed out\n");
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	gbe_turned_on = 1;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void gbe_loadcmap(void)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	int i, j;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	for (i = 0; i < 256; i++) {
37562306a36Sopenharmony_ci		for (j = 0; j < 1000 && gbe->cm_fifo >= 63; j++)
37662306a36Sopenharmony_ci			udelay(10);
37762306a36Sopenharmony_ci		if (j == 1000)
37862306a36Sopenharmony_ci			printk(KERN_ERR "gbefb: cmap FIFO timeout\n");
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		gbe->cmap[i] = gbe_cmap[i];
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci/*
38562306a36Sopenharmony_ci *  Blank the display.
38662306a36Sopenharmony_ci */
38762306a36Sopenharmony_cistatic int gbefb_blank(int blank, struct fb_info *info)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
39062306a36Sopenharmony_ci	switch (blank) {
39162306a36Sopenharmony_ci	case FB_BLANK_UNBLANK:		/* unblank */
39262306a36Sopenharmony_ci		gbe_turn_on();
39362306a36Sopenharmony_ci		gbe_loadcmap();
39462306a36Sopenharmony_ci		break;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	case FB_BLANK_NORMAL:		/* blank */
39762306a36Sopenharmony_ci		gbe_turn_off();
39862306a36Sopenharmony_ci		break;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	default:
40162306a36Sopenharmony_ci		/* Nothing */
40262306a36Sopenharmony_ci		break;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci	return 0;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci/*
40862306a36Sopenharmony_ci *  Setup flatpanel related registers.
40962306a36Sopenharmony_ci */
41062306a36Sopenharmony_cistatic void gbefb_setup_flatpanel(struct gbe_timing_info *timing)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	int fp_wid, fp_hgt, fp_vbs, fp_vbe;
41362306a36Sopenharmony_ci	u32 outputVal = 0;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	SET_GBE_FIELD(VT_FLAGS, HDRV_INVERT, outputVal,
41662306a36Sopenharmony_ci		(timing->flags & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1);
41762306a36Sopenharmony_ci	SET_GBE_FIELD(VT_FLAGS, VDRV_INVERT, outputVal,
41862306a36Sopenharmony_ci		(timing->flags & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1);
41962306a36Sopenharmony_ci	gbe->vt_flags = outputVal;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* Turn on the flat panel */
42262306a36Sopenharmony_ci	fp_wid = 1600;
42362306a36Sopenharmony_ci	fp_hgt = 1024;
42462306a36Sopenharmony_ci	fp_vbs = 0;
42562306a36Sopenharmony_ci	fp_vbe = 1600;
42662306a36Sopenharmony_ci	timing->pll_m = 4;
42762306a36Sopenharmony_ci	timing->pll_n = 1;
42862306a36Sopenharmony_ci	timing->pll_p = 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	outputVal = 0;
43162306a36Sopenharmony_ci	SET_GBE_FIELD(FP_DE, ON, outputVal, fp_vbs);
43262306a36Sopenharmony_ci	SET_GBE_FIELD(FP_DE, OFF, outputVal, fp_vbe);
43362306a36Sopenharmony_ci	gbe->fp_de = outputVal;
43462306a36Sopenharmony_ci	outputVal = 0;
43562306a36Sopenharmony_ci	SET_GBE_FIELD(FP_HDRV, OFF, outputVal, fp_wid);
43662306a36Sopenharmony_ci	gbe->fp_hdrv = outputVal;
43762306a36Sopenharmony_ci	outputVal = 0;
43862306a36Sopenharmony_ci	SET_GBE_FIELD(FP_VDRV, ON, outputVal, 1);
43962306a36Sopenharmony_ci	SET_GBE_FIELD(FP_VDRV, OFF, outputVal, fp_hgt + 1);
44062306a36Sopenharmony_ci	gbe->fp_vdrv = outputVal;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistruct gbe_pll_info {
44462306a36Sopenharmony_ci	int clock_rate;
44562306a36Sopenharmony_ci	int fvco_min;
44662306a36Sopenharmony_ci	int fvco_max;
44762306a36Sopenharmony_ci};
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic struct gbe_pll_info gbe_pll_table[2] = {
45062306a36Sopenharmony_ci	{ 20, 80, 220 },
45162306a36Sopenharmony_ci	{ 27, 80, 220 },
45262306a36Sopenharmony_ci};
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic int compute_gbe_timing(struct fb_var_screeninfo *var,
45562306a36Sopenharmony_ci			      struct gbe_timing_info *timing)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	int pll_m, pll_n, pll_p, error, best_m, best_n, best_p, best_error;
45862306a36Sopenharmony_ci	int pixclock;
45962306a36Sopenharmony_ci	struct gbe_pll_info *gbe_pll;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (gbe_revision < 2)
46262306a36Sopenharmony_ci		gbe_pll = &gbe_pll_table[0];
46362306a36Sopenharmony_ci	else
46462306a36Sopenharmony_ci		gbe_pll = &gbe_pll_table[1];
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* Determine valid resolution and timing
46762306a36Sopenharmony_ci	 * GBE crystal runs at 20Mhz or 27Mhz
46862306a36Sopenharmony_ci	 * pll_m, pll_n, pll_p define the following frequencies
46962306a36Sopenharmony_ci	 * fvco = pll_m * 20Mhz / pll_n
47062306a36Sopenharmony_ci	 * fout = fvco / (2**pll_p) */
47162306a36Sopenharmony_ci	best_error = 1000000000;
47262306a36Sopenharmony_ci	best_n = best_m = best_p = 0;
47362306a36Sopenharmony_ci	for (pll_p = 0; pll_p < 4; pll_p++)
47462306a36Sopenharmony_ci		for (pll_m = 1; pll_m < 256; pll_m++)
47562306a36Sopenharmony_ci			for (pll_n = 1; pll_n < 64; pll_n++) {
47662306a36Sopenharmony_ci				pixclock = (1000000 / gbe_pll->clock_rate) *
47762306a36Sopenharmony_ci						(pll_n << pll_p) / pll_m;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci				error = var->pixclock - pixclock;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci				if (error < 0)
48262306a36Sopenharmony_ci					error = -error;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci				if (error < best_error &&
48562306a36Sopenharmony_ci				    pll_m / pll_n >
48662306a36Sopenharmony_ci				    gbe_pll->fvco_min / gbe_pll->clock_rate &&
48762306a36Sopenharmony_ci 				    pll_m / pll_n <
48862306a36Sopenharmony_ci				    gbe_pll->fvco_max / gbe_pll->clock_rate) {
48962306a36Sopenharmony_ci					best_error = error;
49062306a36Sopenharmony_ci					best_m = pll_m;
49162306a36Sopenharmony_ci					best_n = pll_n;
49262306a36Sopenharmony_ci					best_p = pll_p;
49362306a36Sopenharmony_ci				}
49462306a36Sopenharmony_ci			}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (!best_n || !best_m)
49762306a36Sopenharmony_ci		return -EINVAL;	/* Resolution to high */
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	pixclock = (1000000 / gbe_pll->clock_rate) *
50062306a36Sopenharmony_ci		(best_n << best_p) / best_m;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* set video timing information */
50362306a36Sopenharmony_ci	if (timing) {
50462306a36Sopenharmony_ci		timing->width = var->xres;
50562306a36Sopenharmony_ci		timing->height = var->yres;
50662306a36Sopenharmony_ci		timing->pll_m = best_m;
50762306a36Sopenharmony_ci		timing->pll_n = best_n;
50862306a36Sopenharmony_ci		timing->pll_p = best_p;
50962306a36Sopenharmony_ci		timing->cfreq = gbe_pll->clock_rate * 1000 * timing->pll_m /
51062306a36Sopenharmony_ci			(timing->pll_n << timing->pll_p);
51162306a36Sopenharmony_ci		timing->htotal = var->left_margin + var->xres +
51262306a36Sopenharmony_ci				var->right_margin + var->hsync_len;
51362306a36Sopenharmony_ci		timing->vtotal = var->upper_margin + var->yres +
51462306a36Sopenharmony_ci				var->lower_margin + var->vsync_len;
51562306a36Sopenharmony_ci		timing->fields_sec = 1000 * timing->cfreq / timing->htotal *
51662306a36Sopenharmony_ci				1000 / timing->vtotal;
51762306a36Sopenharmony_ci		timing->hblank_start = var->xres;
51862306a36Sopenharmony_ci		timing->vblank_start = var->yres;
51962306a36Sopenharmony_ci		timing->hblank_end = timing->htotal;
52062306a36Sopenharmony_ci		timing->hsync_start = var->xres + var->right_margin + 1;
52162306a36Sopenharmony_ci		timing->hsync_end = timing->hsync_start + var->hsync_len;
52262306a36Sopenharmony_ci		timing->vblank_end = timing->vtotal;
52362306a36Sopenharmony_ci		timing->vsync_start = var->yres + var->lower_margin + 1;
52462306a36Sopenharmony_ci		timing->vsync_end = timing->vsync_start + var->vsync_len;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	return pixclock;
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic void gbe_set_timing_info(struct gbe_timing_info *timing)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	int temp;
53362306a36Sopenharmony_ci	unsigned int val;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* setup dot clock PLL */
53662306a36Sopenharmony_ci	val = 0;
53762306a36Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, M, val, timing->pll_m - 1);
53862306a36Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, N, val, timing->pll_n - 1);
53962306a36Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, P, val, timing->pll_p);
54062306a36Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, RUN, val, 0);	/* do not start yet */
54162306a36Sopenharmony_ci	gbe->dotclock = val;
54262306a36Sopenharmony_ci	mdelay(10);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	/* setup pixel counter */
54562306a36Sopenharmony_ci	val = 0;
54662306a36Sopenharmony_ci	SET_GBE_FIELD(VT_XYMAX, MAXX, val, timing->htotal);
54762306a36Sopenharmony_ci	SET_GBE_FIELD(VT_XYMAX, MAXY, val, timing->vtotal);
54862306a36Sopenharmony_ci	gbe->vt_xymax = val;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* setup video timing signals */
55162306a36Sopenharmony_ci	val = 0;
55262306a36Sopenharmony_ci	SET_GBE_FIELD(VT_VSYNC, VSYNC_ON, val, timing->vsync_start);
55362306a36Sopenharmony_ci	SET_GBE_FIELD(VT_VSYNC, VSYNC_OFF, val, timing->vsync_end);
55462306a36Sopenharmony_ci	gbe->vt_vsync = val;
55562306a36Sopenharmony_ci	val = 0;
55662306a36Sopenharmony_ci	SET_GBE_FIELD(VT_HSYNC, HSYNC_ON, val, timing->hsync_start);
55762306a36Sopenharmony_ci	SET_GBE_FIELD(VT_HSYNC, HSYNC_OFF, val, timing->hsync_end);
55862306a36Sopenharmony_ci	gbe->vt_hsync = val;
55962306a36Sopenharmony_ci	val = 0;
56062306a36Sopenharmony_ci	SET_GBE_FIELD(VT_VBLANK, VBLANK_ON, val, timing->vblank_start);
56162306a36Sopenharmony_ci	SET_GBE_FIELD(VT_VBLANK, VBLANK_OFF, val, timing->vblank_end);
56262306a36Sopenharmony_ci	gbe->vt_vblank = val;
56362306a36Sopenharmony_ci	val = 0;
56462306a36Sopenharmony_ci	SET_GBE_FIELD(VT_HBLANK, HBLANK_ON, val,
56562306a36Sopenharmony_ci		      timing->hblank_start - 5);
56662306a36Sopenharmony_ci	SET_GBE_FIELD(VT_HBLANK, HBLANK_OFF, val,
56762306a36Sopenharmony_ci		      timing->hblank_end - 3);
56862306a36Sopenharmony_ci	gbe->vt_hblank = val;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/* setup internal timing signals */
57162306a36Sopenharmony_ci	val = 0;
57262306a36Sopenharmony_ci	SET_GBE_FIELD(VT_VCMAP, VCMAP_ON, val, timing->vblank_start);
57362306a36Sopenharmony_ci	SET_GBE_FIELD(VT_VCMAP, VCMAP_OFF, val, timing->vblank_end);
57462306a36Sopenharmony_ci	gbe->vt_vcmap = val;
57562306a36Sopenharmony_ci	val = 0;
57662306a36Sopenharmony_ci	SET_GBE_FIELD(VT_HCMAP, HCMAP_ON, val, timing->hblank_start);
57762306a36Sopenharmony_ci	SET_GBE_FIELD(VT_HCMAP, HCMAP_OFF, val, timing->hblank_end);
57862306a36Sopenharmony_ci	gbe->vt_hcmap = val;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	val = 0;
58162306a36Sopenharmony_ci	temp = timing->vblank_start - timing->vblank_end - 1;
58262306a36Sopenharmony_ci	if (temp > 0)
58362306a36Sopenharmony_ci		temp = -temp;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (flat_panel_enabled)
58662306a36Sopenharmony_ci		gbefb_setup_flatpanel(timing);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	SET_GBE_FIELD(DID_START_XY, DID_STARTY, val, (u32) temp);
58962306a36Sopenharmony_ci	if (timing->hblank_end >= 20)
59062306a36Sopenharmony_ci		SET_GBE_FIELD(DID_START_XY, DID_STARTX, val,
59162306a36Sopenharmony_ci			      timing->hblank_end - 20);
59262306a36Sopenharmony_ci	else
59362306a36Sopenharmony_ci		SET_GBE_FIELD(DID_START_XY, DID_STARTX, val,
59462306a36Sopenharmony_ci			      timing->htotal - (20 - timing->hblank_end));
59562306a36Sopenharmony_ci	gbe->did_start_xy = val;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	val = 0;
59862306a36Sopenharmony_ci	SET_GBE_FIELD(CRS_START_XY, CRS_STARTY, val, (u32) (temp + 1));
59962306a36Sopenharmony_ci	if (timing->hblank_end >= GBE_CRS_MAGIC)
60062306a36Sopenharmony_ci		SET_GBE_FIELD(CRS_START_XY, CRS_STARTX, val,
60162306a36Sopenharmony_ci			      timing->hblank_end - GBE_CRS_MAGIC);
60262306a36Sopenharmony_ci	else
60362306a36Sopenharmony_ci		SET_GBE_FIELD(CRS_START_XY, CRS_STARTX, val,
60462306a36Sopenharmony_ci			      timing->htotal - (GBE_CRS_MAGIC -
60562306a36Sopenharmony_ci						timing->hblank_end));
60662306a36Sopenharmony_ci	gbe->crs_start_xy = val;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	val = 0;
60962306a36Sopenharmony_ci	SET_GBE_FIELD(VC_START_XY, VC_STARTY, val, (u32) temp);
61062306a36Sopenharmony_ci	SET_GBE_FIELD(VC_START_XY, VC_STARTX, val, timing->hblank_end - 4);
61162306a36Sopenharmony_ci	gbe->vc_start_xy = val;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	val = 0;
61462306a36Sopenharmony_ci	temp = timing->hblank_end - GBE_PIXEN_MAGIC_ON;
61562306a36Sopenharmony_ci	if (temp < 0)
61662306a36Sopenharmony_ci		temp += timing->htotal;	/* allow blank to wrap around */
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	SET_GBE_FIELD(VT_HPIXEN, HPIXEN_ON, val, temp);
61962306a36Sopenharmony_ci	SET_GBE_FIELD(VT_HPIXEN, HPIXEN_OFF, val,
62062306a36Sopenharmony_ci		      ((temp + timing->width -
62162306a36Sopenharmony_ci			GBE_PIXEN_MAGIC_OFF) % timing->htotal));
62262306a36Sopenharmony_ci	gbe->vt_hpixen = val;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	val = 0;
62562306a36Sopenharmony_ci	SET_GBE_FIELD(VT_VPIXEN, VPIXEN_ON, val, timing->vblank_end);
62662306a36Sopenharmony_ci	SET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val, timing->vblank_start);
62762306a36Sopenharmony_ci	gbe->vt_vpixen = val;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* turn off sync on green */
63062306a36Sopenharmony_ci	val = 0;
63162306a36Sopenharmony_ci	SET_GBE_FIELD(VT_FLAGS, SYNC_LOW, val, 1);
63262306a36Sopenharmony_ci	gbe->vt_flags = val;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/*
63662306a36Sopenharmony_ci *  Set the hardware according to 'par'.
63762306a36Sopenharmony_ci */
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic int gbefb_set_par(struct fb_info *info)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	int i;
64262306a36Sopenharmony_ci	unsigned int val;
64362306a36Sopenharmony_ci	int wholeTilesX, partTilesX, maxPixelsPerTileX;
64462306a36Sopenharmony_ci	int height_pix;
64562306a36Sopenharmony_ci	int xpmax, ypmax;	/* Monitor resolution */
64662306a36Sopenharmony_ci	int bytesPerPixel;	/* Bytes per pixel */
64762306a36Sopenharmony_ci	struct gbefb_par *par = (struct gbefb_par *) info->par;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	compute_gbe_timing(&info->var, &par->timing);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	bytesPerPixel = info->var.bits_per_pixel / 8;
65262306a36Sopenharmony_ci	info->fix.line_length = info->var.xres_virtual * bytesPerPixel;
65362306a36Sopenharmony_ci	xpmax = par->timing.width;
65462306a36Sopenharmony_ci	ypmax = par->timing.height;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	/* turn off GBE */
65762306a36Sopenharmony_ci	gbe_turn_off();
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* set timing info */
66062306a36Sopenharmony_ci	gbe_set_timing_info(&par->timing);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/* initialize DIDs */
66362306a36Sopenharmony_ci	val = 0;
66462306a36Sopenharmony_ci	switch (bytesPerPixel) {
66562306a36Sopenharmony_ci	case 1:
66662306a36Sopenharmony_ci		SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_I8);
66762306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
66862306a36Sopenharmony_ci		break;
66962306a36Sopenharmony_ci	case 2:
67062306a36Sopenharmony_ci		SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_ARGB5);
67162306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
67262306a36Sopenharmony_ci		break;
67362306a36Sopenharmony_ci	case 4:
67462306a36Sopenharmony_ci		SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_RGB8);
67562306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
67662306a36Sopenharmony_ci		break;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci	SET_GBE_FIELD(WID, BUF, val, GBE_BMODE_BOTH);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	for (i = 0; i < 32; i++)
68162306a36Sopenharmony_ci		gbe->mode_regs[i] = val;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/* Initialize interrupts */
68462306a36Sopenharmony_ci	gbe->vt_intr01 = 0xffffffff;
68562306a36Sopenharmony_ci	gbe->vt_intr23 = 0xffffffff;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	/* HACK:
68862306a36Sopenharmony_ci	   The GBE hardware uses a tiled memory to screen mapping. Tiles are
68962306a36Sopenharmony_ci	   blocks of 512x128, 256x128 or 128x128 pixels, respectively for 8bit,
69062306a36Sopenharmony_ci	   16bit and 32 bit modes (64 kB). They cover the screen with partial
69162306a36Sopenharmony_ci	   tiles on the right and/or bottom of the screen if needed.
69262306a36Sopenharmony_ci	   For example in 640x480 8 bit mode the mapping is:
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	   <-------- 640 ----->
69562306a36Sopenharmony_ci	   <---- 512 ----><128|384 offscreen>
69662306a36Sopenharmony_ci	   ^  ^
69762306a36Sopenharmony_ci	   | 128    [tile 0]        [tile 1]
69862306a36Sopenharmony_ci	   |  v
69962306a36Sopenharmony_ci	   ^
70062306a36Sopenharmony_ci	   4 128    [tile 2]        [tile 3]
70162306a36Sopenharmony_ci	   8  v
70262306a36Sopenharmony_ci	   0  ^
70362306a36Sopenharmony_ci	   128    [tile 4]        [tile 5]
70462306a36Sopenharmony_ci	   |  v
70562306a36Sopenharmony_ci	   |  ^
70662306a36Sopenharmony_ci	   v  96    [tile 6]        [tile 7]
70762306a36Sopenharmony_ci	   32 offscreen
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	   Tiles have the advantage that they can be allocated individually in
71062306a36Sopenharmony_ci	   memory. However, this mapping is not linear at all, which is not
71162306a36Sopenharmony_ci	   really convenient. In order to support linear addressing, the GBE
71262306a36Sopenharmony_ci	   DMA hardware is fooled into thinking the screen is only one tile
71362306a36Sopenharmony_ci	   large and but has a greater height, so that the DMA transfer covers
71462306a36Sopenharmony_ci	   the same region.
71562306a36Sopenharmony_ci	   Tiles are still allocated as independent chunks of 64KB of
71662306a36Sopenharmony_ci	   continuous physical memory and remapped so that the kernel sees the
71762306a36Sopenharmony_ci	   framebuffer as a continuous virtual memory. The GBE tile table is
71862306a36Sopenharmony_ci	   set up so that each tile references one of these 64k blocks:
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	   GBE -> tile list    framebuffer           TLB   <------------ CPU
72162306a36Sopenharmony_ci	          [ tile 0 ] -> [ 64KB ]  <- [ 16x 4KB page entries ]     ^
72262306a36Sopenharmony_ci	             ...           ...              ...       linear virtual FB
72362306a36Sopenharmony_ci	          [ tile n ] -> [ 64KB ]  <- [ 16x 4KB page entries ]     v
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	   The GBE hardware is then told that the buffer is 512*tweaked_height,
72762306a36Sopenharmony_ci	   with tweaked_height = real_width*real_height/pixels_per_tile.
72862306a36Sopenharmony_ci	   Thus the GBE hardware will scan the first tile, filing the first 64k
72962306a36Sopenharmony_ci	   covered region of the screen, and then will proceed to the next
73062306a36Sopenharmony_ci	   tile, until the whole screen is covered.
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	   Here is what would happen at 640x480 8bit:
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	   normal tiling               linear
73562306a36Sopenharmony_ci	   ^   11111111111111112222    11111111111111111111  ^
73662306a36Sopenharmony_ci	   128 11111111111111112222    11111111111111111111 102 lines
73762306a36Sopenharmony_ci	       11111111111111112222    11111111111111111111  v
73862306a36Sopenharmony_ci	   V   11111111111111112222    11111111222222222222
73962306a36Sopenharmony_ci	       33333333333333334444    22222222222222222222
74062306a36Sopenharmony_ci	       33333333333333334444    22222222222222222222
74162306a36Sopenharmony_ci	       <      512     >        <  256 >               102*640+256 = 64k
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	   NOTE: The only mode for which this is not working is 800x600 8bit,
74462306a36Sopenharmony_ci	   as 800*600/512 = 937.5 which is not integer and thus causes
74562306a36Sopenharmony_ci	   flickering.
74662306a36Sopenharmony_ci	   I guess this is not so important as one can use 640x480 8bit or
74762306a36Sopenharmony_ci	   800x600 16bit anyway.
74862306a36Sopenharmony_ci	 */
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	/* Tell gbe about the tiles table location */
75162306a36Sopenharmony_ci	/* tile_ptr -> [ tile 1 ] -> FB mem */
75262306a36Sopenharmony_ci	/*             [ tile 2 ] -> FB mem */
75362306a36Sopenharmony_ci	/*               ...                */
75462306a36Sopenharmony_ci	val = 0;
75562306a36Sopenharmony_ci	SET_GBE_FIELD(FRM_CONTROL, FRM_TILE_PTR, val, gbe_tiles.dma >> 9);
75662306a36Sopenharmony_ci	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0); /* do not start */
75762306a36Sopenharmony_ci	SET_GBE_FIELD(FRM_CONTROL, FRM_LINEAR, val, 0);
75862306a36Sopenharmony_ci	gbe->frm_control = val;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	maxPixelsPerTileX = 512 / bytesPerPixel;
76162306a36Sopenharmony_ci	wholeTilesX = 1;
76262306a36Sopenharmony_ci	partTilesX = 0;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	/* Initialize the framebuffer */
76562306a36Sopenharmony_ci	val = 0;
76662306a36Sopenharmony_ci	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_WIDTH_TILE, val, wholeTilesX);
76762306a36Sopenharmony_ci	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_RHS, val, partTilesX);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	switch (bytesPerPixel) {
77062306a36Sopenharmony_ci	case 1:
77162306a36Sopenharmony_ci		SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val,
77262306a36Sopenharmony_ci			      GBE_FRM_DEPTH_8);
77362306a36Sopenharmony_ci		break;
77462306a36Sopenharmony_ci	case 2:
77562306a36Sopenharmony_ci		SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val,
77662306a36Sopenharmony_ci			      GBE_FRM_DEPTH_16);
77762306a36Sopenharmony_ci		break;
77862306a36Sopenharmony_ci	case 4:
77962306a36Sopenharmony_ci		SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val,
78062306a36Sopenharmony_ci			      GBE_FRM_DEPTH_32);
78162306a36Sopenharmony_ci		break;
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci	gbe->frm_size_tile = val;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	/* compute tweaked height */
78662306a36Sopenharmony_ci	height_pix = xpmax * ypmax / maxPixelsPerTileX;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	val = 0;
78962306a36Sopenharmony_ci	SET_GBE_FIELD(FRM_SIZE_PIXEL, FB_HEIGHT_PIX, val, height_pix);
79062306a36Sopenharmony_ci	gbe->frm_size_pixel = val;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	/* turn off DID and overlay DMA */
79362306a36Sopenharmony_ci	gbe->did_control = 0;
79462306a36Sopenharmony_ci	gbe->ovr_width_tile = 0;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* Turn off mouse cursor */
79762306a36Sopenharmony_ci	gbe->crs_ctl = 0;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* Turn on GBE */
80062306a36Sopenharmony_ci	gbe_turn_on();
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	/* Initialize the gamma map */
80362306a36Sopenharmony_ci	udelay(10);
80462306a36Sopenharmony_ci	for (i = 0; i < 256; i++)
80562306a36Sopenharmony_ci		gbe->gmap[i] = (i << 24) | (i << 16) | (i << 8);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* Initialize the color map */
80862306a36Sopenharmony_ci	for (i = 0; i < 256; i++)
80962306a36Sopenharmony_ci		gbe_cmap[i] = (i << 8) | (i << 16) | (i << 24);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	gbe_loadcmap();
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	return 0;
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic void gbefb_encode_fix(struct fb_fix_screeninfo *fix,
81762306a36Sopenharmony_ci			     struct fb_var_screeninfo *var)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
82062306a36Sopenharmony_ci	strcpy(fix->id, "SGI GBE");
82162306a36Sopenharmony_ci	fix->smem_start = (unsigned long) gbe_mem;
82262306a36Sopenharmony_ci	fix->smem_len = gbe_mem_size;
82362306a36Sopenharmony_ci	fix->type = FB_TYPE_PACKED_PIXELS;
82462306a36Sopenharmony_ci	fix->type_aux = 0;
82562306a36Sopenharmony_ci	fix->accel = FB_ACCEL_NONE;
82662306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
82762306a36Sopenharmony_ci	case 8:
82862306a36Sopenharmony_ci		fix->visual = FB_VISUAL_PSEUDOCOLOR;
82962306a36Sopenharmony_ci		break;
83062306a36Sopenharmony_ci	default:
83162306a36Sopenharmony_ci		fix->visual = FB_VISUAL_TRUECOLOR;
83262306a36Sopenharmony_ci		break;
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci	fix->ywrapstep = 0;
83562306a36Sopenharmony_ci	fix->xpanstep = 0;
83662306a36Sopenharmony_ci	fix->ypanstep = 0;
83762306a36Sopenharmony_ci	fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
83862306a36Sopenharmony_ci	fix->mmio_start = GBE_BASE;
83962306a36Sopenharmony_ci	fix->mmio_len = sizeof(struct sgi_gbe);
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci/*
84362306a36Sopenharmony_ci *  Set a single color register. The values supplied are already
84462306a36Sopenharmony_ci *  rounded down to the hardware's capabilities (according to the
84562306a36Sopenharmony_ci *  entries in the var structure). Return != 0 for invalid regno.
84662306a36Sopenharmony_ci */
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cistatic int gbefb_setcolreg(unsigned regno, unsigned red, unsigned green,
84962306a36Sopenharmony_ci			     unsigned blue, unsigned transp,
85062306a36Sopenharmony_ci			     struct fb_info *info)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	int i;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (regno > 255)
85562306a36Sopenharmony_ci		return 1;
85662306a36Sopenharmony_ci	red >>= 8;
85762306a36Sopenharmony_ci	green >>= 8;
85862306a36Sopenharmony_ci	blue >>= 8;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (info->var.bits_per_pixel <= 8) {
86162306a36Sopenharmony_ci		gbe_cmap[regno] = (red << 24) | (green << 16) | (blue << 8);
86262306a36Sopenharmony_ci		if (gbe_turned_on) {
86362306a36Sopenharmony_ci			/* wait for the color map FIFO to have a free entry */
86462306a36Sopenharmony_ci			for (i = 0; i < 1000 && gbe->cm_fifo >= 63; i++)
86562306a36Sopenharmony_ci				udelay(10);
86662306a36Sopenharmony_ci			if (i == 1000) {
86762306a36Sopenharmony_ci				printk(KERN_ERR "gbefb: cmap FIFO timeout\n");
86862306a36Sopenharmony_ci				return 1;
86962306a36Sopenharmony_ci			}
87062306a36Sopenharmony_ci			gbe->cmap[regno] = gbe_cmap[regno];
87162306a36Sopenharmony_ci		}
87262306a36Sopenharmony_ci	} else if (regno < 16) {
87362306a36Sopenharmony_ci		switch (info->var.bits_per_pixel) {
87462306a36Sopenharmony_ci		case 15:
87562306a36Sopenharmony_ci		case 16:
87662306a36Sopenharmony_ci			red >>= 3;
87762306a36Sopenharmony_ci			green >>= 3;
87862306a36Sopenharmony_ci			blue >>= 3;
87962306a36Sopenharmony_ci			pseudo_palette[regno] =
88062306a36Sopenharmony_ci				(red << info->var.red.offset) |
88162306a36Sopenharmony_ci				(green << info->var.green.offset) |
88262306a36Sopenharmony_ci				(blue << info->var.blue.offset);
88362306a36Sopenharmony_ci			break;
88462306a36Sopenharmony_ci		case 32:
88562306a36Sopenharmony_ci			pseudo_palette[regno] =
88662306a36Sopenharmony_ci				(red << info->var.red.offset) |
88762306a36Sopenharmony_ci				(green << info->var.green.offset) |
88862306a36Sopenharmony_ci				(blue << info->var.blue.offset);
88962306a36Sopenharmony_ci			break;
89062306a36Sopenharmony_ci		}
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	return 0;
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci/*
89762306a36Sopenharmony_ci *  Check video mode validity, eventually modify var to best match.
89862306a36Sopenharmony_ci */
89962306a36Sopenharmony_cistatic int gbefb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	unsigned int line_length;
90262306a36Sopenharmony_ci	struct gbe_timing_info timing;
90362306a36Sopenharmony_ci	int ret;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	/* Limit bpp to 8, 16, and 32 */
90662306a36Sopenharmony_ci	if (var->bits_per_pixel <= 8)
90762306a36Sopenharmony_ci		var->bits_per_pixel = 8;
90862306a36Sopenharmony_ci	else if (var->bits_per_pixel <= 16)
90962306a36Sopenharmony_ci		var->bits_per_pixel = 16;
91062306a36Sopenharmony_ci	else if (var->bits_per_pixel <= 32)
91162306a36Sopenharmony_ci		var->bits_per_pixel = 32;
91262306a36Sopenharmony_ci	else
91362306a36Sopenharmony_ci		return -EINVAL;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* Check the mode can be mapped linearly with the tile table trick. */
91662306a36Sopenharmony_ci	/* This requires width x height x bytes/pixel be a multiple of 512 */
91762306a36Sopenharmony_ci	if ((var->xres * var->yres * var->bits_per_pixel) & 4095)
91862306a36Sopenharmony_ci		return -EINVAL;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	var->grayscale = 0;	/* No grayscale for now */
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	ret = compute_gbe_timing(var, &timing);
92362306a36Sopenharmony_ci	var->pixclock = ret;
92462306a36Sopenharmony_ci	if (ret < 0)
92562306a36Sopenharmony_ci		return -EINVAL;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	/* Adjust virtual resolution, if necessary */
92862306a36Sopenharmony_ci	if (var->xres > var->xres_virtual || (!ywrap && !ypan))
92962306a36Sopenharmony_ci		var->xres_virtual = var->xres;
93062306a36Sopenharmony_ci	if (var->yres > var->yres_virtual || (!ywrap && !ypan))
93162306a36Sopenharmony_ci		var->yres_virtual = var->yres;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (var->vmode & FB_VMODE_CONUPDATE) {
93462306a36Sopenharmony_ci		var->vmode |= FB_VMODE_YWRAP;
93562306a36Sopenharmony_ci		var->xoffset = info->var.xoffset;
93662306a36Sopenharmony_ci		var->yoffset = info->var.yoffset;
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	/* No grayscale for now */
94062306a36Sopenharmony_ci	var->grayscale = 0;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	/* Memory limit */
94362306a36Sopenharmony_ci	line_length = var->xres_virtual * var->bits_per_pixel / 8;
94462306a36Sopenharmony_ci	if (line_length * var->yres_virtual > gbe_mem_size)
94562306a36Sopenharmony_ci		return -ENOMEM;	/* Virtual resolution too high */
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
94862306a36Sopenharmony_ci	case 8:
94962306a36Sopenharmony_ci		var->red.offset = 0;
95062306a36Sopenharmony_ci		var->red.length = 8;
95162306a36Sopenharmony_ci		var->green.offset = 0;
95262306a36Sopenharmony_ci		var->green.length = 8;
95362306a36Sopenharmony_ci		var->blue.offset = 0;
95462306a36Sopenharmony_ci		var->blue.length = 8;
95562306a36Sopenharmony_ci		var->transp.offset = 0;
95662306a36Sopenharmony_ci		var->transp.length = 0;
95762306a36Sopenharmony_ci		break;
95862306a36Sopenharmony_ci	case 16:		/* RGB 1555 */
95962306a36Sopenharmony_ci		var->red.offset = 10;
96062306a36Sopenharmony_ci		var->red.length = 5;
96162306a36Sopenharmony_ci		var->green.offset = 5;
96262306a36Sopenharmony_ci		var->green.length = 5;
96362306a36Sopenharmony_ci		var->blue.offset = 0;
96462306a36Sopenharmony_ci		var->blue.length = 5;
96562306a36Sopenharmony_ci		var->transp.offset = 0;
96662306a36Sopenharmony_ci		var->transp.length = 0;
96762306a36Sopenharmony_ci		break;
96862306a36Sopenharmony_ci	case 32:		/* RGB 8888 */
96962306a36Sopenharmony_ci		var->red.offset = 24;
97062306a36Sopenharmony_ci		var->red.length = 8;
97162306a36Sopenharmony_ci		var->green.offset = 16;
97262306a36Sopenharmony_ci		var->green.length = 8;
97362306a36Sopenharmony_ci		var->blue.offset = 8;
97462306a36Sopenharmony_ci		var->blue.length = 8;
97562306a36Sopenharmony_ci		var->transp.offset = 0;
97662306a36Sopenharmony_ci		var->transp.length = 8;
97762306a36Sopenharmony_ci		break;
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci	var->red.msb_right = 0;
98062306a36Sopenharmony_ci	var->green.msb_right = 0;
98162306a36Sopenharmony_ci	var->blue.msb_right = 0;
98262306a36Sopenharmony_ci	var->transp.msb_right = 0;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	var->left_margin = timing.htotal - timing.hsync_end;
98562306a36Sopenharmony_ci	var->right_margin = timing.hsync_start - timing.width;
98662306a36Sopenharmony_ci	var->upper_margin = timing.vtotal - timing.vsync_end;
98762306a36Sopenharmony_ci	var->lower_margin = timing.vsync_start - timing.height;
98862306a36Sopenharmony_ci	var->hsync_len = timing.hsync_end - timing.hsync_start;
98962306a36Sopenharmony_ci	var->vsync_len = timing.vsync_end - timing.vsync_start;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	return 0;
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic int gbefb_mmap(struct fb_info *info,
99562306a36Sopenharmony_ci			struct vm_area_struct *vma)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	unsigned long size = vma->vm_end - vma->vm_start;
99862306a36Sopenharmony_ci	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
99962306a36Sopenharmony_ci	unsigned long addr;
100062306a36Sopenharmony_ci	unsigned long phys_addr, phys_size;
100162306a36Sopenharmony_ci	u16 *tile;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	/* check range */
100462306a36Sopenharmony_ci	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
100562306a36Sopenharmony_ci		return -EINVAL;
100662306a36Sopenharmony_ci	if (size > gbe_mem_size)
100762306a36Sopenharmony_ci		return -EINVAL;
100862306a36Sopenharmony_ci	if (offset > gbe_mem_size - size)
100962306a36Sopenharmony_ci		return -EINVAL;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	/* remap using the fastest write-through mode on architecture */
101262306a36Sopenharmony_ci	/* try not polluting the cache when possible */
101362306a36Sopenharmony_ci#ifdef CONFIG_MIPS
101462306a36Sopenharmony_ci	pgprot_val(vma->vm_page_prot) =
101562306a36Sopenharmony_ci		pgprot_fb(pgprot_val(vma->vm_page_prot));
101662306a36Sopenharmony_ci#endif
101762306a36Sopenharmony_ci	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	/* look for the starting tile */
102062306a36Sopenharmony_ci	tile = &gbe_tiles.cpu[offset >> TILE_SHIFT];
102162306a36Sopenharmony_ci	addr = vma->vm_start;
102262306a36Sopenharmony_ci	offset &= TILE_MASK;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	/* remap each tile separately */
102562306a36Sopenharmony_ci	do {
102662306a36Sopenharmony_ci		phys_addr = (((unsigned long) (*tile)) << TILE_SHIFT) + offset;
102762306a36Sopenharmony_ci		if ((offset + size) < TILE_SIZE)
102862306a36Sopenharmony_ci			phys_size = size;
102962306a36Sopenharmony_ci		else
103062306a36Sopenharmony_ci			phys_size = TILE_SIZE - offset;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		if (remap_pfn_range(vma, addr, phys_addr >> PAGE_SHIFT,
103362306a36Sopenharmony_ci						phys_size, vma->vm_page_prot))
103462306a36Sopenharmony_ci			return -EAGAIN;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci		offset = 0;
103762306a36Sopenharmony_ci		size -= phys_size;
103862306a36Sopenharmony_ci		addr += phys_size;
103962306a36Sopenharmony_ci		tile++;
104062306a36Sopenharmony_ci	} while (size);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	return 0;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic const struct fb_ops gbefb_ops = {
104662306a36Sopenharmony_ci	.owner		= THIS_MODULE,
104762306a36Sopenharmony_ci	.fb_check_var	= gbefb_check_var,
104862306a36Sopenharmony_ci	.fb_set_par	= gbefb_set_par,
104962306a36Sopenharmony_ci	.fb_setcolreg	= gbefb_setcolreg,
105062306a36Sopenharmony_ci	.fb_mmap	= gbefb_mmap,
105162306a36Sopenharmony_ci	.fb_blank	= gbefb_blank,
105262306a36Sopenharmony_ci	.fb_fillrect	= cfb_fillrect,
105362306a36Sopenharmony_ci	.fb_copyarea	= cfb_copyarea,
105462306a36Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
105562306a36Sopenharmony_ci};
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci/*
105862306a36Sopenharmony_ci * sysfs
105962306a36Sopenharmony_ci */
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic ssize_t gbefb_show_memsize(struct device *dev, struct device_attribute *attr, char *buf)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", gbe_mem_size);
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic DEVICE_ATTR(size, S_IRUGO, gbefb_show_memsize, NULL);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_cistatic ssize_t gbefb_show_rev(struct device *device, struct device_attribute *attr, char *buf)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", gbe_revision);
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic DEVICE_ATTR(revision, S_IRUGO, gbefb_show_rev, NULL);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic struct attribute *gbefb_attrs[] = {
107662306a36Sopenharmony_ci	&dev_attr_size.attr,
107762306a36Sopenharmony_ci	&dev_attr_revision.attr,
107862306a36Sopenharmony_ci	NULL,
107962306a36Sopenharmony_ci};
108062306a36Sopenharmony_ciATTRIBUTE_GROUPS(gbefb);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci/*
108362306a36Sopenharmony_ci * Initialization
108462306a36Sopenharmony_ci */
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_cistatic int gbefb_setup(char *options)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	char *this_opt;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	if (!options || !*options)
109162306a36Sopenharmony_ci		return 0;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
109462306a36Sopenharmony_ci		if (!strncmp(this_opt, "monitor:", 8)) {
109562306a36Sopenharmony_ci			if (!strncmp(this_opt + 8, "crt", 3)) {
109662306a36Sopenharmony_ci				flat_panel_enabled = 0;
109762306a36Sopenharmony_ci				default_var = &default_var_CRT;
109862306a36Sopenharmony_ci				default_mode = &default_mode_CRT;
109962306a36Sopenharmony_ci			} else if (!strncmp(this_opt + 8, "1600sw", 6) ||
110062306a36Sopenharmony_ci				   !strncmp(this_opt + 8, "lcd", 3)) {
110162306a36Sopenharmony_ci				flat_panel_enabled = 1;
110262306a36Sopenharmony_ci				default_var = &default_var_LCD;
110362306a36Sopenharmony_ci				default_mode = &default_mode_LCD;
110462306a36Sopenharmony_ci			}
110562306a36Sopenharmony_ci		} else if (!strncmp(this_opt, "mem:", 4)) {
110662306a36Sopenharmony_ci			gbe_mem_size = memparse(this_opt + 4, &this_opt);
110762306a36Sopenharmony_ci			if (gbe_mem_size > CONFIG_FB_GBE_MEM * 1024 * 1024)
110862306a36Sopenharmony_ci				gbe_mem_size = CONFIG_FB_GBE_MEM * 1024 * 1024;
110962306a36Sopenharmony_ci			if (gbe_mem_size < TILE_SIZE)
111062306a36Sopenharmony_ci				gbe_mem_size = TILE_SIZE;
111162306a36Sopenharmony_ci		} else
111262306a36Sopenharmony_ci			mode_option = this_opt;
111362306a36Sopenharmony_ci	}
111462306a36Sopenharmony_ci	return 0;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic int gbefb_probe(struct platform_device *p_dev)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	int i, ret = 0;
112062306a36Sopenharmony_ci	struct fb_info *info;
112162306a36Sopenharmony_ci	struct gbefb_par *par;
112262306a36Sopenharmony_ci#ifndef MODULE
112362306a36Sopenharmony_ci	char *options = NULL;
112462306a36Sopenharmony_ci#endif
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct gbefb_par), &p_dev->dev);
112762306a36Sopenharmony_ci	if (!info)
112862306a36Sopenharmony_ci		return -ENOMEM;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci#ifndef MODULE
113162306a36Sopenharmony_ci	if (fb_get_options("gbefb", &options)) {
113262306a36Sopenharmony_ci		ret = -ENODEV;
113362306a36Sopenharmony_ci		goto out_release_framebuffer;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci	gbefb_setup(options);
113662306a36Sopenharmony_ci#endif
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	if (!request_mem_region(GBE_BASE, sizeof(struct sgi_gbe), "GBE")) {
113962306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: couldn't reserve mmio region\n");
114062306a36Sopenharmony_ci		ret = -EBUSY;
114162306a36Sopenharmony_ci		goto out_release_framebuffer;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	gbe = (struct sgi_gbe *) devm_ioremap(&p_dev->dev, GBE_BASE,
114562306a36Sopenharmony_ci					      sizeof(struct sgi_gbe));
114662306a36Sopenharmony_ci	if (!gbe) {
114762306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: couldn't map mmio region\n");
114862306a36Sopenharmony_ci		ret = -ENXIO;
114962306a36Sopenharmony_ci		goto out_release_mem_region;
115062306a36Sopenharmony_ci	}
115162306a36Sopenharmony_ci	gbe_revision = gbe->ctrlstat & 15;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	gbe_tiles.cpu = dmam_alloc_coherent(&p_dev->dev,
115462306a36Sopenharmony_ci				GBE_TLB_SIZE * sizeof(uint16_t),
115562306a36Sopenharmony_ci				&gbe_tiles.dma, GFP_KERNEL);
115662306a36Sopenharmony_ci	if (!gbe_tiles.cpu) {
115762306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: couldn't allocate tiles table\n");
115862306a36Sopenharmony_ci		ret = -ENOMEM;
115962306a36Sopenharmony_ci		goto out_release_mem_region;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	if (gbe_mem_phys) {
116362306a36Sopenharmony_ci		/* memory was allocated at boot time */
116462306a36Sopenharmony_ci		gbe_mem = devm_ioremap_wc(&p_dev->dev, gbe_mem_phys,
116562306a36Sopenharmony_ci					  gbe_mem_size);
116662306a36Sopenharmony_ci		if (!gbe_mem) {
116762306a36Sopenharmony_ci			printk(KERN_ERR "gbefb: couldn't map framebuffer\n");
116862306a36Sopenharmony_ci			ret = -ENOMEM;
116962306a36Sopenharmony_ci			goto out_release_mem_region;
117062306a36Sopenharmony_ci		}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci		gbe_dma_addr = 0;
117362306a36Sopenharmony_ci	} else {
117462306a36Sopenharmony_ci		/* try to allocate memory with the classical allocator
117562306a36Sopenharmony_ci		 * this has high chance to fail on low memory machines */
117662306a36Sopenharmony_ci		gbe_mem = dmam_alloc_attrs(&p_dev->dev, gbe_mem_size,
117762306a36Sopenharmony_ci				&gbe_dma_addr, GFP_KERNEL,
117862306a36Sopenharmony_ci				DMA_ATTR_WRITE_COMBINE);
117962306a36Sopenharmony_ci		if (!gbe_mem) {
118062306a36Sopenharmony_ci			printk(KERN_ERR "gbefb: couldn't allocate framebuffer memory\n");
118162306a36Sopenharmony_ci			ret = -ENOMEM;
118262306a36Sopenharmony_ci			goto out_release_mem_region;
118362306a36Sopenharmony_ci		}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci		gbe_mem_phys = (unsigned long) gbe_dma_addr;
118662306a36Sopenharmony_ci	}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	par = info->par;
118962306a36Sopenharmony_ci	par->wc_cookie = arch_phys_wc_add(gbe_mem_phys, gbe_mem_size);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	/* map framebuffer memory into tiles table */
119262306a36Sopenharmony_ci	for (i = 0; i < (gbe_mem_size >> TILE_SHIFT); i++)
119362306a36Sopenharmony_ci		gbe_tiles.cpu[i] = (gbe_mem_phys >> TILE_SHIFT) + i;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	info->fbops = &gbefb_ops;
119662306a36Sopenharmony_ci	info->pseudo_palette = pseudo_palette;
119762306a36Sopenharmony_ci	info->screen_base = gbe_mem;
119862306a36Sopenharmony_ci	fb_alloc_cmap(&info->cmap, 256, 0);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	/* reset GBE */
120162306a36Sopenharmony_ci	gbe_reset();
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	/* turn on default video mode */
120462306a36Sopenharmony_ci	if (fb_find_mode(&par->var, info, mode_option, NULL, 0,
120562306a36Sopenharmony_ci			 default_mode, 8) == 0)
120662306a36Sopenharmony_ci		par->var = *default_var;
120762306a36Sopenharmony_ci	info->var = par->var;
120862306a36Sopenharmony_ci	gbefb_check_var(&par->var, info);
120962306a36Sopenharmony_ci	gbefb_encode_fix(&info->fix, &info->var);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if (register_framebuffer(info) < 0) {
121262306a36Sopenharmony_ci		printk(KERN_ERR "gbefb: couldn't register framebuffer\n");
121362306a36Sopenharmony_ci		ret = -ENXIO;
121462306a36Sopenharmony_ci		goto out_gbe_unmap;
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	platform_set_drvdata(p_dev, info);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	fb_info(info, "%s rev %d @ 0x%08x using %dkB memory\n",
122062306a36Sopenharmony_ci		info->fix.id, gbe_revision, (unsigned)GBE_BASE,
122162306a36Sopenharmony_ci		gbe_mem_size >> 10);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	return 0;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ciout_gbe_unmap:
122662306a36Sopenharmony_ci	arch_phys_wc_del(par->wc_cookie);
122762306a36Sopenharmony_ciout_release_mem_region:
122862306a36Sopenharmony_ci	release_mem_region(GBE_BASE, sizeof(struct sgi_gbe));
122962306a36Sopenharmony_ciout_release_framebuffer:
123062306a36Sopenharmony_ci	framebuffer_release(info);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	return ret;
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic void gbefb_remove(struct platform_device* p_dev)
123662306a36Sopenharmony_ci{
123762306a36Sopenharmony_ci	struct fb_info *info = platform_get_drvdata(p_dev);
123862306a36Sopenharmony_ci	struct gbefb_par *par = info->par;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	unregister_framebuffer(info);
124162306a36Sopenharmony_ci	gbe_turn_off();
124262306a36Sopenharmony_ci	arch_phys_wc_del(par->wc_cookie);
124362306a36Sopenharmony_ci	release_mem_region(GBE_BASE, sizeof(struct sgi_gbe));
124462306a36Sopenharmony_ci	framebuffer_release(info);
124562306a36Sopenharmony_ci}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_cistatic struct platform_driver gbefb_driver = {
124862306a36Sopenharmony_ci	.probe = gbefb_probe,
124962306a36Sopenharmony_ci	.remove_new = gbefb_remove,
125062306a36Sopenharmony_ci	.driver	= {
125162306a36Sopenharmony_ci		.name = "gbefb",
125262306a36Sopenharmony_ci		.dev_groups	= gbefb_groups,
125362306a36Sopenharmony_ci	},
125462306a36Sopenharmony_ci};
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_cistatic struct platform_device *gbefb_device;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_cistatic int __init gbefb_init(void)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	int ret = platform_driver_register(&gbefb_driver);
126162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_SGI_IP32) && !ret) {
126262306a36Sopenharmony_ci		gbefb_device = platform_device_alloc("gbefb", 0);
126362306a36Sopenharmony_ci		if (gbefb_device) {
126462306a36Sopenharmony_ci			ret = platform_device_add(gbefb_device);
126562306a36Sopenharmony_ci		} else {
126662306a36Sopenharmony_ci			ret = -ENOMEM;
126762306a36Sopenharmony_ci		}
126862306a36Sopenharmony_ci		if (ret) {
126962306a36Sopenharmony_ci			platform_device_put(gbefb_device);
127062306a36Sopenharmony_ci			platform_driver_unregister(&gbefb_driver);
127162306a36Sopenharmony_ci		}
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci	return ret;
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_cistatic void __exit gbefb_exit(void)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	platform_device_unregister(gbefb_device);
127962306a36Sopenharmony_ci	platform_driver_unregister(&gbefb_driver);
128062306a36Sopenharmony_ci}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_cimodule_init(gbefb_init);
128362306a36Sopenharmony_cimodule_exit(gbefb_exit);
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1286