18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  SGI GBE frame buffer driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 1999 Silicon Graphics, Inc. - Jeffrey Newquist
58c2ecf20Sopenharmony_ci *  Copyright (C) 2002 Vivien Chappelier <vivien.chappelier@linux-mips.org>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
88c2ecf20Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
98c2ecf20Sopenharmony_ci *  more details.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <linux/gfp.h>
178c2ecf20Sopenharmony_ci#include <linux/fb.h>
188c2ecf20Sopenharmony_ci#include <linux/init.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/mm.h>
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci#include <linux/io.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS
268c2ecf20Sopenharmony_ci#include <asm/addrspace.h>
278c2ecf20Sopenharmony_ci#endif
288c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
298c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <video/gbe.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic struct sgi_gbe *gbe;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct gbefb_par {
368c2ecf20Sopenharmony_ci	struct fb_var_screeninfo var;
378c2ecf20Sopenharmony_ci	struct gbe_timing_info timing;
388c2ecf20Sopenharmony_ci	int wc_cookie;
398c2ecf20Sopenharmony_ci	int valid;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define GBE_BASE	0x16000000 /* SGI O2 */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* macro for fastest write-though access to the framebuffer */
458c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS
468c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_R10000
478c2ecf20Sopenharmony_ci#define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_UNCACHED_ACCELERATED)
488c2ecf20Sopenharmony_ci#else
498c2ecf20Sopenharmony_ci#define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_CACHABLE_NO_WA)
508c2ecf20Sopenharmony_ci#endif
518c2ecf20Sopenharmony_ci#endif
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci *  RAM we reserve for the frame buffer. This defines the maximum screen
558c2ecf20Sopenharmony_ci *  size
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_ci#if CONFIG_FB_GBE_MEM > 8
588c2ecf20Sopenharmony_ci#error GBE Framebuffer cannot use more than 8MB of memory
598c2ecf20Sopenharmony_ci#endif
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define TILE_SHIFT 16
628c2ecf20Sopenharmony_ci#define TILE_SIZE (1 << TILE_SHIFT)
638c2ecf20Sopenharmony_ci#define TILE_MASK (TILE_SIZE - 1)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic unsigned int gbe_mem_size = CONFIG_FB_GBE_MEM * 1024*1024;
668c2ecf20Sopenharmony_cistatic void *gbe_mem;
678c2ecf20Sopenharmony_cistatic dma_addr_t gbe_dma_addr;
688c2ecf20Sopenharmony_cistatic unsigned long gbe_mem_phys;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic struct {
718c2ecf20Sopenharmony_ci	uint16_t *cpu;
728c2ecf20Sopenharmony_ci	dma_addr_t dma;
738c2ecf20Sopenharmony_ci} gbe_tiles;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int gbe_revision;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int ypan, ywrap;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic uint32_t pseudo_palette[16];
808c2ecf20Sopenharmony_cistatic uint32_t gbe_cmap[256];
818c2ecf20Sopenharmony_cistatic int gbe_turned_on; /* 0 turned off, 1 turned on */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic char *mode_option = NULL;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* default CRT mode */
868c2ecf20Sopenharmony_cistatic struct fb_var_screeninfo default_var_CRT = {
878c2ecf20Sopenharmony_ci	/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
888c2ecf20Sopenharmony_ci	.xres		= 640,
898c2ecf20Sopenharmony_ci	.yres		= 480,
908c2ecf20Sopenharmony_ci	.xres_virtual	= 640,
918c2ecf20Sopenharmony_ci	.yres_virtual	= 480,
928c2ecf20Sopenharmony_ci	.xoffset	= 0,
938c2ecf20Sopenharmony_ci	.yoffset	= 0,
948c2ecf20Sopenharmony_ci	.bits_per_pixel	= 8,
958c2ecf20Sopenharmony_ci	.grayscale	= 0,
968c2ecf20Sopenharmony_ci	.red		= { 0, 8, 0 },
978c2ecf20Sopenharmony_ci	.green		= { 0, 8, 0 },
988c2ecf20Sopenharmony_ci	.blue		= { 0, 8, 0 },
998c2ecf20Sopenharmony_ci	.transp		= { 0, 0, 0 },
1008c2ecf20Sopenharmony_ci	.nonstd		= 0,
1018c2ecf20Sopenharmony_ci	.activate	= 0,
1028c2ecf20Sopenharmony_ci	.height		= -1,
1038c2ecf20Sopenharmony_ci	.width		= -1,
1048c2ecf20Sopenharmony_ci	.accel_flags	= 0,
1058c2ecf20Sopenharmony_ci	.pixclock	= 39722,	/* picoseconds */
1068c2ecf20Sopenharmony_ci	.left_margin	= 48,
1078c2ecf20Sopenharmony_ci	.right_margin	= 16,
1088c2ecf20Sopenharmony_ci	.upper_margin	= 33,
1098c2ecf20Sopenharmony_ci	.lower_margin	= 10,
1108c2ecf20Sopenharmony_ci	.hsync_len	= 96,
1118c2ecf20Sopenharmony_ci	.vsync_len	= 2,
1128c2ecf20Sopenharmony_ci	.sync		= 0,
1138c2ecf20Sopenharmony_ci	.vmode		= FB_VMODE_NONINTERLACED,
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/* default LCD mode */
1178c2ecf20Sopenharmony_cistatic struct fb_var_screeninfo default_var_LCD = {
1188c2ecf20Sopenharmony_ci	/* 1600x1024, 8 bpp */
1198c2ecf20Sopenharmony_ci	.xres		= 1600,
1208c2ecf20Sopenharmony_ci	.yres		= 1024,
1218c2ecf20Sopenharmony_ci	.xres_virtual	= 1600,
1228c2ecf20Sopenharmony_ci	.yres_virtual	= 1024,
1238c2ecf20Sopenharmony_ci	.xoffset	= 0,
1248c2ecf20Sopenharmony_ci	.yoffset	= 0,
1258c2ecf20Sopenharmony_ci	.bits_per_pixel	= 8,
1268c2ecf20Sopenharmony_ci	.grayscale	= 0,
1278c2ecf20Sopenharmony_ci	.red		= { 0, 8, 0 },
1288c2ecf20Sopenharmony_ci	.green		= { 0, 8, 0 },
1298c2ecf20Sopenharmony_ci	.blue		= { 0, 8, 0 },
1308c2ecf20Sopenharmony_ci	.transp		= { 0, 0, 0 },
1318c2ecf20Sopenharmony_ci	.nonstd		= 0,
1328c2ecf20Sopenharmony_ci	.activate	= 0,
1338c2ecf20Sopenharmony_ci	.height		= -1,
1348c2ecf20Sopenharmony_ci	.width		= -1,
1358c2ecf20Sopenharmony_ci	.accel_flags	= 0,
1368c2ecf20Sopenharmony_ci	.pixclock	= 9353,
1378c2ecf20Sopenharmony_ci	.left_margin	= 20,
1388c2ecf20Sopenharmony_ci	.right_margin	= 30,
1398c2ecf20Sopenharmony_ci	.upper_margin	= 37,
1408c2ecf20Sopenharmony_ci	.lower_margin	= 3,
1418c2ecf20Sopenharmony_ci	.hsync_len	= 20,
1428c2ecf20Sopenharmony_ci	.vsync_len	= 3,
1438c2ecf20Sopenharmony_ci	.sync		= 0,
1448c2ecf20Sopenharmony_ci	.vmode		= FB_VMODE_NONINTERLACED
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/* default modedb mode */
1488c2ecf20Sopenharmony_ci/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */
1498c2ecf20Sopenharmony_cistatic struct fb_videomode default_mode_CRT = {
1508c2ecf20Sopenharmony_ci	.refresh	= 60,
1518c2ecf20Sopenharmony_ci	.xres		= 640,
1528c2ecf20Sopenharmony_ci	.yres		= 480,
1538c2ecf20Sopenharmony_ci	.pixclock	= 39722,
1548c2ecf20Sopenharmony_ci	.left_margin	= 48,
1558c2ecf20Sopenharmony_ci	.right_margin	= 16,
1568c2ecf20Sopenharmony_ci	.upper_margin	= 33,
1578c2ecf20Sopenharmony_ci	.lower_margin	= 10,
1588c2ecf20Sopenharmony_ci	.hsync_len	= 96,
1598c2ecf20Sopenharmony_ci	.vsync_len	= 2,
1608c2ecf20Sopenharmony_ci	.sync		= 0,
1618c2ecf20Sopenharmony_ci	.vmode		= FB_VMODE_NONINTERLACED,
1628c2ecf20Sopenharmony_ci};
1638c2ecf20Sopenharmony_ci/* 1600x1024 SGI flatpanel 1600sw */
1648c2ecf20Sopenharmony_cistatic struct fb_videomode default_mode_LCD = {
1658c2ecf20Sopenharmony_ci	/* 1600x1024, 8 bpp */
1668c2ecf20Sopenharmony_ci	.xres		= 1600,
1678c2ecf20Sopenharmony_ci	.yres		= 1024,
1688c2ecf20Sopenharmony_ci	.pixclock	= 9353,
1698c2ecf20Sopenharmony_ci	.left_margin	= 20,
1708c2ecf20Sopenharmony_ci	.right_margin	= 30,
1718c2ecf20Sopenharmony_ci	.upper_margin	= 37,
1728c2ecf20Sopenharmony_ci	.lower_margin	= 3,
1738c2ecf20Sopenharmony_ci	.hsync_len	= 20,
1748c2ecf20Sopenharmony_ci	.vsync_len	= 3,
1758c2ecf20Sopenharmony_ci	.vmode		= FB_VMODE_NONINTERLACED,
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic struct fb_videomode *default_mode = &default_mode_CRT;
1798c2ecf20Sopenharmony_cistatic struct fb_var_screeninfo *default_var = &default_var_CRT;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int flat_panel_enabled = 0;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void gbe_reset(void)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	/* Turn on dotclock PLL */
1868c2ecf20Sopenharmony_ci	gbe->ctrlstat = 0x300aa000;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/*
1918c2ecf20Sopenharmony_ci * Function:	gbe_turn_off
1928c2ecf20Sopenharmony_ci * Parameters:	(None)
1938c2ecf20Sopenharmony_ci * Description:	This should turn off the monitor and gbe.  This is used
1948c2ecf20Sopenharmony_ci *              when switching between the serial console and the graphics
1958c2ecf20Sopenharmony_ci *              console.
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void gbe_turn_off(void)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	int i;
2018c2ecf20Sopenharmony_ci	unsigned int val, x, y, vpixen_off;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	gbe_turned_on = 0;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* check if pixel counter is on */
2068c2ecf20Sopenharmony_ci	val = gbe->vt_xy;
2078c2ecf20Sopenharmony_ci	if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 1)
2088c2ecf20Sopenharmony_ci		return;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* turn off DMA */
2118c2ecf20Sopenharmony_ci	val = gbe->ovr_control;
2128c2ecf20Sopenharmony_ci	SET_GBE_FIELD(OVR_CONTROL, OVR_DMA_ENABLE, val, 0);
2138c2ecf20Sopenharmony_ci	gbe->ovr_control = val;
2148c2ecf20Sopenharmony_ci	udelay(1000);
2158c2ecf20Sopenharmony_ci	val = gbe->frm_control;
2168c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0);
2178c2ecf20Sopenharmony_ci	gbe->frm_control = val;
2188c2ecf20Sopenharmony_ci	udelay(1000);
2198c2ecf20Sopenharmony_ci	val = gbe->did_control;
2208c2ecf20Sopenharmony_ci	SET_GBE_FIELD(DID_CONTROL, DID_DMA_ENABLE, val, 0);
2218c2ecf20Sopenharmony_ci	gbe->did_control = val;
2228c2ecf20Sopenharmony_ci	udelay(1000);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* We have to wait through two vertical retrace periods before
2258c2ecf20Sopenharmony_ci	 * the pixel DMA is turned off for sure. */
2268c2ecf20Sopenharmony_ci	for (i = 0; i < 10000; i++) {
2278c2ecf20Sopenharmony_ci		val = gbe->frm_inhwctrl;
2288c2ecf20Sopenharmony_ci		if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val)) {
2298c2ecf20Sopenharmony_ci			udelay(10);
2308c2ecf20Sopenharmony_ci		} else {
2318c2ecf20Sopenharmony_ci			val = gbe->ovr_inhwctrl;
2328c2ecf20Sopenharmony_ci			if (GET_GBE_FIELD(OVR_INHWCTRL, OVR_DMA_ENABLE, val)) {
2338c2ecf20Sopenharmony_ci				udelay(10);
2348c2ecf20Sopenharmony_ci			} else {
2358c2ecf20Sopenharmony_ci				val = gbe->did_inhwctrl;
2368c2ecf20Sopenharmony_ci				if (GET_GBE_FIELD(DID_INHWCTRL, DID_DMA_ENABLE, val)) {
2378c2ecf20Sopenharmony_ci					udelay(10);
2388c2ecf20Sopenharmony_ci				} else
2398c2ecf20Sopenharmony_ci					break;
2408c2ecf20Sopenharmony_ci			}
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci	if (i == 10000)
2448c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: turn off DMA timed out\n");
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* wait for vpixen_off */
2478c2ecf20Sopenharmony_ci	val = gbe->vt_vpixen;
2488c2ecf20Sopenharmony_ci	vpixen_off = GET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	for (i = 0; i < 100000; i++) {
2518c2ecf20Sopenharmony_ci		val = gbe->vt_xy;
2528c2ecf20Sopenharmony_ci		x = GET_GBE_FIELD(VT_XY, X, val);
2538c2ecf20Sopenharmony_ci		y = GET_GBE_FIELD(VT_XY, Y, val);
2548c2ecf20Sopenharmony_ci		if (y < vpixen_off)
2558c2ecf20Sopenharmony_ci			break;
2568c2ecf20Sopenharmony_ci		udelay(1);
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci	if (i == 100000)
2598c2ecf20Sopenharmony_ci		printk(KERN_ERR
2608c2ecf20Sopenharmony_ci		       "gbefb: wait for vpixen_off timed out\n");
2618c2ecf20Sopenharmony_ci	for (i = 0; i < 10000; i++) {
2628c2ecf20Sopenharmony_ci		val = gbe->vt_xy;
2638c2ecf20Sopenharmony_ci		x = GET_GBE_FIELD(VT_XY, X, val);
2648c2ecf20Sopenharmony_ci		y = GET_GBE_FIELD(VT_XY, Y, val);
2658c2ecf20Sopenharmony_ci		if (y > vpixen_off)
2668c2ecf20Sopenharmony_ci			break;
2678c2ecf20Sopenharmony_ci		udelay(1);
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci	if (i == 10000)
2708c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: wait for vpixen_off timed out\n");
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/* turn off pixel counter */
2738c2ecf20Sopenharmony_ci	val = 0;
2748c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_XY, FREEZE, val, 1);
2758c2ecf20Sopenharmony_ci	gbe->vt_xy = val;
2768c2ecf20Sopenharmony_ci	mdelay(10);
2778c2ecf20Sopenharmony_ci	for (i = 0; i < 10000; i++) {
2788c2ecf20Sopenharmony_ci		val = gbe->vt_xy;
2798c2ecf20Sopenharmony_ci		if (GET_GBE_FIELD(VT_XY, FREEZE, val) != 1)
2808c2ecf20Sopenharmony_ci			udelay(10);
2818c2ecf20Sopenharmony_ci		else
2828c2ecf20Sopenharmony_ci			break;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci	if (i == 10000)
2858c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: turn off pixel clock timed out\n");
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* turn off dot clock */
2888c2ecf20Sopenharmony_ci	val = gbe->dotclock;
2898c2ecf20Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, RUN, val, 0);
2908c2ecf20Sopenharmony_ci	gbe->dotclock = val;
2918c2ecf20Sopenharmony_ci	mdelay(10);
2928c2ecf20Sopenharmony_ci	for (i = 0; i < 10000; i++) {
2938c2ecf20Sopenharmony_ci		val = gbe->dotclock;
2948c2ecf20Sopenharmony_ci		if (GET_GBE_FIELD(DOTCLK, RUN, val))
2958c2ecf20Sopenharmony_ci			udelay(10);
2968c2ecf20Sopenharmony_ci		else
2978c2ecf20Sopenharmony_ci			break;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci	if (i == 10000)
3008c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: turn off dotclock timed out\n");
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* reset the frame DMA FIFO */
3038c2ecf20Sopenharmony_ci	val = gbe->frm_size_tile;
3048c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 1);
3058c2ecf20Sopenharmony_ci	gbe->frm_size_tile = val;
3068c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 0);
3078c2ecf20Sopenharmony_ci	gbe->frm_size_tile = val;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic void gbe_turn_on(void)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	unsigned int val, i;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/*
3158c2ecf20Sopenharmony_ci	 * Check if pixel counter is off, for unknown reason this
3168c2ecf20Sopenharmony_ci	 * code hangs Visual Workstations
3178c2ecf20Sopenharmony_ci	 */
3188c2ecf20Sopenharmony_ci	if (gbe_revision < 2) {
3198c2ecf20Sopenharmony_ci		val = gbe->vt_xy;
3208c2ecf20Sopenharmony_ci		if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 0)
3218c2ecf20Sopenharmony_ci			return;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/* turn on dot clock */
3258c2ecf20Sopenharmony_ci	val = gbe->dotclock;
3268c2ecf20Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, RUN, val, 1);
3278c2ecf20Sopenharmony_ci	gbe->dotclock = val;
3288c2ecf20Sopenharmony_ci	mdelay(10);
3298c2ecf20Sopenharmony_ci	for (i = 0; i < 10000; i++) {
3308c2ecf20Sopenharmony_ci		val = gbe->dotclock;
3318c2ecf20Sopenharmony_ci		if (GET_GBE_FIELD(DOTCLK, RUN, val) != 1)
3328c2ecf20Sopenharmony_ci			udelay(10);
3338c2ecf20Sopenharmony_ci		else
3348c2ecf20Sopenharmony_ci			break;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci	if (i == 10000)
3378c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: turn on dotclock timed out\n");
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/* turn on pixel counter */
3408c2ecf20Sopenharmony_ci	val = 0;
3418c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_XY, FREEZE, val, 0);
3428c2ecf20Sopenharmony_ci	gbe->vt_xy = val;
3438c2ecf20Sopenharmony_ci	mdelay(10);
3448c2ecf20Sopenharmony_ci	for (i = 0; i < 10000; i++) {
3458c2ecf20Sopenharmony_ci		val = gbe->vt_xy;
3468c2ecf20Sopenharmony_ci		if (GET_GBE_FIELD(VT_XY, FREEZE, val))
3478c2ecf20Sopenharmony_ci			udelay(10);
3488c2ecf20Sopenharmony_ci		else
3498c2ecf20Sopenharmony_ci			break;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci	if (i == 10000)
3528c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: turn on pixel clock timed out\n");
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/* turn on DMA */
3558c2ecf20Sopenharmony_ci	val = gbe->frm_control;
3568c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 1);
3578c2ecf20Sopenharmony_ci	gbe->frm_control = val;
3588c2ecf20Sopenharmony_ci	udelay(1000);
3598c2ecf20Sopenharmony_ci	for (i = 0; i < 10000; i++) {
3608c2ecf20Sopenharmony_ci		val = gbe->frm_inhwctrl;
3618c2ecf20Sopenharmony_ci		if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val) != 1)
3628c2ecf20Sopenharmony_ci			udelay(10);
3638c2ecf20Sopenharmony_ci		else
3648c2ecf20Sopenharmony_ci			break;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	if (i == 10000)
3678c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: turn on DMA timed out\n");
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	gbe_turned_on = 1;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic void gbe_loadcmap(void)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	int i, j;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	for (i = 0; i < 256; i++) {
3778c2ecf20Sopenharmony_ci		for (j = 0; j < 1000 && gbe->cm_fifo >= 63; j++)
3788c2ecf20Sopenharmony_ci			udelay(10);
3798c2ecf20Sopenharmony_ci		if (j == 1000)
3808c2ecf20Sopenharmony_ci			printk(KERN_ERR "gbefb: cmap FIFO timeout\n");
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci		gbe->cmap[i] = gbe_cmap[i];
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/*
3878c2ecf20Sopenharmony_ci *  Blank the display.
3888c2ecf20Sopenharmony_ci */
3898c2ecf20Sopenharmony_cistatic int gbefb_blank(int blank, struct fb_info *info)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
3928c2ecf20Sopenharmony_ci	switch (blank) {
3938c2ecf20Sopenharmony_ci	case FB_BLANK_UNBLANK:		/* unblank */
3948c2ecf20Sopenharmony_ci		gbe_turn_on();
3958c2ecf20Sopenharmony_ci		gbe_loadcmap();
3968c2ecf20Sopenharmony_ci		break;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	case FB_BLANK_NORMAL:		/* blank */
3998c2ecf20Sopenharmony_ci		gbe_turn_off();
4008c2ecf20Sopenharmony_ci		break;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	default:
4038c2ecf20Sopenharmony_ci		/* Nothing */
4048c2ecf20Sopenharmony_ci		break;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci	return 0;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci/*
4108c2ecf20Sopenharmony_ci *  Setup flatpanel related registers.
4118c2ecf20Sopenharmony_ci */
4128c2ecf20Sopenharmony_cistatic void gbefb_setup_flatpanel(struct gbe_timing_info *timing)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	int fp_wid, fp_hgt, fp_vbs, fp_vbe;
4158c2ecf20Sopenharmony_ci	u32 outputVal = 0;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_FLAGS, HDRV_INVERT, outputVal,
4188c2ecf20Sopenharmony_ci		(timing->flags & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1);
4198c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_FLAGS, VDRV_INVERT, outputVal,
4208c2ecf20Sopenharmony_ci		(timing->flags & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1);
4218c2ecf20Sopenharmony_ci	gbe->vt_flags = outputVal;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* Turn on the flat panel */
4248c2ecf20Sopenharmony_ci	fp_wid = 1600;
4258c2ecf20Sopenharmony_ci	fp_hgt = 1024;
4268c2ecf20Sopenharmony_ci	fp_vbs = 0;
4278c2ecf20Sopenharmony_ci	fp_vbe = 1600;
4288c2ecf20Sopenharmony_ci	timing->pll_m = 4;
4298c2ecf20Sopenharmony_ci	timing->pll_n = 1;
4308c2ecf20Sopenharmony_ci	timing->pll_p = 0;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	outputVal = 0;
4338c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FP_DE, ON, outputVal, fp_vbs);
4348c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FP_DE, OFF, outputVal, fp_vbe);
4358c2ecf20Sopenharmony_ci	gbe->fp_de = outputVal;
4368c2ecf20Sopenharmony_ci	outputVal = 0;
4378c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FP_HDRV, OFF, outputVal, fp_wid);
4388c2ecf20Sopenharmony_ci	gbe->fp_hdrv = outputVal;
4398c2ecf20Sopenharmony_ci	outputVal = 0;
4408c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FP_VDRV, ON, outputVal, 1);
4418c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FP_VDRV, OFF, outputVal, fp_hgt + 1);
4428c2ecf20Sopenharmony_ci	gbe->fp_vdrv = outputVal;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistruct gbe_pll_info {
4468c2ecf20Sopenharmony_ci	int clock_rate;
4478c2ecf20Sopenharmony_ci	int fvco_min;
4488c2ecf20Sopenharmony_ci	int fvco_max;
4498c2ecf20Sopenharmony_ci};
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic struct gbe_pll_info gbe_pll_table[2] = {
4528c2ecf20Sopenharmony_ci	{ 20, 80, 220 },
4538c2ecf20Sopenharmony_ci	{ 27, 80, 220 },
4548c2ecf20Sopenharmony_ci};
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic int compute_gbe_timing(struct fb_var_screeninfo *var,
4578c2ecf20Sopenharmony_ci			      struct gbe_timing_info *timing)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	int pll_m, pll_n, pll_p, error, best_m, best_n, best_p, best_error;
4608c2ecf20Sopenharmony_ci	int pixclock;
4618c2ecf20Sopenharmony_ci	struct gbe_pll_info *gbe_pll;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if (gbe_revision < 2)
4648c2ecf20Sopenharmony_ci		gbe_pll = &gbe_pll_table[0];
4658c2ecf20Sopenharmony_ci	else
4668c2ecf20Sopenharmony_ci		gbe_pll = &gbe_pll_table[1];
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* Determine valid resolution and timing
4698c2ecf20Sopenharmony_ci	 * GBE crystal runs at 20Mhz or 27Mhz
4708c2ecf20Sopenharmony_ci	 * pll_m, pll_n, pll_p define the following frequencies
4718c2ecf20Sopenharmony_ci	 * fvco = pll_m * 20Mhz / pll_n
4728c2ecf20Sopenharmony_ci	 * fout = fvco / (2**pll_p) */
4738c2ecf20Sopenharmony_ci	best_error = 1000000000;
4748c2ecf20Sopenharmony_ci	best_n = best_m = best_p = 0;
4758c2ecf20Sopenharmony_ci	for (pll_p = 0; pll_p < 4; pll_p++)
4768c2ecf20Sopenharmony_ci		for (pll_m = 1; pll_m < 256; pll_m++)
4778c2ecf20Sopenharmony_ci			for (pll_n = 1; pll_n < 64; pll_n++) {
4788c2ecf20Sopenharmony_ci				pixclock = (1000000 / gbe_pll->clock_rate) *
4798c2ecf20Sopenharmony_ci						(pll_n << pll_p) / pll_m;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci				error = var->pixclock - pixclock;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci				if (error < 0)
4848c2ecf20Sopenharmony_ci					error = -error;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci				if (error < best_error &&
4878c2ecf20Sopenharmony_ci				    pll_m / pll_n >
4888c2ecf20Sopenharmony_ci				    gbe_pll->fvco_min / gbe_pll->clock_rate &&
4898c2ecf20Sopenharmony_ci 				    pll_m / pll_n <
4908c2ecf20Sopenharmony_ci				    gbe_pll->fvco_max / gbe_pll->clock_rate) {
4918c2ecf20Sopenharmony_ci					best_error = error;
4928c2ecf20Sopenharmony_ci					best_m = pll_m;
4938c2ecf20Sopenharmony_ci					best_n = pll_n;
4948c2ecf20Sopenharmony_ci					best_p = pll_p;
4958c2ecf20Sopenharmony_ci				}
4968c2ecf20Sopenharmony_ci			}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (!best_n || !best_m)
4998c2ecf20Sopenharmony_ci		return -EINVAL;	/* Resolution to high */
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	pixclock = (1000000 / gbe_pll->clock_rate) *
5028c2ecf20Sopenharmony_ci		(best_n << best_p) / best_m;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* set video timing information */
5058c2ecf20Sopenharmony_ci	if (timing) {
5068c2ecf20Sopenharmony_ci		timing->width = var->xres;
5078c2ecf20Sopenharmony_ci		timing->height = var->yres;
5088c2ecf20Sopenharmony_ci		timing->pll_m = best_m;
5098c2ecf20Sopenharmony_ci		timing->pll_n = best_n;
5108c2ecf20Sopenharmony_ci		timing->pll_p = best_p;
5118c2ecf20Sopenharmony_ci		timing->cfreq = gbe_pll->clock_rate * 1000 * timing->pll_m /
5128c2ecf20Sopenharmony_ci			(timing->pll_n << timing->pll_p);
5138c2ecf20Sopenharmony_ci		timing->htotal = var->left_margin + var->xres +
5148c2ecf20Sopenharmony_ci				var->right_margin + var->hsync_len;
5158c2ecf20Sopenharmony_ci		timing->vtotal = var->upper_margin + var->yres +
5168c2ecf20Sopenharmony_ci				var->lower_margin + var->vsync_len;
5178c2ecf20Sopenharmony_ci		timing->fields_sec = 1000 * timing->cfreq / timing->htotal *
5188c2ecf20Sopenharmony_ci				1000 / timing->vtotal;
5198c2ecf20Sopenharmony_ci		timing->hblank_start = var->xres;
5208c2ecf20Sopenharmony_ci		timing->vblank_start = var->yres;
5218c2ecf20Sopenharmony_ci		timing->hblank_end = timing->htotal;
5228c2ecf20Sopenharmony_ci		timing->hsync_start = var->xres + var->right_margin + 1;
5238c2ecf20Sopenharmony_ci		timing->hsync_end = timing->hsync_start + var->hsync_len;
5248c2ecf20Sopenharmony_ci		timing->vblank_end = timing->vtotal;
5258c2ecf20Sopenharmony_ci		timing->vsync_start = var->yres + var->lower_margin + 1;
5268c2ecf20Sopenharmony_ci		timing->vsync_end = timing->vsync_start + var->vsync_len;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	return pixclock;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic void gbe_set_timing_info(struct gbe_timing_info *timing)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	int temp;
5358c2ecf20Sopenharmony_ci	unsigned int val;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* setup dot clock PLL */
5388c2ecf20Sopenharmony_ci	val = 0;
5398c2ecf20Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, M, val, timing->pll_m - 1);
5408c2ecf20Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, N, val, timing->pll_n - 1);
5418c2ecf20Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, P, val, timing->pll_p);
5428c2ecf20Sopenharmony_ci	SET_GBE_FIELD(DOTCLK, RUN, val, 0);	/* do not start yet */
5438c2ecf20Sopenharmony_ci	gbe->dotclock = val;
5448c2ecf20Sopenharmony_ci	mdelay(10);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	/* setup pixel counter */
5478c2ecf20Sopenharmony_ci	val = 0;
5488c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_XYMAX, MAXX, val, timing->htotal);
5498c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_XYMAX, MAXY, val, timing->vtotal);
5508c2ecf20Sopenharmony_ci	gbe->vt_xymax = val;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/* setup video timing signals */
5538c2ecf20Sopenharmony_ci	val = 0;
5548c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_VSYNC, VSYNC_ON, val, timing->vsync_start);
5558c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_VSYNC, VSYNC_OFF, val, timing->vsync_end);
5568c2ecf20Sopenharmony_ci	gbe->vt_vsync = val;
5578c2ecf20Sopenharmony_ci	val = 0;
5588c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_HSYNC, HSYNC_ON, val, timing->hsync_start);
5598c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_HSYNC, HSYNC_OFF, val, timing->hsync_end);
5608c2ecf20Sopenharmony_ci	gbe->vt_hsync = val;
5618c2ecf20Sopenharmony_ci	val = 0;
5628c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_VBLANK, VBLANK_ON, val, timing->vblank_start);
5638c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_VBLANK, VBLANK_OFF, val, timing->vblank_end);
5648c2ecf20Sopenharmony_ci	gbe->vt_vblank = val;
5658c2ecf20Sopenharmony_ci	val = 0;
5668c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_HBLANK, HBLANK_ON, val,
5678c2ecf20Sopenharmony_ci		      timing->hblank_start - 5);
5688c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_HBLANK, HBLANK_OFF, val,
5698c2ecf20Sopenharmony_ci		      timing->hblank_end - 3);
5708c2ecf20Sopenharmony_ci	gbe->vt_hblank = val;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* setup internal timing signals */
5738c2ecf20Sopenharmony_ci	val = 0;
5748c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_VCMAP, VCMAP_ON, val, timing->vblank_start);
5758c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_VCMAP, VCMAP_OFF, val, timing->vblank_end);
5768c2ecf20Sopenharmony_ci	gbe->vt_vcmap = val;
5778c2ecf20Sopenharmony_ci	val = 0;
5788c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_HCMAP, HCMAP_ON, val, timing->hblank_start);
5798c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_HCMAP, HCMAP_OFF, val, timing->hblank_end);
5808c2ecf20Sopenharmony_ci	gbe->vt_hcmap = val;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	val = 0;
5838c2ecf20Sopenharmony_ci	temp = timing->vblank_start - timing->vblank_end - 1;
5848c2ecf20Sopenharmony_ci	if (temp > 0)
5858c2ecf20Sopenharmony_ci		temp = -temp;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (flat_panel_enabled)
5888c2ecf20Sopenharmony_ci		gbefb_setup_flatpanel(timing);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	SET_GBE_FIELD(DID_START_XY, DID_STARTY, val, (u32) temp);
5918c2ecf20Sopenharmony_ci	if (timing->hblank_end >= 20)
5928c2ecf20Sopenharmony_ci		SET_GBE_FIELD(DID_START_XY, DID_STARTX, val,
5938c2ecf20Sopenharmony_ci			      timing->hblank_end - 20);
5948c2ecf20Sopenharmony_ci	else
5958c2ecf20Sopenharmony_ci		SET_GBE_FIELD(DID_START_XY, DID_STARTX, val,
5968c2ecf20Sopenharmony_ci			      timing->htotal - (20 - timing->hblank_end));
5978c2ecf20Sopenharmony_ci	gbe->did_start_xy = val;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	val = 0;
6008c2ecf20Sopenharmony_ci	SET_GBE_FIELD(CRS_START_XY, CRS_STARTY, val, (u32) (temp + 1));
6018c2ecf20Sopenharmony_ci	if (timing->hblank_end >= GBE_CRS_MAGIC)
6028c2ecf20Sopenharmony_ci		SET_GBE_FIELD(CRS_START_XY, CRS_STARTX, val,
6038c2ecf20Sopenharmony_ci			      timing->hblank_end - GBE_CRS_MAGIC);
6048c2ecf20Sopenharmony_ci	else
6058c2ecf20Sopenharmony_ci		SET_GBE_FIELD(CRS_START_XY, CRS_STARTX, val,
6068c2ecf20Sopenharmony_ci			      timing->htotal - (GBE_CRS_MAGIC -
6078c2ecf20Sopenharmony_ci						timing->hblank_end));
6088c2ecf20Sopenharmony_ci	gbe->crs_start_xy = val;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	val = 0;
6118c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VC_START_XY, VC_STARTY, val, (u32) temp);
6128c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VC_START_XY, VC_STARTX, val, timing->hblank_end - 4);
6138c2ecf20Sopenharmony_ci	gbe->vc_start_xy = val;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	val = 0;
6168c2ecf20Sopenharmony_ci	temp = timing->hblank_end - GBE_PIXEN_MAGIC_ON;
6178c2ecf20Sopenharmony_ci	if (temp < 0)
6188c2ecf20Sopenharmony_ci		temp += timing->htotal;	/* allow blank to wrap around */
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_HPIXEN, HPIXEN_ON, val, temp);
6218c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_HPIXEN, HPIXEN_OFF, val,
6228c2ecf20Sopenharmony_ci		      ((temp + timing->width -
6238c2ecf20Sopenharmony_ci			GBE_PIXEN_MAGIC_OFF) % timing->htotal));
6248c2ecf20Sopenharmony_ci	gbe->vt_hpixen = val;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	val = 0;
6278c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_VPIXEN, VPIXEN_ON, val, timing->vblank_end);
6288c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val, timing->vblank_start);
6298c2ecf20Sopenharmony_ci	gbe->vt_vpixen = val;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	/* turn off sync on green */
6328c2ecf20Sopenharmony_ci	val = 0;
6338c2ecf20Sopenharmony_ci	SET_GBE_FIELD(VT_FLAGS, SYNC_LOW, val, 1);
6348c2ecf20Sopenharmony_ci	gbe->vt_flags = val;
6358c2ecf20Sopenharmony_ci}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci/*
6388c2ecf20Sopenharmony_ci *  Set the hardware according to 'par'.
6398c2ecf20Sopenharmony_ci */
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic int gbefb_set_par(struct fb_info *info)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	int i;
6448c2ecf20Sopenharmony_ci	unsigned int val;
6458c2ecf20Sopenharmony_ci	int wholeTilesX, partTilesX, maxPixelsPerTileX;
6468c2ecf20Sopenharmony_ci	int height_pix;
6478c2ecf20Sopenharmony_ci	int xpmax, ypmax;	/* Monitor resolution */
6488c2ecf20Sopenharmony_ci	int bytesPerPixel;	/* Bytes per pixel */
6498c2ecf20Sopenharmony_ci	struct gbefb_par *par = (struct gbefb_par *) info->par;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	compute_gbe_timing(&info->var, &par->timing);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	bytesPerPixel = info->var.bits_per_pixel / 8;
6548c2ecf20Sopenharmony_ci	info->fix.line_length = info->var.xres_virtual * bytesPerPixel;
6558c2ecf20Sopenharmony_ci	xpmax = par->timing.width;
6568c2ecf20Sopenharmony_ci	ypmax = par->timing.height;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	/* turn off GBE */
6598c2ecf20Sopenharmony_ci	gbe_turn_off();
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	/* set timing info */
6628c2ecf20Sopenharmony_ci	gbe_set_timing_info(&par->timing);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	/* initialize DIDs */
6658c2ecf20Sopenharmony_ci	val = 0;
6668c2ecf20Sopenharmony_ci	switch (bytesPerPixel) {
6678c2ecf20Sopenharmony_ci	case 1:
6688c2ecf20Sopenharmony_ci		SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_I8);
6698c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
6708c2ecf20Sopenharmony_ci		break;
6718c2ecf20Sopenharmony_ci	case 2:
6728c2ecf20Sopenharmony_ci		SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_ARGB5);
6738c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
6748c2ecf20Sopenharmony_ci		break;
6758c2ecf20Sopenharmony_ci	case 4:
6768c2ecf20Sopenharmony_ci		SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_RGB8);
6778c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
6788c2ecf20Sopenharmony_ci		break;
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci	SET_GBE_FIELD(WID, BUF, val, GBE_BMODE_BOTH);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i++)
6838c2ecf20Sopenharmony_ci		gbe->mode_regs[i] = val;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* Initialize interrupts */
6868c2ecf20Sopenharmony_ci	gbe->vt_intr01 = 0xffffffff;
6878c2ecf20Sopenharmony_ci	gbe->vt_intr23 = 0xffffffff;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	/* HACK:
6908c2ecf20Sopenharmony_ci	   The GBE hardware uses a tiled memory to screen mapping. Tiles are
6918c2ecf20Sopenharmony_ci	   blocks of 512x128, 256x128 or 128x128 pixels, respectively for 8bit,
6928c2ecf20Sopenharmony_ci	   16bit and 32 bit modes (64 kB). They cover the screen with partial
6938c2ecf20Sopenharmony_ci	   tiles on the right and/or bottom of the screen if needed.
6948c2ecf20Sopenharmony_ci	   For example in 640x480 8 bit mode the mapping is:
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	   <-------- 640 ----->
6978c2ecf20Sopenharmony_ci	   <---- 512 ----><128|384 offscreen>
6988c2ecf20Sopenharmony_ci	   ^  ^
6998c2ecf20Sopenharmony_ci	   | 128    [tile 0]        [tile 1]
7008c2ecf20Sopenharmony_ci	   |  v
7018c2ecf20Sopenharmony_ci	   ^
7028c2ecf20Sopenharmony_ci	   4 128    [tile 2]        [tile 3]
7038c2ecf20Sopenharmony_ci	   8  v
7048c2ecf20Sopenharmony_ci	   0  ^
7058c2ecf20Sopenharmony_ci	   128    [tile 4]        [tile 5]
7068c2ecf20Sopenharmony_ci	   |  v
7078c2ecf20Sopenharmony_ci	   |  ^
7088c2ecf20Sopenharmony_ci	   v  96    [tile 6]        [tile 7]
7098c2ecf20Sopenharmony_ci	   32 offscreen
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	   Tiles have the advantage that they can be allocated individually in
7128c2ecf20Sopenharmony_ci	   memory. However, this mapping is not linear at all, which is not
7138c2ecf20Sopenharmony_ci	   really convenient. In order to support linear addressing, the GBE
7148c2ecf20Sopenharmony_ci	   DMA hardware is fooled into thinking the screen is only one tile
7158c2ecf20Sopenharmony_ci	   large and but has a greater height, so that the DMA transfer covers
7168c2ecf20Sopenharmony_ci	   the same region.
7178c2ecf20Sopenharmony_ci	   Tiles are still allocated as independent chunks of 64KB of
7188c2ecf20Sopenharmony_ci	   continuous physical memory and remapped so that the kernel sees the
7198c2ecf20Sopenharmony_ci	   framebuffer as a continuous virtual memory. The GBE tile table is
7208c2ecf20Sopenharmony_ci	   set up so that each tile references one of these 64k blocks:
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	   GBE -> tile list    framebuffer           TLB   <------------ CPU
7238c2ecf20Sopenharmony_ci	          [ tile 0 ] -> [ 64KB ]  <- [ 16x 4KB page entries ]     ^
7248c2ecf20Sopenharmony_ci	             ...           ...              ...       linear virtual FB
7258c2ecf20Sopenharmony_ci	          [ tile n ] -> [ 64KB ]  <- [ 16x 4KB page entries ]     v
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	   The GBE hardware is then told that the buffer is 512*tweaked_height,
7298c2ecf20Sopenharmony_ci	   with tweaked_height = real_width*real_height/pixels_per_tile.
7308c2ecf20Sopenharmony_ci	   Thus the GBE hardware will scan the first tile, filing the first 64k
7318c2ecf20Sopenharmony_ci	   covered region of the screen, and then will proceed to the next
7328c2ecf20Sopenharmony_ci	   tile, until the whole screen is covered.
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	   Here is what would happen at 640x480 8bit:
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	   normal tiling               linear
7378c2ecf20Sopenharmony_ci	   ^   11111111111111112222    11111111111111111111  ^
7388c2ecf20Sopenharmony_ci	   128 11111111111111112222    11111111111111111111 102 lines
7398c2ecf20Sopenharmony_ci	       11111111111111112222    11111111111111111111  v
7408c2ecf20Sopenharmony_ci	   V   11111111111111112222    11111111222222222222
7418c2ecf20Sopenharmony_ci	       33333333333333334444    22222222222222222222
7428c2ecf20Sopenharmony_ci	       33333333333333334444    22222222222222222222
7438c2ecf20Sopenharmony_ci	       <      512     >        <  256 >               102*640+256 = 64k
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	   NOTE: The only mode for which this is not working is 800x600 8bit,
7468c2ecf20Sopenharmony_ci	   as 800*600/512 = 937.5 which is not integer and thus causes
7478c2ecf20Sopenharmony_ci	   flickering.
7488c2ecf20Sopenharmony_ci	   I guess this is not so important as one can use 640x480 8bit or
7498c2ecf20Sopenharmony_ci	   800x600 16bit anyway.
7508c2ecf20Sopenharmony_ci	 */
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	/* Tell gbe about the tiles table location */
7538c2ecf20Sopenharmony_ci	/* tile_ptr -> [ tile 1 ] -> FB mem */
7548c2ecf20Sopenharmony_ci	/*             [ tile 2 ] -> FB mem */
7558c2ecf20Sopenharmony_ci	/*               ...                */
7568c2ecf20Sopenharmony_ci	val = 0;
7578c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FRM_CONTROL, FRM_TILE_PTR, val, gbe_tiles.dma >> 9);
7588c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0); /* do not start */
7598c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FRM_CONTROL, FRM_LINEAR, val, 0);
7608c2ecf20Sopenharmony_ci	gbe->frm_control = val;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	maxPixelsPerTileX = 512 / bytesPerPixel;
7638c2ecf20Sopenharmony_ci	wholeTilesX = 1;
7648c2ecf20Sopenharmony_ci	partTilesX = 0;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	/* Initialize the framebuffer */
7678c2ecf20Sopenharmony_ci	val = 0;
7688c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_WIDTH_TILE, val, wholeTilesX);
7698c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_RHS, val, partTilesX);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	switch (bytesPerPixel) {
7728c2ecf20Sopenharmony_ci	case 1:
7738c2ecf20Sopenharmony_ci		SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val,
7748c2ecf20Sopenharmony_ci			      GBE_FRM_DEPTH_8);
7758c2ecf20Sopenharmony_ci		break;
7768c2ecf20Sopenharmony_ci	case 2:
7778c2ecf20Sopenharmony_ci		SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val,
7788c2ecf20Sopenharmony_ci			      GBE_FRM_DEPTH_16);
7798c2ecf20Sopenharmony_ci		break;
7808c2ecf20Sopenharmony_ci	case 4:
7818c2ecf20Sopenharmony_ci		SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val,
7828c2ecf20Sopenharmony_ci			      GBE_FRM_DEPTH_32);
7838c2ecf20Sopenharmony_ci		break;
7848c2ecf20Sopenharmony_ci	}
7858c2ecf20Sopenharmony_ci	gbe->frm_size_tile = val;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/* compute tweaked height */
7888c2ecf20Sopenharmony_ci	height_pix = xpmax * ypmax / maxPixelsPerTileX;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	val = 0;
7918c2ecf20Sopenharmony_ci	SET_GBE_FIELD(FRM_SIZE_PIXEL, FB_HEIGHT_PIX, val, height_pix);
7928c2ecf20Sopenharmony_ci	gbe->frm_size_pixel = val;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	/* turn off DID and overlay DMA */
7958c2ecf20Sopenharmony_ci	gbe->did_control = 0;
7968c2ecf20Sopenharmony_ci	gbe->ovr_width_tile = 0;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	/* Turn off mouse cursor */
7998c2ecf20Sopenharmony_ci	gbe->crs_ctl = 0;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	/* Turn on GBE */
8028c2ecf20Sopenharmony_ci	gbe_turn_on();
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	/* Initialize the gamma map */
8058c2ecf20Sopenharmony_ci	udelay(10);
8068c2ecf20Sopenharmony_ci	for (i = 0; i < 256; i++)
8078c2ecf20Sopenharmony_ci		gbe->gmap[i] = (i << 24) | (i << 16) | (i << 8);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	/* Initialize the color map */
8108c2ecf20Sopenharmony_ci	for (i = 0; i < 256; i++)
8118c2ecf20Sopenharmony_ci		gbe_cmap[i] = (i << 8) | (i << 16) | (i << 24);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	gbe_loadcmap();
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	return 0;
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cistatic void gbefb_encode_fix(struct fb_fix_screeninfo *fix,
8198c2ecf20Sopenharmony_ci			     struct fb_var_screeninfo *var)
8208c2ecf20Sopenharmony_ci{
8218c2ecf20Sopenharmony_ci	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
8228c2ecf20Sopenharmony_ci	strcpy(fix->id, "SGI GBE");
8238c2ecf20Sopenharmony_ci	fix->smem_start = (unsigned long) gbe_mem;
8248c2ecf20Sopenharmony_ci	fix->smem_len = gbe_mem_size;
8258c2ecf20Sopenharmony_ci	fix->type = FB_TYPE_PACKED_PIXELS;
8268c2ecf20Sopenharmony_ci	fix->type_aux = 0;
8278c2ecf20Sopenharmony_ci	fix->accel = FB_ACCEL_NONE;
8288c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
8298c2ecf20Sopenharmony_ci	case 8:
8308c2ecf20Sopenharmony_ci		fix->visual = FB_VISUAL_PSEUDOCOLOR;
8318c2ecf20Sopenharmony_ci		break;
8328c2ecf20Sopenharmony_ci	default:
8338c2ecf20Sopenharmony_ci		fix->visual = FB_VISUAL_TRUECOLOR;
8348c2ecf20Sopenharmony_ci		break;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci	fix->ywrapstep = 0;
8378c2ecf20Sopenharmony_ci	fix->xpanstep = 0;
8388c2ecf20Sopenharmony_ci	fix->ypanstep = 0;
8398c2ecf20Sopenharmony_ci	fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
8408c2ecf20Sopenharmony_ci	fix->mmio_start = GBE_BASE;
8418c2ecf20Sopenharmony_ci	fix->mmio_len = sizeof(struct sgi_gbe);
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci/*
8458c2ecf20Sopenharmony_ci *  Set a single color register. The values supplied are already
8468c2ecf20Sopenharmony_ci *  rounded down to the hardware's capabilities (according to the
8478c2ecf20Sopenharmony_ci *  entries in the var structure). Return != 0 for invalid regno.
8488c2ecf20Sopenharmony_ci */
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_cistatic int gbefb_setcolreg(unsigned regno, unsigned red, unsigned green,
8518c2ecf20Sopenharmony_ci			     unsigned blue, unsigned transp,
8528c2ecf20Sopenharmony_ci			     struct fb_info *info)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	int i;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	if (regno > 255)
8578c2ecf20Sopenharmony_ci		return 1;
8588c2ecf20Sopenharmony_ci	red >>= 8;
8598c2ecf20Sopenharmony_ci	green >>= 8;
8608c2ecf20Sopenharmony_ci	blue >>= 8;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (info->var.bits_per_pixel <= 8) {
8638c2ecf20Sopenharmony_ci		gbe_cmap[regno] = (red << 24) | (green << 16) | (blue << 8);
8648c2ecf20Sopenharmony_ci		if (gbe_turned_on) {
8658c2ecf20Sopenharmony_ci			/* wait for the color map FIFO to have a free entry */
8668c2ecf20Sopenharmony_ci			for (i = 0; i < 1000 && gbe->cm_fifo >= 63; i++)
8678c2ecf20Sopenharmony_ci				udelay(10);
8688c2ecf20Sopenharmony_ci			if (i == 1000) {
8698c2ecf20Sopenharmony_ci				printk(KERN_ERR "gbefb: cmap FIFO timeout\n");
8708c2ecf20Sopenharmony_ci				return 1;
8718c2ecf20Sopenharmony_ci			}
8728c2ecf20Sopenharmony_ci			gbe->cmap[regno] = gbe_cmap[regno];
8738c2ecf20Sopenharmony_ci		}
8748c2ecf20Sopenharmony_ci	} else if (regno < 16) {
8758c2ecf20Sopenharmony_ci		switch (info->var.bits_per_pixel) {
8768c2ecf20Sopenharmony_ci		case 15:
8778c2ecf20Sopenharmony_ci		case 16:
8788c2ecf20Sopenharmony_ci			red >>= 3;
8798c2ecf20Sopenharmony_ci			green >>= 3;
8808c2ecf20Sopenharmony_ci			blue >>= 3;
8818c2ecf20Sopenharmony_ci			pseudo_palette[regno] =
8828c2ecf20Sopenharmony_ci				(red << info->var.red.offset) |
8838c2ecf20Sopenharmony_ci				(green << info->var.green.offset) |
8848c2ecf20Sopenharmony_ci				(blue << info->var.blue.offset);
8858c2ecf20Sopenharmony_ci			break;
8868c2ecf20Sopenharmony_ci		case 32:
8878c2ecf20Sopenharmony_ci			pseudo_palette[regno] =
8888c2ecf20Sopenharmony_ci				(red << info->var.red.offset) |
8898c2ecf20Sopenharmony_ci				(green << info->var.green.offset) |
8908c2ecf20Sopenharmony_ci				(blue << info->var.blue.offset);
8918c2ecf20Sopenharmony_ci			break;
8928c2ecf20Sopenharmony_ci		}
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	return 0;
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci/*
8998c2ecf20Sopenharmony_ci *  Check video mode validity, eventually modify var to best match.
9008c2ecf20Sopenharmony_ci */
9018c2ecf20Sopenharmony_cistatic int gbefb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	unsigned int line_length;
9048c2ecf20Sopenharmony_ci	struct gbe_timing_info timing;
9058c2ecf20Sopenharmony_ci	int ret;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	/* Limit bpp to 8, 16, and 32 */
9088c2ecf20Sopenharmony_ci	if (var->bits_per_pixel <= 8)
9098c2ecf20Sopenharmony_ci		var->bits_per_pixel = 8;
9108c2ecf20Sopenharmony_ci	else if (var->bits_per_pixel <= 16)
9118c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
9128c2ecf20Sopenharmony_ci	else if (var->bits_per_pixel <= 32)
9138c2ecf20Sopenharmony_ci		var->bits_per_pixel = 32;
9148c2ecf20Sopenharmony_ci	else
9158c2ecf20Sopenharmony_ci		return -EINVAL;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/* Check the mode can be mapped linearly with the tile table trick. */
9188c2ecf20Sopenharmony_ci	/* This requires width x height x bytes/pixel be a multiple of 512 */
9198c2ecf20Sopenharmony_ci	if ((var->xres * var->yres * var->bits_per_pixel) & 4095)
9208c2ecf20Sopenharmony_ci		return -EINVAL;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	var->grayscale = 0;	/* No grayscale for now */
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	ret = compute_gbe_timing(var, &timing);
9258c2ecf20Sopenharmony_ci	var->pixclock = ret;
9268c2ecf20Sopenharmony_ci	if (ret < 0)
9278c2ecf20Sopenharmony_ci		return -EINVAL;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	/* Adjust virtual resolution, if necessary */
9308c2ecf20Sopenharmony_ci	if (var->xres > var->xres_virtual || (!ywrap && !ypan))
9318c2ecf20Sopenharmony_ci		var->xres_virtual = var->xres;
9328c2ecf20Sopenharmony_ci	if (var->yres > var->yres_virtual || (!ywrap && !ypan))
9338c2ecf20Sopenharmony_ci		var->yres_virtual = var->yres;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	if (var->vmode & FB_VMODE_CONUPDATE) {
9368c2ecf20Sopenharmony_ci		var->vmode |= FB_VMODE_YWRAP;
9378c2ecf20Sopenharmony_ci		var->xoffset = info->var.xoffset;
9388c2ecf20Sopenharmony_ci		var->yoffset = info->var.yoffset;
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	/* No grayscale for now */
9428c2ecf20Sopenharmony_ci	var->grayscale = 0;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	/* Memory limit */
9458c2ecf20Sopenharmony_ci	line_length = var->xres_virtual * var->bits_per_pixel / 8;
9468c2ecf20Sopenharmony_ci	if (line_length * var->yres_virtual > gbe_mem_size)
9478c2ecf20Sopenharmony_ci		return -ENOMEM;	/* Virtual resolution too high */
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
9508c2ecf20Sopenharmony_ci	case 8:
9518c2ecf20Sopenharmony_ci		var->red.offset = 0;
9528c2ecf20Sopenharmony_ci		var->red.length = 8;
9538c2ecf20Sopenharmony_ci		var->green.offset = 0;
9548c2ecf20Sopenharmony_ci		var->green.length = 8;
9558c2ecf20Sopenharmony_ci		var->blue.offset = 0;
9568c2ecf20Sopenharmony_ci		var->blue.length = 8;
9578c2ecf20Sopenharmony_ci		var->transp.offset = 0;
9588c2ecf20Sopenharmony_ci		var->transp.length = 0;
9598c2ecf20Sopenharmony_ci		break;
9608c2ecf20Sopenharmony_ci	case 16:		/* RGB 1555 */
9618c2ecf20Sopenharmony_ci		var->red.offset = 10;
9628c2ecf20Sopenharmony_ci		var->red.length = 5;
9638c2ecf20Sopenharmony_ci		var->green.offset = 5;
9648c2ecf20Sopenharmony_ci		var->green.length = 5;
9658c2ecf20Sopenharmony_ci		var->blue.offset = 0;
9668c2ecf20Sopenharmony_ci		var->blue.length = 5;
9678c2ecf20Sopenharmony_ci		var->transp.offset = 0;
9688c2ecf20Sopenharmony_ci		var->transp.length = 0;
9698c2ecf20Sopenharmony_ci		break;
9708c2ecf20Sopenharmony_ci	case 32:		/* RGB 8888 */
9718c2ecf20Sopenharmony_ci		var->red.offset = 24;
9728c2ecf20Sopenharmony_ci		var->red.length = 8;
9738c2ecf20Sopenharmony_ci		var->green.offset = 16;
9748c2ecf20Sopenharmony_ci		var->green.length = 8;
9758c2ecf20Sopenharmony_ci		var->blue.offset = 8;
9768c2ecf20Sopenharmony_ci		var->blue.length = 8;
9778c2ecf20Sopenharmony_ci		var->transp.offset = 0;
9788c2ecf20Sopenharmony_ci		var->transp.length = 8;
9798c2ecf20Sopenharmony_ci		break;
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci	var->red.msb_right = 0;
9828c2ecf20Sopenharmony_ci	var->green.msb_right = 0;
9838c2ecf20Sopenharmony_ci	var->blue.msb_right = 0;
9848c2ecf20Sopenharmony_ci	var->transp.msb_right = 0;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	var->left_margin = timing.htotal - timing.hsync_end;
9878c2ecf20Sopenharmony_ci	var->right_margin = timing.hsync_start - timing.width;
9888c2ecf20Sopenharmony_ci	var->upper_margin = timing.vtotal - timing.vsync_end;
9898c2ecf20Sopenharmony_ci	var->lower_margin = timing.vsync_start - timing.height;
9908c2ecf20Sopenharmony_ci	var->hsync_len = timing.hsync_end - timing.hsync_start;
9918c2ecf20Sopenharmony_ci	var->vsync_len = timing.vsync_end - timing.vsync_start;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	return 0;
9948c2ecf20Sopenharmony_ci}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_cistatic int gbefb_mmap(struct fb_info *info,
9978c2ecf20Sopenharmony_ci			struct vm_area_struct *vma)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	unsigned long size = vma->vm_end - vma->vm_start;
10008c2ecf20Sopenharmony_ci	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
10018c2ecf20Sopenharmony_ci	unsigned long addr;
10028c2ecf20Sopenharmony_ci	unsigned long phys_addr, phys_size;
10038c2ecf20Sopenharmony_ci	u16 *tile;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	/* check range */
10068c2ecf20Sopenharmony_ci	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
10078c2ecf20Sopenharmony_ci		return -EINVAL;
10088c2ecf20Sopenharmony_ci	if (size > gbe_mem_size)
10098c2ecf20Sopenharmony_ci		return -EINVAL;
10108c2ecf20Sopenharmony_ci	if (offset > gbe_mem_size - size)
10118c2ecf20Sopenharmony_ci		return -EINVAL;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	/* remap using the fastest write-through mode on architecture */
10148c2ecf20Sopenharmony_ci	/* try not polluting the cache when possible */
10158c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS
10168c2ecf20Sopenharmony_ci	pgprot_val(vma->vm_page_prot) =
10178c2ecf20Sopenharmony_ci		pgprot_fb(pgprot_val(vma->vm_page_prot));
10188c2ecf20Sopenharmony_ci#endif
10198c2ecf20Sopenharmony_ci	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	/* look for the starting tile */
10228c2ecf20Sopenharmony_ci	tile = &gbe_tiles.cpu[offset >> TILE_SHIFT];
10238c2ecf20Sopenharmony_ci	addr = vma->vm_start;
10248c2ecf20Sopenharmony_ci	offset &= TILE_MASK;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	/* remap each tile separately */
10278c2ecf20Sopenharmony_ci	do {
10288c2ecf20Sopenharmony_ci		phys_addr = (((unsigned long) (*tile)) << TILE_SHIFT) + offset;
10298c2ecf20Sopenharmony_ci		if ((offset + size) < TILE_SIZE)
10308c2ecf20Sopenharmony_ci			phys_size = size;
10318c2ecf20Sopenharmony_ci		else
10328c2ecf20Sopenharmony_ci			phys_size = TILE_SIZE - offset;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci		if (remap_pfn_range(vma, addr, phys_addr >> PAGE_SHIFT,
10358c2ecf20Sopenharmony_ci						phys_size, vma->vm_page_prot))
10368c2ecf20Sopenharmony_ci			return -EAGAIN;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci		offset = 0;
10398c2ecf20Sopenharmony_ci		size -= phys_size;
10408c2ecf20Sopenharmony_ci		addr += phys_size;
10418c2ecf20Sopenharmony_ci		tile++;
10428c2ecf20Sopenharmony_ci	} while (size);
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	return 0;
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic const struct fb_ops gbefb_ops = {
10488c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
10498c2ecf20Sopenharmony_ci	.fb_check_var	= gbefb_check_var,
10508c2ecf20Sopenharmony_ci	.fb_set_par	= gbefb_set_par,
10518c2ecf20Sopenharmony_ci	.fb_setcolreg	= gbefb_setcolreg,
10528c2ecf20Sopenharmony_ci	.fb_mmap	= gbefb_mmap,
10538c2ecf20Sopenharmony_ci	.fb_blank	= gbefb_blank,
10548c2ecf20Sopenharmony_ci	.fb_fillrect	= cfb_fillrect,
10558c2ecf20Sopenharmony_ci	.fb_copyarea	= cfb_copyarea,
10568c2ecf20Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
10578c2ecf20Sopenharmony_ci};
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci/*
10608c2ecf20Sopenharmony_ci * sysfs
10618c2ecf20Sopenharmony_ci */
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_cistatic ssize_t gbefb_show_memsize(struct device *dev, struct device_attribute *attr, char *buf)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%u\n", gbe_mem_size);
10668c2ecf20Sopenharmony_ci}
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_cistatic DEVICE_ATTR(size, S_IRUGO, gbefb_show_memsize, NULL);
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic ssize_t gbefb_show_rev(struct device *device, struct device_attribute *attr, char *buf)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", gbe_revision);
10738c2ecf20Sopenharmony_ci}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_cistatic DEVICE_ATTR(revision, S_IRUGO, gbefb_show_rev, NULL);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_cistatic void gbefb_remove_sysfs(struct device *dev)
10788c2ecf20Sopenharmony_ci{
10798c2ecf20Sopenharmony_ci	device_remove_file(dev, &dev_attr_size);
10808c2ecf20Sopenharmony_ci	device_remove_file(dev, &dev_attr_revision);
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cistatic void gbefb_create_sysfs(struct device *dev)
10848c2ecf20Sopenharmony_ci{
10858c2ecf20Sopenharmony_ci	device_create_file(dev, &dev_attr_size);
10868c2ecf20Sopenharmony_ci	device_create_file(dev, &dev_attr_revision);
10878c2ecf20Sopenharmony_ci}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci/*
10908c2ecf20Sopenharmony_ci * Initialization
10918c2ecf20Sopenharmony_ci */
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic int gbefb_setup(char *options)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	char *this_opt;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	if (!options || !*options)
10988c2ecf20Sopenharmony_ci		return 0;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
11018c2ecf20Sopenharmony_ci		if (!strncmp(this_opt, "monitor:", 8)) {
11028c2ecf20Sopenharmony_ci			if (!strncmp(this_opt + 8, "crt", 3)) {
11038c2ecf20Sopenharmony_ci				flat_panel_enabled = 0;
11048c2ecf20Sopenharmony_ci				default_var = &default_var_CRT;
11058c2ecf20Sopenharmony_ci				default_mode = &default_mode_CRT;
11068c2ecf20Sopenharmony_ci			} else if (!strncmp(this_opt + 8, "1600sw", 6) ||
11078c2ecf20Sopenharmony_ci				   !strncmp(this_opt + 8, "lcd", 3)) {
11088c2ecf20Sopenharmony_ci				flat_panel_enabled = 1;
11098c2ecf20Sopenharmony_ci				default_var = &default_var_LCD;
11108c2ecf20Sopenharmony_ci				default_mode = &default_mode_LCD;
11118c2ecf20Sopenharmony_ci			}
11128c2ecf20Sopenharmony_ci		} else if (!strncmp(this_opt, "mem:", 4)) {
11138c2ecf20Sopenharmony_ci			gbe_mem_size = memparse(this_opt + 4, &this_opt);
11148c2ecf20Sopenharmony_ci			if (gbe_mem_size > CONFIG_FB_GBE_MEM * 1024 * 1024)
11158c2ecf20Sopenharmony_ci				gbe_mem_size = CONFIG_FB_GBE_MEM * 1024 * 1024;
11168c2ecf20Sopenharmony_ci			if (gbe_mem_size < TILE_SIZE)
11178c2ecf20Sopenharmony_ci				gbe_mem_size = TILE_SIZE;
11188c2ecf20Sopenharmony_ci		} else
11198c2ecf20Sopenharmony_ci			mode_option = this_opt;
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ci	return 0;
11228c2ecf20Sopenharmony_ci}
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_cistatic int gbefb_probe(struct platform_device *p_dev)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	int i, ret = 0;
11278c2ecf20Sopenharmony_ci	struct fb_info *info;
11288c2ecf20Sopenharmony_ci	struct gbefb_par *par;
11298c2ecf20Sopenharmony_ci#ifndef MODULE
11308c2ecf20Sopenharmony_ci	char *options = NULL;
11318c2ecf20Sopenharmony_ci#endif
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct gbefb_par), &p_dev->dev);
11348c2ecf20Sopenharmony_ci	if (!info)
11358c2ecf20Sopenharmony_ci		return -ENOMEM;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci#ifndef MODULE
11388c2ecf20Sopenharmony_ci	if (fb_get_options("gbefb", &options)) {
11398c2ecf20Sopenharmony_ci		ret = -ENODEV;
11408c2ecf20Sopenharmony_ci		goto out_release_framebuffer;
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci	gbefb_setup(options);
11438c2ecf20Sopenharmony_ci#endif
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	if (!request_mem_region(GBE_BASE, sizeof(struct sgi_gbe), "GBE")) {
11468c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: couldn't reserve mmio region\n");
11478c2ecf20Sopenharmony_ci		ret = -EBUSY;
11488c2ecf20Sopenharmony_ci		goto out_release_framebuffer;
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	gbe = (struct sgi_gbe *) devm_ioremap(&p_dev->dev, GBE_BASE,
11528c2ecf20Sopenharmony_ci					      sizeof(struct sgi_gbe));
11538c2ecf20Sopenharmony_ci	if (!gbe) {
11548c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: couldn't map mmio region\n");
11558c2ecf20Sopenharmony_ci		ret = -ENXIO;
11568c2ecf20Sopenharmony_ci		goto out_release_mem_region;
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci	gbe_revision = gbe->ctrlstat & 15;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	gbe_tiles.cpu = dmam_alloc_coherent(&p_dev->dev,
11618c2ecf20Sopenharmony_ci				GBE_TLB_SIZE * sizeof(uint16_t),
11628c2ecf20Sopenharmony_ci				&gbe_tiles.dma, GFP_KERNEL);
11638c2ecf20Sopenharmony_ci	if (!gbe_tiles.cpu) {
11648c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: couldn't allocate tiles table\n");
11658c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11668c2ecf20Sopenharmony_ci		goto out_release_mem_region;
11678c2ecf20Sopenharmony_ci	}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	if (gbe_mem_phys) {
11708c2ecf20Sopenharmony_ci		/* memory was allocated at boot time */
11718c2ecf20Sopenharmony_ci		gbe_mem = devm_ioremap_wc(&p_dev->dev, gbe_mem_phys,
11728c2ecf20Sopenharmony_ci					  gbe_mem_size);
11738c2ecf20Sopenharmony_ci		if (!gbe_mem) {
11748c2ecf20Sopenharmony_ci			printk(KERN_ERR "gbefb: couldn't map framebuffer\n");
11758c2ecf20Sopenharmony_ci			ret = -ENOMEM;
11768c2ecf20Sopenharmony_ci			goto out_release_mem_region;
11778c2ecf20Sopenharmony_ci		}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci		gbe_dma_addr = 0;
11808c2ecf20Sopenharmony_ci	} else {
11818c2ecf20Sopenharmony_ci		/* try to allocate memory with the classical allocator
11828c2ecf20Sopenharmony_ci		 * this has high chance to fail on low memory machines */
11838c2ecf20Sopenharmony_ci		gbe_mem = dmam_alloc_attrs(&p_dev->dev, gbe_mem_size,
11848c2ecf20Sopenharmony_ci				&gbe_dma_addr, GFP_KERNEL,
11858c2ecf20Sopenharmony_ci				DMA_ATTR_WRITE_COMBINE);
11868c2ecf20Sopenharmony_ci		if (!gbe_mem) {
11878c2ecf20Sopenharmony_ci			printk(KERN_ERR "gbefb: couldn't allocate framebuffer memory\n");
11888c2ecf20Sopenharmony_ci			ret = -ENOMEM;
11898c2ecf20Sopenharmony_ci			goto out_release_mem_region;
11908c2ecf20Sopenharmony_ci		}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci		gbe_mem_phys = (unsigned long) gbe_dma_addr;
11938c2ecf20Sopenharmony_ci	}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	par = info->par;
11968c2ecf20Sopenharmony_ci	par->wc_cookie = arch_phys_wc_add(gbe_mem_phys, gbe_mem_size);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	/* map framebuffer memory into tiles table */
11998c2ecf20Sopenharmony_ci	for (i = 0; i < (gbe_mem_size >> TILE_SHIFT); i++)
12008c2ecf20Sopenharmony_ci		gbe_tiles.cpu[i] = (gbe_mem_phys >> TILE_SHIFT) + i;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	info->fbops = &gbefb_ops;
12038c2ecf20Sopenharmony_ci	info->pseudo_palette = pseudo_palette;
12048c2ecf20Sopenharmony_ci	info->flags = FBINFO_DEFAULT;
12058c2ecf20Sopenharmony_ci	info->screen_base = gbe_mem;
12068c2ecf20Sopenharmony_ci	fb_alloc_cmap(&info->cmap, 256, 0);
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	/* reset GBE */
12098c2ecf20Sopenharmony_ci	gbe_reset();
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	/* turn on default video mode */
12128c2ecf20Sopenharmony_ci	if (fb_find_mode(&par->var, info, mode_option, NULL, 0,
12138c2ecf20Sopenharmony_ci			 default_mode, 8) == 0)
12148c2ecf20Sopenharmony_ci		par->var = *default_var;
12158c2ecf20Sopenharmony_ci	info->var = par->var;
12168c2ecf20Sopenharmony_ci	gbefb_check_var(&par->var, info);
12178c2ecf20Sopenharmony_ci	gbefb_encode_fix(&info->fix, &info->var);
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	if (register_framebuffer(info) < 0) {
12208c2ecf20Sopenharmony_ci		printk(KERN_ERR "gbefb: couldn't register framebuffer\n");
12218c2ecf20Sopenharmony_ci		ret = -ENXIO;
12228c2ecf20Sopenharmony_ci		goto out_gbe_unmap;
12238c2ecf20Sopenharmony_ci	}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	platform_set_drvdata(p_dev, info);
12268c2ecf20Sopenharmony_ci	gbefb_create_sysfs(&p_dev->dev);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	fb_info(info, "%s rev %d @ 0x%08x using %dkB memory\n",
12298c2ecf20Sopenharmony_ci		info->fix.id, gbe_revision, (unsigned)GBE_BASE,
12308c2ecf20Sopenharmony_ci		gbe_mem_size >> 10);
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	return 0;
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ciout_gbe_unmap:
12358c2ecf20Sopenharmony_ci	arch_phys_wc_del(par->wc_cookie);
12368c2ecf20Sopenharmony_ciout_release_mem_region:
12378c2ecf20Sopenharmony_ci	release_mem_region(GBE_BASE, sizeof(struct sgi_gbe));
12388c2ecf20Sopenharmony_ciout_release_framebuffer:
12398c2ecf20Sopenharmony_ci	framebuffer_release(info);
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	return ret;
12428c2ecf20Sopenharmony_ci}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cistatic int gbefb_remove(struct platform_device* p_dev)
12458c2ecf20Sopenharmony_ci{
12468c2ecf20Sopenharmony_ci	struct fb_info *info = platform_get_drvdata(p_dev);
12478c2ecf20Sopenharmony_ci	struct gbefb_par *par = info->par;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	unregister_framebuffer(info);
12508c2ecf20Sopenharmony_ci	gbe_turn_off();
12518c2ecf20Sopenharmony_ci	arch_phys_wc_del(par->wc_cookie);
12528c2ecf20Sopenharmony_ci	release_mem_region(GBE_BASE, sizeof(struct sgi_gbe));
12538c2ecf20Sopenharmony_ci	gbefb_remove_sysfs(&p_dev->dev);
12548c2ecf20Sopenharmony_ci	framebuffer_release(info);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	return 0;
12578c2ecf20Sopenharmony_ci}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_cistatic struct platform_driver gbefb_driver = {
12608c2ecf20Sopenharmony_ci	.probe = gbefb_probe,
12618c2ecf20Sopenharmony_ci	.remove = gbefb_remove,
12628c2ecf20Sopenharmony_ci	.driver	= {
12638c2ecf20Sopenharmony_ci		.name = "gbefb",
12648c2ecf20Sopenharmony_ci	},
12658c2ecf20Sopenharmony_ci};
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_cistatic struct platform_device *gbefb_device;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_cistatic int __init gbefb_init(void)
12708c2ecf20Sopenharmony_ci{
12718c2ecf20Sopenharmony_ci	int ret = platform_driver_register(&gbefb_driver);
12728c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_SGI_IP32) && !ret) {
12738c2ecf20Sopenharmony_ci		gbefb_device = platform_device_alloc("gbefb", 0);
12748c2ecf20Sopenharmony_ci		if (gbefb_device) {
12758c2ecf20Sopenharmony_ci			ret = platform_device_add(gbefb_device);
12768c2ecf20Sopenharmony_ci		} else {
12778c2ecf20Sopenharmony_ci			ret = -ENOMEM;
12788c2ecf20Sopenharmony_ci		}
12798c2ecf20Sopenharmony_ci		if (ret) {
12808c2ecf20Sopenharmony_ci			platform_device_put(gbefb_device);
12818c2ecf20Sopenharmony_ci			platform_driver_unregister(&gbefb_driver);
12828c2ecf20Sopenharmony_ci		}
12838c2ecf20Sopenharmony_ci	}
12848c2ecf20Sopenharmony_ci	return ret;
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_cistatic void __exit gbefb_exit(void)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	platform_device_unregister(gbefb_device);
12908c2ecf20Sopenharmony_ci	platform_driver_unregister(&gbefb_driver);
12918c2ecf20Sopenharmony_ci}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_cimodule_init(gbefb_init);
12948c2ecf20Sopenharmony_cimodule_exit(gbefb_exit);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1297