xref: /OK3568_Linux_fs/kernel/drivers/video/fbdev/gbefb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  *  SGI GBE frame buffer driver
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  *  Copyright (C) 1999 Silicon Graphics, Inc. - Jeffrey Newquist
5*4882a593Smuzhiyun  *  Copyright (C) 2002 Vivien Chappelier <vivien.chappelier@linux-mips.org>
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *  This file is subject to the terms and conditions of the GNU General Public
8*4882a593Smuzhiyun  *  License. See the file COPYING in the main directory of this archive for
9*4882a593Smuzhiyun  *  more details.
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/delay.h>
13*4882a593Smuzhiyun #include <linux/platform_device.h>
14*4882a593Smuzhiyun #include <linux/dma-mapping.h>
15*4882a593Smuzhiyun #include <linux/errno.h>
16*4882a593Smuzhiyun #include <linux/gfp.h>
17*4882a593Smuzhiyun #include <linux/fb.h>
18*4882a593Smuzhiyun #include <linux/init.h>
19*4882a593Smuzhiyun #include <linux/interrupt.h>
20*4882a593Smuzhiyun #include <linux/kernel.h>
21*4882a593Smuzhiyun #include <linux/mm.h>
22*4882a593Smuzhiyun #include <linux/module.h>
23*4882a593Smuzhiyun #include <linux/io.h>
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #ifdef CONFIG_MIPS
26*4882a593Smuzhiyun #include <asm/addrspace.h>
27*4882a593Smuzhiyun #endif
28*4882a593Smuzhiyun #include <asm/byteorder.h>
29*4882a593Smuzhiyun #include <asm/tlbflush.h>
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #include <video/gbe.h>
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun static struct sgi_gbe *gbe;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun struct gbefb_par {
36*4882a593Smuzhiyun 	struct fb_var_screeninfo var;
37*4882a593Smuzhiyun 	struct gbe_timing_info timing;
38*4882a593Smuzhiyun 	int wc_cookie;
39*4882a593Smuzhiyun 	int valid;
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun #define GBE_BASE	0x16000000 /* SGI O2 */
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun /* macro for fastest write-though access to the framebuffer */
45*4882a593Smuzhiyun #ifdef CONFIG_MIPS
46*4882a593Smuzhiyun #ifdef CONFIG_CPU_R10000
47*4882a593Smuzhiyun #define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_UNCACHED_ACCELERATED)
48*4882a593Smuzhiyun #else
49*4882a593Smuzhiyun #define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_CACHABLE_NO_WA)
50*4882a593Smuzhiyun #endif
51*4882a593Smuzhiyun #endif
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun /*
54*4882a593Smuzhiyun  *  RAM we reserve for the frame buffer. This defines the maximum screen
55*4882a593Smuzhiyun  *  size
56*4882a593Smuzhiyun  */
57*4882a593Smuzhiyun #if CONFIG_FB_GBE_MEM > 8
58*4882a593Smuzhiyun #error GBE Framebuffer cannot use more than 8MB of memory
59*4882a593Smuzhiyun #endif
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun #define TILE_SHIFT 16
62*4882a593Smuzhiyun #define TILE_SIZE (1 << TILE_SHIFT)
63*4882a593Smuzhiyun #define TILE_MASK (TILE_SIZE - 1)
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun static unsigned int gbe_mem_size = CONFIG_FB_GBE_MEM * 1024*1024;
66*4882a593Smuzhiyun static void *gbe_mem;
67*4882a593Smuzhiyun static dma_addr_t gbe_dma_addr;
68*4882a593Smuzhiyun static unsigned long gbe_mem_phys;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun static struct {
71*4882a593Smuzhiyun 	uint16_t *cpu;
72*4882a593Smuzhiyun 	dma_addr_t dma;
73*4882a593Smuzhiyun } gbe_tiles;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun static int gbe_revision;
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun static int ypan, ywrap;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun static uint32_t pseudo_palette[16];
80*4882a593Smuzhiyun static uint32_t gbe_cmap[256];
81*4882a593Smuzhiyun static int gbe_turned_on; /* 0 turned off, 1 turned on */
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun static char *mode_option = NULL;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun /* default CRT mode */
86*4882a593Smuzhiyun static struct fb_var_screeninfo default_var_CRT = {
87*4882a593Smuzhiyun 	/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
88*4882a593Smuzhiyun 	.xres		= 640,
89*4882a593Smuzhiyun 	.yres		= 480,
90*4882a593Smuzhiyun 	.xres_virtual	= 640,
91*4882a593Smuzhiyun 	.yres_virtual	= 480,
92*4882a593Smuzhiyun 	.xoffset	= 0,
93*4882a593Smuzhiyun 	.yoffset	= 0,
94*4882a593Smuzhiyun 	.bits_per_pixel	= 8,
95*4882a593Smuzhiyun 	.grayscale	= 0,
96*4882a593Smuzhiyun 	.red		= { 0, 8, 0 },
97*4882a593Smuzhiyun 	.green		= { 0, 8, 0 },
98*4882a593Smuzhiyun 	.blue		= { 0, 8, 0 },
99*4882a593Smuzhiyun 	.transp		= { 0, 0, 0 },
100*4882a593Smuzhiyun 	.nonstd		= 0,
101*4882a593Smuzhiyun 	.activate	= 0,
102*4882a593Smuzhiyun 	.height		= -1,
103*4882a593Smuzhiyun 	.width		= -1,
104*4882a593Smuzhiyun 	.accel_flags	= 0,
105*4882a593Smuzhiyun 	.pixclock	= 39722,	/* picoseconds */
106*4882a593Smuzhiyun 	.left_margin	= 48,
107*4882a593Smuzhiyun 	.right_margin	= 16,
108*4882a593Smuzhiyun 	.upper_margin	= 33,
109*4882a593Smuzhiyun 	.lower_margin	= 10,
110*4882a593Smuzhiyun 	.hsync_len	= 96,
111*4882a593Smuzhiyun 	.vsync_len	= 2,
112*4882a593Smuzhiyun 	.sync		= 0,
113*4882a593Smuzhiyun 	.vmode		= FB_VMODE_NONINTERLACED,
114*4882a593Smuzhiyun };
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun /* default LCD mode */
117*4882a593Smuzhiyun static struct fb_var_screeninfo default_var_LCD = {
118*4882a593Smuzhiyun 	/* 1600x1024, 8 bpp */
119*4882a593Smuzhiyun 	.xres		= 1600,
120*4882a593Smuzhiyun 	.yres		= 1024,
121*4882a593Smuzhiyun 	.xres_virtual	= 1600,
122*4882a593Smuzhiyun 	.yres_virtual	= 1024,
123*4882a593Smuzhiyun 	.xoffset	= 0,
124*4882a593Smuzhiyun 	.yoffset	= 0,
125*4882a593Smuzhiyun 	.bits_per_pixel	= 8,
126*4882a593Smuzhiyun 	.grayscale	= 0,
127*4882a593Smuzhiyun 	.red		= { 0, 8, 0 },
128*4882a593Smuzhiyun 	.green		= { 0, 8, 0 },
129*4882a593Smuzhiyun 	.blue		= { 0, 8, 0 },
130*4882a593Smuzhiyun 	.transp		= { 0, 0, 0 },
131*4882a593Smuzhiyun 	.nonstd		= 0,
132*4882a593Smuzhiyun 	.activate	= 0,
133*4882a593Smuzhiyun 	.height		= -1,
134*4882a593Smuzhiyun 	.width		= -1,
135*4882a593Smuzhiyun 	.accel_flags	= 0,
136*4882a593Smuzhiyun 	.pixclock	= 9353,
137*4882a593Smuzhiyun 	.left_margin	= 20,
138*4882a593Smuzhiyun 	.right_margin	= 30,
139*4882a593Smuzhiyun 	.upper_margin	= 37,
140*4882a593Smuzhiyun 	.lower_margin	= 3,
141*4882a593Smuzhiyun 	.hsync_len	= 20,
142*4882a593Smuzhiyun 	.vsync_len	= 3,
143*4882a593Smuzhiyun 	.sync		= 0,
144*4882a593Smuzhiyun 	.vmode		= FB_VMODE_NONINTERLACED
145*4882a593Smuzhiyun };
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun /* default modedb mode */
148*4882a593Smuzhiyun /* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */
149*4882a593Smuzhiyun static struct fb_videomode default_mode_CRT = {
150*4882a593Smuzhiyun 	.refresh	= 60,
151*4882a593Smuzhiyun 	.xres		= 640,
152*4882a593Smuzhiyun 	.yres		= 480,
153*4882a593Smuzhiyun 	.pixclock	= 39722,
154*4882a593Smuzhiyun 	.left_margin	= 48,
155*4882a593Smuzhiyun 	.right_margin	= 16,
156*4882a593Smuzhiyun 	.upper_margin	= 33,
157*4882a593Smuzhiyun 	.lower_margin	= 10,
158*4882a593Smuzhiyun 	.hsync_len	= 96,
159*4882a593Smuzhiyun 	.vsync_len	= 2,
160*4882a593Smuzhiyun 	.sync		= 0,
161*4882a593Smuzhiyun 	.vmode		= FB_VMODE_NONINTERLACED,
162*4882a593Smuzhiyun };
163*4882a593Smuzhiyun /* 1600x1024 SGI flatpanel 1600sw */
164*4882a593Smuzhiyun static struct fb_videomode default_mode_LCD = {
165*4882a593Smuzhiyun 	/* 1600x1024, 8 bpp */
166*4882a593Smuzhiyun 	.xres		= 1600,
167*4882a593Smuzhiyun 	.yres		= 1024,
168*4882a593Smuzhiyun 	.pixclock	= 9353,
169*4882a593Smuzhiyun 	.left_margin	= 20,
170*4882a593Smuzhiyun 	.right_margin	= 30,
171*4882a593Smuzhiyun 	.upper_margin	= 37,
172*4882a593Smuzhiyun 	.lower_margin	= 3,
173*4882a593Smuzhiyun 	.hsync_len	= 20,
174*4882a593Smuzhiyun 	.vsync_len	= 3,
175*4882a593Smuzhiyun 	.vmode		= FB_VMODE_NONINTERLACED,
176*4882a593Smuzhiyun };
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun static struct fb_videomode *default_mode = &default_mode_CRT;
179*4882a593Smuzhiyun static struct fb_var_screeninfo *default_var = &default_var_CRT;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun static int flat_panel_enabled = 0;
182*4882a593Smuzhiyun 
gbe_reset(void)183*4882a593Smuzhiyun static void gbe_reset(void)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun 	/* Turn on dotclock PLL */
186*4882a593Smuzhiyun 	gbe->ctrlstat = 0x300aa000;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun /*
191*4882a593Smuzhiyun  * Function:	gbe_turn_off
192*4882a593Smuzhiyun  * Parameters:	(None)
193*4882a593Smuzhiyun  * Description:	This should turn off the monitor and gbe.  This is used
194*4882a593Smuzhiyun  *              when switching between the serial console and the graphics
195*4882a593Smuzhiyun  *              console.
196*4882a593Smuzhiyun  */
197*4882a593Smuzhiyun 
gbe_turn_off(void)198*4882a593Smuzhiyun static void gbe_turn_off(void)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	int i;
201*4882a593Smuzhiyun 	unsigned int val, x, y, vpixen_off;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	gbe_turned_on = 0;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	/* check if pixel counter is on */
206*4882a593Smuzhiyun 	val = gbe->vt_xy;
207*4882a593Smuzhiyun 	if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 1)
208*4882a593Smuzhiyun 		return;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	/* turn off DMA */
211*4882a593Smuzhiyun 	val = gbe->ovr_control;
212*4882a593Smuzhiyun 	SET_GBE_FIELD(OVR_CONTROL, OVR_DMA_ENABLE, val, 0);
213*4882a593Smuzhiyun 	gbe->ovr_control = val;
214*4882a593Smuzhiyun 	udelay(1000);
215*4882a593Smuzhiyun 	val = gbe->frm_control;
216*4882a593Smuzhiyun 	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0);
217*4882a593Smuzhiyun 	gbe->frm_control = val;
218*4882a593Smuzhiyun 	udelay(1000);
219*4882a593Smuzhiyun 	val = gbe->did_control;
220*4882a593Smuzhiyun 	SET_GBE_FIELD(DID_CONTROL, DID_DMA_ENABLE, val, 0);
221*4882a593Smuzhiyun 	gbe->did_control = val;
222*4882a593Smuzhiyun 	udelay(1000);
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	/* We have to wait through two vertical retrace periods before
225*4882a593Smuzhiyun 	 * the pixel DMA is turned off for sure. */
226*4882a593Smuzhiyun 	for (i = 0; i < 10000; i++) {
227*4882a593Smuzhiyun 		val = gbe->frm_inhwctrl;
228*4882a593Smuzhiyun 		if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val)) {
229*4882a593Smuzhiyun 			udelay(10);
230*4882a593Smuzhiyun 		} else {
231*4882a593Smuzhiyun 			val = gbe->ovr_inhwctrl;
232*4882a593Smuzhiyun 			if (GET_GBE_FIELD(OVR_INHWCTRL, OVR_DMA_ENABLE, val)) {
233*4882a593Smuzhiyun 				udelay(10);
234*4882a593Smuzhiyun 			} else {
235*4882a593Smuzhiyun 				val = gbe->did_inhwctrl;
236*4882a593Smuzhiyun 				if (GET_GBE_FIELD(DID_INHWCTRL, DID_DMA_ENABLE, val)) {
237*4882a593Smuzhiyun 					udelay(10);
238*4882a593Smuzhiyun 				} else
239*4882a593Smuzhiyun 					break;
240*4882a593Smuzhiyun 			}
241*4882a593Smuzhiyun 		}
242*4882a593Smuzhiyun 	}
243*4882a593Smuzhiyun 	if (i == 10000)
244*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: turn off DMA timed out\n");
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	/* wait for vpixen_off */
247*4882a593Smuzhiyun 	val = gbe->vt_vpixen;
248*4882a593Smuzhiyun 	vpixen_off = GET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val);
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	for (i = 0; i < 100000; i++) {
251*4882a593Smuzhiyun 		val = gbe->vt_xy;
252*4882a593Smuzhiyun 		x = GET_GBE_FIELD(VT_XY, X, val);
253*4882a593Smuzhiyun 		y = GET_GBE_FIELD(VT_XY, Y, val);
254*4882a593Smuzhiyun 		if (y < vpixen_off)
255*4882a593Smuzhiyun 			break;
256*4882a593Smuzhiyun 		udelay(1);
257*4882a593Smuzhiyun 	}
258*4882a593Smuzhiyun 	if (i == 100000)
259*4882a593Smuzhiyun 		printk(KERN_ERR
260*4882a593Smuzhiyun 		       "gbefb: wait for vpixen_off timed out\n");
261*4882a593Smuzhiyun 	for (i = 0; i < 10000; i++) {
262*4882a593Smuzhiyun 		val = gbe->vt_xy;
263*4882a593Smuzhiyun 		x = GET_GBE_FIELD(VT_XY, X, val);
264*4882a593Smuzhiyun 		y = GET_GBE_FIELD(VT_XY, Y, val);
265*4882a593Smuzhiyun 		if (y > vpixen_off)
266*4882a593Smuzhiyun 			break;
267*4882a593Smuzhiyun 		udelay(1);
268*4882a593Smuzhiyun 	}
269*4882a593Smuzhiyun 	if (i == 10000)
270*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: wait for vpixen_off timed out\n");
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	/* turn off pixel counter */
273*4882a593Smuzhiyun 	val = 0;
274*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_XY, FREEZE, val, 1);
275*4882a593Smuzhiyun 	gbe->vt_xy = val;
276*4882a593Smuzhiyun 	mdelay(10);
277*4882a593Smuzhiyun 	for (i = 0; i < 10000; i++) {
278*4882a593Smuzhiyun 		val = gbe->vt_xy;
279*4882a593Smuzhiyun 		if (GET_GBE_FIELD(VT_XY, FREEZE, val) != 1)
280*4882a593Smuzhiyun 			udelay(10);
281*4882a593Smuzhiyun 		else
282*4882a593Smuzhiyun 			break;
283*4882a593Smuzhiyun 	}
284*4882a593Smuzhiyun 	if (i == 10000)
285*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: turn off pixel clock timed out\n");
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	/* turn off dot clock */
288*4882a593Smuzhiyun 	val = gbe->dotclock;
289*4882a593Smuzhiyun 	SET_GBE_FIELD(DOTCLK, RUN, val, 0);
290*4882a593Smuzhiyun 	gbe->dotclock = val;
291*4882a593Smuzhiyun 	mdelay(10);
292*4882a593Smuzhiyun 	for (i = 0; i < 10000; i++) {
293*4882a593Smuzhiyun 		val = gbe->dotclock;
294*4882a593Smuzhiyun 		if (GET_GBE_FIELD(DOTCLK, RUN, val))
295*4882a593Smuzhiyun 			udelay(10);
296*4882a593Smuzhiyun 		else
297*4882a593Smuzhiyun 			break;
298*4882a593Smuzhiyun 	}
299*4882a593Smuzhiyun 	if (i == 10000)
300*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: turn off dotclock timed out\n");
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	/* reset the frame DMA FIFO */
303*4882a593Smuzhiyun 	val = gbe->frm_size_tile;
304*4882a593Smuzhiyun 	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 1);
305*4882a593Smuzhiyun 	gbe->frm_size_tile = val;
306*4882a593Smuzhiyun 	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 0);
307*4882a593Smuzhiyun 	gbe->frm_size_tile = val;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun 
gbe_turn_on(void)310*4882a593Smuzhiyun static void gbe_turn_on(void)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun 	unsigned int val, i;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	/*
315*4882a593Smuzhiyun 	 * Check if pixel counter is off, for unknown reason this
316*4882a593Smuzhiyun 	 * code hangs Visual Workstations
317*4882a593Smuzhiyun 	 */
318*4882a593Smuzhiyun 	if (gbe_revision < 2) {
319*4882a593Smuzhiyun 		val = gbe->vt_xy;
320*4882a593Smuzhiyun 		if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 0)
321*4882a593Smuzhiyun 			return;
322*4882a593Smuzhiyun 	}
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	/* turn on dot clock */
325*4882a593Smuzhiyun 	val = gbe->dotclock;
326*4882a593Smuzhiyun 	SET_GBE_FIELD(DOTCLK, RUN, val, 1);
327*4882a593Smuzhiyun 	gbe->dotclock = val;
328*4882a593Smuzhiyun 	mdelay(10);
329*4882a593Smuzhiyun 	for (i = 0; i < 10000; i++) {
330*4882a593Smuzhiyun 		val = gbe->dotclock;
331*4882a593Smuzhiyun 		if (GET_GBE_FIELD(DOTCLK, RUN, val) != 1)
332*4882a593Smuzhiyun 			udelay(10);
333*4882a593Smuzhiyun 		else
334*4882a593Smuzhiyun 			break;
335*4882a593Smuzhiyun 	}
336*4882a593Smuzhiyun 	if (i == 10000)
337*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: turn on dotclock timed out\n");
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	/* turn on pixel counter */
340*4882a593Smuzhiyun 	val = 0;
341*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_XY, FREEZE, val, 0);
342*4882a593Smuzhiyun 	gbe->vt_xy = val;
343*4882a593Smuzhiyun 	mdelay(10);
344*4882a593Smuzhiyun 	for (i = 0; i < 10000; i++) {
345*4882a593Smuzhiyun 		val = gbe->vt_xy;
346*4882a593Smuzhiyun 		if (GET_GBE_FIELD(VT_XY, FREEZE, val))
347*4882a593Smuzhiyun 			udelay(10);
348*4882a593Smuzhiyun 		else
349*4882a593Smuzhiyun 			break;
350*4882a593Smuzhiyun 	}
351*4882a593Smuzhiyun 	if (i == 10000)
352*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: turn on pixel clock timed out\n");
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	/* turn on DMA */
355*4882a593Smuzhiyun 	val = gbe->frm_control;
356*4882a593Smuzhiyun 	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 1);
357*4882a593Smuzhiyun 	gbe->frm_control = val;
358*4882a593Smuzhiyun 	udelay(1000);
359*4882a593Smuzhiyun 	for (i = 0; i < 10000; i++) {
360*4882a593Smuzhiyun 		val = gbe->frm_inhwctrl;
361*4882a593Smuzhiyun 		if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val) != 1)
362*4882a593Smuzhiyun 			udelay(10);
363*4882a593Smuzhiyun 		else
364*4882a593Smuzhiyun 			break;
365*4882a593Smuzhiyun 	}
366*4882a593Smuzhiyun 	if (i == 10000)
367*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: turn on DMA timed out\n");
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	gbe_turned_on = 1;
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun 
gbe_loadcmap(void)372*4882a593Smuzhiyun static void gbe_loadcmap(void)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun 	int i, j;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	for (i = 0; i < 256; i++) {
377*4882a593Smuzhiyun 		for (j = 0; j < 1000 && gbe->cm_fifo >= 63; j++)
378*4882a593Smuzhiyun 			udelay(10);
379*4882a593Smuzhiyun 		if (j == 1000)
380*4882a593Smuzhiyun 			printk(KERN_ERR "gbefb: cmap FIFO timeout\n");
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 		gbe->cmap[i] = gbe_cmap[i];
383*4882a593Smuzhiyun 	}
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun /*
387*4882a593Smuzhiyun  *  Blank the display.
388*4882a593Smuzhiyun  */
gbefb_blank(int blank,struct fb_info * info)389*4882a593Smuzhiyun static int gbefb_blank(int blank, struct fb_info *info)
390*4882a593Smuzhiyun {
391*4882a593Smuzhiyun 	/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
392*4882a593Smuzhiyun 	switch (blank) {
393*4882a593Smuzhiyun 	case FB_BLANK_UNBLANK:		/* unblank */
394*4882a593Smuzhiyun 		gbe_turn_on();
395*4882a593Smuzhiyun 		gbe_loadcmap();
396*4882a593Smuzhiyun 		break;
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	case FB_BLANK_NORMAL:		/* blank */
399*4882a593Smuzhiyun 		gbe_turn_off();
400*4882a593Smuzhiyun 		break;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	default:
403*4882a593Smuzhiyun 		/* Nothing */
404*4882a593Smuzhiyun 		break;
405*4882a593Smuzhiyun 	}
406*4882a593Smuzhiyun 	return 0;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun /*
410*4882a593Smuzhiyun  *  Setup flatpanel related registers.
411*4882a593Smuzhiyun  */
gbefb_setup_flatpanel(struct gbe_timing_info * timing)412*4882a593Smuzhiyun static void gbefb_setup_flatpanel(struct gbe_timing_info *timing)
413*4882a593Smuzhiyun {
414*4882a593Smuzhiyun 	int fp_wid, fp_hgt, fp_vbs, fp_vbe;
415*4882a593Smuzhiyun 	u32 outputVal = 0;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_FLAGS, HDRV_INVERT, outputVal,
418*4882a593Smuzhiyun 		(timing->flags & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1);
419*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_FLAGS, VDRV_INVERT, outputVal,
420*4882a593Smuzhiyun 		(timing->flags & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1);
421*4882a593Smuzhiyun 	gbe->vt_flags = outputVal;
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	/* Turn on the flat panel */
424*4882a593Smuzhiyun 	fp_wid = 1600;
425*4882a593Smuzhiyun 	fp_hgt = 1024;
426*4882a593Smuzhiyun 	fp_vbs = 0;
427*4882a593Smuzhiyun 	fp_vbe = 1600;
428*4882a593Smuzhiyun 	timing->pll_m = 4;
429*4882a593Smuzhiyun 	timing->pll_n = 1;
430*4882a593Smuzhiyun 	timing->pll_p = 0;
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	outputVal = 0;
433*4882a593Smuzhiyun 	SET_GBE_FIELD(FP_DE, ON, outputVal, fp_vbs);
434*4882a593Smuzhiyun 	SET_GBE_FIELD(FP_DE, OFF, outputVal, fp_vbe);
435*4882a593Smuzhiyun 	gbe->fp_de = outputVal;
436*4882a593Smuzhiyun 	outputVal = 0;
437*4882a593Smuzhiyun 	SET_GBE_FIELD(FP_HDRV, OFF, outputVal, fp_wid);
438*4882a593Smuzhiyun 	gbe->fp_hdrv = outputVal;
439*4882a593Smuzhiyun 	outputVal = 0;
440*4882a593Smuzhiyun 	SET_GBE_FIELD(FP_VDRV, ON, outputVal, 1);
441*4882a593Smuzhiyun 	SET_GBE_FIELD(FP_VDRV, OFF, outputVal, fp_hgt + 1);
442*4882a593Smuzhiyun 	gbe->fp_vdrv = outputVal;
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun struct gbe_pll_info {
446*4882a593Smuzhiyun 	int clock_rate;
447*4882a593Smuzhiyun 	int fvco_min;
448*4882a593Smuzhiyun 	int fvco_max;
449*4882a593Smuzhiyun };
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun static struct gbe_pll_info gbe_pll_table[2] = {
452*4882a593Smuzhiyun 	{ 20, 80, 220 },
453*4882a593Smuzhiyun 	{ 27, 80, 220 },
454*4882a593Smuzhiyun };
455*4882a593Smuzhiyun 
compute_gbe_timing(struct fb_var_screeninfo * var,struct gbe_timing_info * timing)456*4882a593Smuzhiyun static int compute_gbe_timing(struct fb_var_screeninfo *var,
457*4882a593Smuzhiyun 			      struct gbe_timing_info *timing)
458*4882a593Smuzhiyun {
459*4882a593Smuzhiyun 	int pll_m, pll_n, pll_p, error, best_m, best_n, best_p, best_error;
460*4882a593Smuzhiyun 	int pixclock;
461*4882a593Smuzhiyun 	struct gbe_pll_info *gbe_pll;
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	if (gbe_revision < 2)
464*4882a593Smuzhiyun 		gbe_pll = &gbe_pll_table[0];
465*4882a593Smuzhiyun 	else
466*4882a593Smuzhiyun 		gbe_pll = &gbe_pll_table[1];
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	/* Determine valid resolution and timing
469*4882a593Smuzhiyun 	 * GBE crystal runs at 20Mhz or 27Mhz
470*4882a593Smuzhiyun 	 * pll_m, pll_n, pll_p define the following frequencies
471*4882a593Smuzhiyun 	 * fvco = pll_m * 20Mhz / pll_n
472*4882a593Smuzhiyun 	 * fout = fvco / (2**pll_p) */
473*4882a593Smuzhiyun 	best_error = 1000000000;
474*4882a593Smuzhiyun 	best_n = best_m = best_p = 0;
475*4882a593Smuzhiyun 	for (pll_p = 0; pll_p < 4; pll_p++)
476*4882a593Smuzhiyun 		for (pll_m = 1; pll_m < 256; pll_m++)
477*4882a593Smuzhiyun 			for (pll_n = 1; pll_n < 64; pll_n++) {
478*4882a593Smuzhiyun 				pixclock = (1000000 / gbe_pll->clock_rate) *
479*4882a593Smuzhiyun 						(pll_n << pll_p) / pll_m;
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 				error = var->pixclock - pixclock;
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 				if (error < 0)
484*4882a593Smuzhiyun 					error = -error;
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 				if (error < best_error &&
487*4882a593Smuzhiyun 				    pll_m / pll_n >
488*4882a593Smuzhiyun 				    gbe_pll->fvco_min / gbe_pll->clock_rate &&
489*4882a593Smuzhiyun  				    pll_m / pll_n <
490*4882a593Smuzhiyun 				    gbe_pll->fvco_max / gbe_pll->clock_rate) {
491*4882a593Smuzhiyun 					best_error = error;
492*4882a593Smuzhiyun 					best_m = pll_m;
493*4882a593Smuzhiyun 					best_n = pll_n;
494*4882a593Smuzhiyun 					best_p = pll_p;
495*4882a593Smuzhiyun 				}
496*4882a593Smuzhiyun 			}
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 	if (!best_n || !best_m)
499*4882a593Smuzhiyun 		return -EINVAL;	/* Resolution to high */
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	pixclock = (1000000 / gbe_pll->clock_rate) *
502*4882a593Smuzhiyun 		(best_n << best_p) / best_m;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	/* set video timing information */
505*4882a593Smuzhiyun 	if (timing) {
506*4882a593Smuzhiyun 		timing->width = var->xres;
507*4882a593Smuzhiyun 		timing->height = var->yres;
508*4882a593Smuzhiyun 		timing->pll_m = best_m;
509*4882a593Smuzhiyun 		timing->pll_n = best_n;
510*4882a593Smuzhiyun 		timing->pll_p = best_p;
511*4882a593Smuzhiyun 		timing->cfreq = gbe_pll->clock_rate * 1000 * timing->pll_m /
512*4882a593Smuzhiyun 			(timing->pll_n << timing->pll_p);
513*4882a593Smuzhiyun 		timing->htotal = var->left_margin + var->xres +
514*4882a593Smuzhiyun 				var->right_margin + var->hsync_len;
515*4882a593Smuzhiyun 		timing->vtotal = var->upper_margin + var->yres +
516*4882a593Smuzhiyun 				var->lower_margin + var->vsync_len;
517*4882a593Smuzhiyun 		timing->fields_sec = 1000 * timing->cfreq / timing->htotal *
518*4882a593Smuzhiyun 				1000 / timing->vtotal;
519*4882a593Smuzhiyun 		timing->hblank_start = var->xres;
520*4882a593Smuzhiyun 		timing->vblank_start = var->yres;
521*4882a593Smuzhiyun 		timing->hblank_end = timing->htotal;
522*4882a593Smuzhiyun 		timing->hsync_start = var->xres + var->right_margin + 1;
523*4882a593Smuzhiyun 		timing->hsync_end = timing->hsync_start + var->hsync_len;
524*4882a593Smuzhiyun 		timing->vblank_end = timing->vtotal;
525*4882a593Smuzhiyun 		timing->vsync_start = var->yres + var->lower_margin + 1;
526*4882a593Smuzhiyun 		timing->vsync_end = timing->vsync_start + var->vsync_len;
527*4882a593Smuzhiyun 	}
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	return pixclock;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun 
gbe_set_timing_info(struct gbe_timing_info * timing)532*4882a593Smuzhiyun static void gbe_set_timing_info(struct gbe_timing_info *timing)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun 	int temp;
535*4882a593Smuzhiyun 	unsigned int val;
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun 	/* setup dot clock PLL */
538*4882a593Smuzhiyun 	val = 0;
539*4882a593Smuzhiyun 	SET_GBE_FIELD(DOTCLK, M, val, timing->pll_m - 1);
540*4882a593Smuzhiyun 	SET_GBE_FIELD(DOTCLK, N, val, timing->pll_n - 1);
541*4882a593Smuzhiyun 	SET_GBE_FIELD(DOTCLK, P, val, timing->pll_p);
542*4882a593Smuzhiyun 	SET_GBE_FIELD(DOTCLK, RUN, val, 0);	/* do not start yet */
543*4882a593Smuzhiyun 	gbe->dotclock = val;
544*4882a593Smuzhiyun 	mdelay(10);
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	/* setup pixel counter */
547*4882a593Smuzhiyun 	val = 0;
548*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_XYMAX, MAXX, val, timing->htotal);
549*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_XYMAX, MAXY, val, timing->vtotal);
550*4882a593Smuzhiyun 	gbe->vt_xymax = val;
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	/* setup video timing signals */
553*4882a593Smuzhiyun 	val = 0;
554*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_VSYNC, VSYNC_ON, val, timing->vsync_start);
555*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_VSYNC, VSYNC_OFF, val, timing->vsync_end);
556*4882a593Smuzhiyun 	gbe->vt_vsync = val;
557*4882a593Smuzhiyun 	val = 0;
558*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_HSYNC, HSYNC_ON, val, timing->hsync_start);
559*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_HSYNC, HSYNC_OFF, val, timing->hsync_end);
560*4882a593Smuzhiyun 	gbe->vt_hsync = val;
561*4882a593Smuzhiyun 	val = 0;
562*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_VBLANK, VBLANK_ON, val, timing->vblank_start);
563*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_VBLANK, VBLANK_OFF, val, timing->vblank_end);
564*4882a593Smuzhiyun 	gbe->vt_vblank = val;
565*4882a593Smuzhiyun 	val = 0;
566*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_HBLANK, HBLANK_ON, val,
567*4882a593Smuzhiyun 		      timing->hblank_start - 5);
568*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_HBLANK, HBLANK_OFF, val,
569*4882a593Smuzhiyun 		      timing->hblank_end - 3);
570*4882a593Smuzhiyun 	gbe->vt_hblank = val;
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	/* setup internal timing signals */
573*4882a593Smuzhiyun 	val = 0;
574*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_VCMAP, VCMAP_ON, val, timing->vblank_start);
575*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_VCMAP, VCMAP_OFF, val, timing->vblank_end);
576*4882a593Smuzhiyun 	gbe->vt_vcmap = val;
577*4882a593Smuzhiyun 	val = 0;
578*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_HCMAP, HCMAP_ON, val, timing->hblank_start);
579*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_HCMAP, HCMAP_OFF, val, timing->hblank_end);
580*4882a593Smuzhiyun 	gbe->vt_hcmap = val;
581*4882a593Smuzhiyun 
582*4882a593Smuzhiyun 	val = 0;
583*4882a593Smuzhiyun 	temp = timing->vblank_start - timing->vblank_end - 1;
584*4882a593Smuzhiyun 	if (temp > 0)
585*4882a593Smuzhiyun 		temp = -temp;
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	if (flat_panel_enabled)
588*4882a593Smuzhiyun 		gbefb_setup_flatpanel(timing);
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 	SET_GBE_FIELD(DID_START_XY, DID_STARTY, val, (u32) temp);
591*4882a593Smuzhiyun 	if (timing->hblank_end >= 20)
592*4882a593Smuzhiyun 		SET_GBE_FIELD(DID_START_XY, DID_STARTX, val,
593*4882a593Smuzhiyun 			      timing->hblank_end - 20);
594*4882a593Smuzhiyun 	else
595*4882a593Smuzhiyun 		SET_GBE_FIELD(DID_START_XY, DID_STARTX, val,
596*4882a593Smuzhiyun 			      timing->htotal - (20 - timing->hblank_end));
597*4882a593Smuzhiyun 	gbe->did_start_xy = val;
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun 	val = 0;
600*4882a593Smuzhiyun 	SET_GBE_FIELD(CRS_START_XY, CRS_STARTY, val, (u32) (temp + 1));
601*4882a593Smuzhiyun 	if (timing->hblank_end >= GBE_CRS_MAGIC)
602*4882a593Smuzhiyun 		SET_GBE_FIELD(CRS_START_XY, CRS_STARTX, val,
603*4882a593Smuzhiyun 			      timing->hblank_end - GBE_CRS_MAGIC);
604*4882a593Smuzhiyun 	else
605*4882a593Smuzhiyun 		SET_GBE_FIELD(CRS_START_XY, CRS_STARTX, val,
606*4882a593Smuzhiyun 			      timing->htotal - (GBE_CRS_MAGIC -
607*4882a593Smuzhiyun 						timing->hblank_end));
608*4882a593Smuzhiyun 	gbe->crs_start_xy = val;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	val = 0;
611*4882a593Smuzhiyun 	SET_GBE_FIELD(VC_START_XY, VC_STARTY, val, (u32) temp);
612*4882a593Smuzhiyun 	SET_GBE_FIELD(VC_START_XY, VC_STARTX, val, timing->hblank_end - 4);
613*4882a593Smuzhiyun 	gbe->vc_start_xy = val;
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	val = 0;
616*4882a593Smuzhiyun 	temp = timing->hblank_end - GBE_PIXEN_MAGIC_ON;
617*4882a593Smuzhiyun 	if (temp < 0)
618*4882a593Smuzhiyun 		temp += timing->htotal;	/* allow blank to wrap around */
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_HPIXEN, HPIXEN_ON, val, temp);
621*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_HPIXEN, HPIXEN_OFF, val,
622*4882a593Smuzhiyun 		      ((temp + timing->width -
623*4882a593Smuzhiyun 			GBE_PIXEN_MAGIC_OFF) % timing->htotal));
624*4882a593Smuzhiyun 	gbe->vt_hpixen = val;
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	val = 0;
627*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_VPIXEN, VPIXEN_ON, val, timing->vblank_end);
628*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val, timing->vblank_start);
629*4882a593Smuzhiyun 	gbe->vt_vpixen = val;
630*4882a593Smuzhiyun 
631*4882a593Smuzhiyun 	/* turn off sync on green */
632*4882a593Smuzhiyun 	val = 0;
633*4882a593Smuzhiyun 	SET_GBE_FIELD(VT_FLAGS, SYNC_LOW, val, 1);
634*4882a593Smuzhiyun 	gbe->vt_flags = val;
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun /*
638*4882a593Smuzhiyun  *  Set the hardware according to 'par'.
639*4882a593Smuzhiyun  */
640*4882a593Smuzhiyun 
gbefb_set_par(struct fb_info * info)641*4882a593Smuzhiyun static int gbefb_set_par(struct fb_info *info)
642*4882a593Smuzhiyun {
643*4882a593Smuzhiyun 	int i;
644*4882a593Smuzhiyun 	unsigned int val;
645*4882a593Smuzhiyun 	int wholeTilesX, partTilesX, maxPixelsPerTileX;
646*4882a593Smuzhiyun 	int height_pix;
647*4882a593Smuzhiyun 	int xpmax, ypmax;	/* Monitor resolution */
648*4882a593Smuzhiyun 	int bytesPerPixel;	/* Bytes per pixel */
649*4882a593Smuzhiyun 	struct gbefb_par *par = (struct gbefb_par *) info->par;
650*4882a593Smuzhiyun 
651*4882a593Smuzhiyun 	compute_gbe_timing(&info->var, &par->timing);
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	bytesPerPixel = info->var.bits_per_pixel / 8;
654*4882a593Smuzhiyun 	info->fix.line_length = info->var.xres_virtual * bytesPerPixel;
655*4882a593Smuzhiyun 	xpmax = par->timing.width;
656*4882a593Smuzhiyun 	ypmax = par->timing.height;
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 	/* turn off GBE */
659*4882a593Smuzhiyun 	gbe_turn_off();
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 	/* set timing info */
662*4882a593Smuzhiyun 	gbe_set_timing_info(&par->timing);
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 	/* initialize DIDs */
665*4882a593Smuzhiyun 	val = 0;
666*4882a593Smuzhiyun 	switch (bytesPerPixel) {
667*4882a593Smuzhiyun 	case 1:
668*4882a593Smuzhiyun 		SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_I8);
669*4882a593Smuzhiyun 		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
670*4882a593Smuzhiyun 		break;
671*4882a593Smuzhiyun 	case 2:
672*4882a593Smuzhiyun 		SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_ARGB5);
673*4882a593Smuzhiyun 		info->fix.visual = FB_VISUAL_TRUECOLOR;
674*4882a593Smuzhiyun 		break;
675*4882a593Smuzhiyun 	case 4:
676*4882a593Smuzhiyun 		SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_RGB8);
677*4882a593Smuzhiyun 		info->fix.visual = FB_VISUAL_TRUECOLOR;
678*4882a593Smuzhiyun 		break;
679*4882a593Smuzhiyun 	}
680*4882a593Smuzhiyun 	SET_GBE_FIELD(WID, BUF, val, GBE_BMODE_BOTH);
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	for (i = 0; i < 32; i++)
683*4882a593Smuzhiyun 		gbe->mode_regs[i] = val;
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun 	/* Initialize interrupts */
686*4882a593Smuzhiyun 	gbe->vt_intr01 = 0xffffffff;
687*4882a593Smuzhiyun 	gbe->vt_intr23 = 0xffffffff;
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun 	/* HACK:
690*4882a593Smuzhiyun 	   The GBE hardware uses a tiled memory to screen mapping. Tiles are
691*4882a593Smuzhiyun 	   blocks of 512x128, 256x128 or 128x128 pixels, respectively for 8bit,
692*4882a593Smuzhiyun 	   16bit and 32 bit modes (64 kB). They cover the screen with partial
693*4882a593Smuzhiyun 	   tiles on the right and/or bottom of the screen if needed.
694*4882a593Smuzhiyun 	   For example in 640x480 8 bit mode the mapping is:
695*4882a593Smuzhiyun 
696*4882a593Smuzhiyun 	   <-------- 640 ----->
697*4882a593Smuzhiyun 	   <---- 512 ----><128|384 offscreen>
698*4882a593Smuzhiyun 	   ^  ^
699*4882a593Smuzhiyun 	   | 128    [tile 0]        [tile 1]
700*4882a593Smuzhiyun 	   |  v
701*4882a593Smuzhiyun 	   ^
702*4882a593Smuzhiyun 	   4 128    [tile 2]        [tile 3]
703*4882a593Smuzhiyun 	   8  v
704*4882a593Smuzhiyun 	   0  ^
705*4882a593Smuzhiyun 	   128    [tile 4]        [tile 5]
706*4882a593Smuzhiyun 	   |  v
707*4882a593Smuzhiyun 	   |  ^
708*4882a593Smuzhiyun 	   v  96    [tile 6]        [tile 7]
709*4882a593Smuzhiyun 	   32 offscreen
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun 	   Tiles have the advantage that they can be allocated individually in
712*4882a593Smuzhiyun 	   memory. However, this mapping is not linear at all, which is not
713*4882a593Smuzhiyun 	   really convenient. In order to support linear addressing, the GBE
714*4882a593Smuzhiyun 	   DMA hardware is fooled into thinking the screen is only one tile
715*4882a593Smuzhiyun 	   large and but has a greater height, so that the DMA transfer covers
716*4882a593Smuzhiyun 	   the same region.
717*4882a593Smuzhiyun 	   Tiles are still allocated as independent chunks of 64KB of
718*4882a593Smuzhiyun 	   continuous physical memory and remapped so that the kernel sees the
719*4882a593Smuzhiyun 	   framebuffer as a continuous virtual memory. The GBE tile table is
720*4882a593Smuzhiyun 	   set up so that each tile references one of these 64k blocks:
721*4882a593Smuzhiyun 
722*4882a593Smuzhiyun 	   GBE -> tile list    framebuffer           TLB   <------------ CPU
723*4882a593Smuzhiyun 	          [ tile 0 ] -> [ 64KB ]  <- [ 16x 4KB page entries ]     ^
724*4882a593Smuzhiyun 	             ...           ...              ...       linear virtual FB
725*4882a593Smuzhiyun 	          [ tile n ] -> [ 64KB ]  <- [ 16x 4KB page entries ]     v
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 	   The GBE hardware is then told that the buffer is 512*tweaked_height,
729*4882a593Smuzhiyun 	   with tweaked_height = real_width*real_height/pixels_per_tile.
730*4882a593Smuzhiyun 	   Thus the GBE hardware will scan the first tile, filing the first 64k
731*4882a593Smuzhiyun 	   covered region of the screen, and then will proceed to the next
732*4882a593Smuzhiyun 	   tile, until the whole screen is covered.
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 	   Here is what would happen at 640x480 8bit:
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 	   normal tiling               linear
737*4882a593Smuzhiyun 	   ^   11111111111111112222    11111111111111111111  ^
738*4882a593Smuzhiyun 	   128 11111111111111112222    11111111111111111111 102 lines
739*4882a593Smuzhiyun 	       11111111111111112222    11111111111111111111  v
740*4882a593Smuzhiyun 	   V   11111111111111112222    11111111222222222222
741*4882a593Smuzhiyun 	       33333333333333334444    22222222222222222222
742*4882a593Smuzhiyun 	       33333333333333334444    22222222222222222222
743*4882a593Smuzhiyun 	       <      512     >        <  256 >               102*640+256 = 64k
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun 	   NOTE: The only mode for which this is not working is 800x600 8bit,
746*4882a593Smuzhiyun 	   as 800*600/512 = 937.5 which is not integer and thus causes
747*4882a593Smuzhiyun 	   flickering.
748*4882a593Smuzhiyun 	   I guess this is not so important as one can use 640x480 8bit or
749*4882a593Smuzhiyun 	   800x600 16bit anyway.
750*4882a593Smuzhiyun 	 */
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun 	/* Tell gbe about the tiles table location */
753*4882a593Smuzhiyun 	/* tile_ptr -> [ tile 1 ] -> FB mem */
754*4882a593Smuzhiyun 	/*             [ tile 2 ] -> FB mem */
755*4882a593Smuzhiyun 	/*               ...                */
756*4882a593Smuzhiyun 	val = 0;
757*4882a593Smuzhiyun 	SET_GBE_FIELD(FRM_CONTROL, FRM_TILE_PTR, val, gbe_tiles.dma >> 9);
758*4882a593Smuzhiyun 	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0); /* do not start */
759*4882a593Smuzhiyun 	SET_GBE_FIELD(FRM_CONTROL, FRM_LINEAR, val, 0);
760*4882a593Smuzhiyun 	gbe->frm_control = val;
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 	maxPixelsPerTileX = 512 / bytesPerPixel;
763*4882a593Smuzhiyun 	wholeTilesX = 1;
764*4882a593Smuzhiyun 	partTilesX = 0;
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 	/* Initialize the framebuffer */
767*4882a593Smuzhiyun 	val = 0;
768*4882a593Smuzhiyun 	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_WIDTH_TILE, val, wholeTilesX);
769*4882a593Smuzhiyun 	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_RHS, val, partTilesX);
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	switch (bytesPerPixel) {
772*4882a593Smuzhiyun 	case 1:
773*4882a593Smuzhiyun 		SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val,
774*4882a593Smuzhiyun 			      GBE_FRM_DEPTH_8);
775*4882a593Smuzhiyun 		break;
776*4882a593Smuzhiyun 	case 2:
777*4882a593Smuzhiyun 		SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val,
778*4882a593Smuzhiyun 			      GBE_FRM_DEPTH_16);
779*4882a593Smuzhiyun 		break;
780*4882a593Smuzhiyun 	case 4:
781*4882a593Smuzhiyun 		SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val,
782*4882a593Smuzhiyun 			      GBE_FRM_DEPTH_32);
783*4882a593Smuzhiyun 		break;
784*4882a593Smuzhiyun 	}
785*4882a593Smuzhiyun 	gbe->frm_size_tile = val;
786*4882a593Smuzhiyun 
787*4882a593Smuzhiyun 	/* compute tweaked height */
788*4882a593Smuzhiyun 	height_pix = xpmax * ypmax / maxPixelsPerTileX;
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 	val = 0;
791*4882a593Smuzhiyun 	SET_GBE_FIELD(FRM_SIZE_PIXEL, FB_HEIGHT_PIX, val, height_pix);
792*4882a593Smuzhiyun 	gbe->frm_size_pixel = val;
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	/* turn off DID and overlay DMA */
795*4882a593Smuzhiyun 	gbe->did_control = 0;
796*4882a593Smuzhiyun 	gbe->ovr_width_tile = 0;
797*4882a593Smuzhiyun 
798*4882a593Smuzhiyun 	/* Turn off mouse cursor */
799*4882a593Smuzhiyun 	gbe->crs_ctl = 0;
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 	/* Turn on GBE */
802*4882a593Smuzhiyun 	gbe_turn_on();
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 	/* Initialize the gamma map */
805*4882a593Smuzhiyun 	udelay(10);
806*4882a593Smuzhiyun 	for (i = 0; i < 256; i++)
807*4882a593Smuzhiyun 		gbe->gmap[i] = (i << 24) | (i << 16) | (i << 8);
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun 	/* Initialize the color map */
810*4882a593Smuzhiyun 	for (i = 0; i < 256; i++)
811*4882a593Smuzhiyun 		gbe_cmap[i] = (i << 8) | (i << 16) | (i << 24);
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	gbe_loadcmap();
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	return 0;
816*4882a593Smuzhiyun }
817*4882a593Smuzhiyun 
gbefb_encode_fix(struct fb_fix_screeninfo * fix,struct fb_var_screeninfo * var)818*4882a593Smuzhiyun static void gbefb_encode_fix(struct fb_fix_screeninfo *fix,
819*4882a593Smuzhiyun 			     struct fb_var_screeninfo *var)
820*4882a593Smuzhiyun {
821*4882a593Smuzhiyun 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
822*4882a593Smuzhiyun 	strcpy(fix->id, "SGI GBE");
823*4882a593Smuzhiyun 	fix->smem_start = (unsigned long) gbe_mem;
824*4882a593Smuzhiyun 	fix->smem_len = gbe_mem_size;
825*4882a593Smuzhiyun 	fix->type = FB_TYPE_PACKED_PIXELS;
826*4882a593Smuzhiyun 	fix->type_aux = 0;
827*4882a593Smuzhiyun 	fix->accel = FB_ACCEL_NONE;
828*4882a593Smuzhiyun 	switch (var->bits_per_pixel) {
829*4882a593Smuzhiyun 	case 8:
830*4882a593Smuzhiyun 		fix->visual = FB_VISUAL_PSEUDOCOLOR;
831*4882a593Smuzhiyun 		break;
832*4882a593Smuzhiyun 	default:
833*4882a593Smuzhiyun 		fix->visual = FB_VISUAL_TRUECOLOR;
834*4882a593Smuzhiyun 		break;
835*4882a593Smuzhiyun 	}
836*4882a593Smuzhiyun 	fix->ywrapstep = 0;
837*4882a593Smuzhiyun 	fix->xpanstep = 0;
838*4882a593Smuzhiyun 	fix->ypanstep = 0;
839*4882a593Smuzhiyun 	fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
840*4882a593Smuzhiyun 	fix->mmio_start = GBE_BASE;
841*4882a593Smuzhiyun 	fix->mmio_len = sizeof(struct sgi_gbe);
842*4882a593Smuzhiyun }
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun /*
845*4882a593Smuzhiyun  *  Set a single color register. The values supplied are already
846*4882a593Smuzhiyun  *  rounded down to the hardware's capabilities (according to the
847*4882a593Smuzhiyun  *  entries in the var structure). Return != 0 for invalid regno.
848*4882a593Smuzhiyun  */
849*4882a593Smuzhiyun 
gbefb_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)850*4882a593Smuzhiyun static int gbefb_setcolreg(unsigned regno, unsigned red, unsigned green,
851*4882a593Smuzhiyun 			     unsigned blue, unsigned transp,
852*4882a593Smuzhiyun 			     struct fb_info *info)
853*4882a593Smuzhiyun {
854*4882a593Smuzhiyun 	int i;
855*4882a593Smuzhiyun 
856*4882a593Smuzhiyun 	if (regno > 255)
857*4882a593Smuzhiyun 		return 1;
858*4882a593Smuzhiyun 	red >>= 8;
859*4882a593Smuzhiyun 	green >>= 8;
860*4882a593Smuzhiyun 	blue >>= 8;
861*4882a593Smuzhiyun 
862*4882a593Smuzhiyun 	if (info->var.bits_per_pixel <= 8) {
863*4882a593Smuzhiyun 		gbe_cmap[regno] = (red << 24) | (green << 16) | (blue << 8);
864*4882a593Smuzhiyun 		if (gbe_turned_on) {
865*4882a593Smuzhiyun 			/* wait for the color map FIFO to have a free entry */
866*4882a593Smuzhiyun 			for (i = 0; i < 1000 && gbe->cm_fifo >= 63; i++)
867*4882a593Smuzhiyun 				udelay(10);
868*4882a593Smuzhiyun 			if (i == 1000) {
869*4882a593Smuzhiyun 				printk(KERN_ERR "gbefb: cmap FIFO timeout\n");
870*4882a593Smuzhiyun 				return 1;
871*4882a593Smuzhiyun 			}
872*4882a593Smuzhiyun 			gbe->cmap[regno] = gbe_cmap[regno];
873*4882a593Smuzhiyun 		}
874*4882a593Smuzhiyun 	} else if (regno < 16) {
875*4882a593Smuzhiyun 		switch (info->var.bits_per_pixel) {
876*4882a593Smuzhiyun 		case 15:
877*4882a593Smuzhiyun 		case 16:
878*4882a593Smuzhiyun 			red >>= 3;
879*4882a593Smuzhiyun 			green >>= 3;
880*4882a593Smuzhiyun 			blue >>= 3;
881*4882a593Smuzhiyun 			pseudo_palette[regno] =
882*4882a593Smuzhiyun 				(red << info->var.red.offset) |
883*4882a593Smuzhiyun 				(green << info->var.green.offset) |
884*4882a593Smuzhiyun 				(blue << info->var.blue.offset);
885*4882a593Smuzhiyun 			break;
886*4882a593Smuzhiyun 		case 32:
887*4882a593Smuzhiyun 			pseudo_palette[regno] =
888*4882a593Smuzhiyun 				(red << info->var.red.offset) |
889*4882a593Smuzhiyun 				(green << info->var.green.offset) |
890*4882a593Smuzhiyun 				(blue << info->var.blue.offset);
891*4882a593Smuzhiyun 			break;
892*4882a593Smuzhiyun 		}
893*4882a593Smuzhiyun 	}
894*4882a593Smuzhiyun 
895*4882a593Smuzhiyun 	return 0;
896*4882a593Smuzhiyun }
897*4882a593Smuzhiyun 
898*4882a593Smuzhiyun /*
899*4882a593Smuzhiyun  *  Check video mode validity, eventually modify var to best match.
900*4882a593Smuzhiyun  */
gbefb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)901*4882a593Smuzhiyun static int gbefb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
902*4882a593Smuzhiyun {
903*4882a593Smuzhiyun 	unsigned int line_length;
904*4882a593Smuzhiyun 	struct gbe_timing_info timing;
905*4882a593Smuzhiyun 	int ret;
906*4882a593Smuzhiyun 
907*4882a593Smuzhiyun 	/* Limit bpp to 8, 16, and 32 */
908*4882a593Smuzhiyun 	if (var->bits_per_pixel <= 8)
909*4882a593Smuzhiyun 		var->bits_per_pixel = 8;
910*4882a593Smuzhiyun 	else if (var->bits_per_pixel <= 16)
911*4882a593Smuzhiyun 		var->bits_per_pixel = 16;
912*4882a593Smuzhiyun 	else if (var->bits_per_pixel <= 32)
913*4882a593Smuzhiyun 		var->bits_per_pixel = 32;
914*4882a593Smuzhiyun 	else
915*4882a593Smuzhiyun 		return -EINVAL;
916*4882a593Smuzhiyun 
917*4882a593Smuzhiyun 	/* Check the mode can be mapped linearly with the tile table trick. */
918*4882a593Smuzhiyun 	/* This requires width x height x bytes/pixel be a multiple of 512 */
919*4882a593Smuzhiyun 	if ((var->xres * var->yres * var->bits_per_pixel) & 4095)
920*4882a593Smuzhiyun 		return -EINVAL;
921*4882a593Smuzhiyun 
922*4882a593Smuzhiyun 	var->grayscale = 0;	/* No grayscale for now */
923*4882a593Smuzhiyun 
924*4882a593Smuzhiyun 	ret = compute_gbe_timing(var, &timing);
925*4882a593Smuzhiyun 	var->pixclock = ret;
926*4882a593Smuzhiyun 	if (ret < 0)
927*4882a593Smuzhiyun 		return -EINVAL;
928*4882a593Smuzhiyun 
929*4882a593Smuzhiyun 	/* Adjust virtual resolution, if necessary */
930*4882a593Smuzhiyun 	if (var->xres > var->xres_virtual || (!ywrap && !ypan))
931*4882a593Smuzhiyun 		var->xres_virtual = var->xres;
932*4882a593Smuzhiyun 	if (var->yres > var->yres_virtual || (!ywrap && !ypan))
933*4882a593Smuzhiyun 		var->yres_virtual = var->yres;
934*4882a593Smuzhiyun 
935*4882a593Smuzhiyun 	if (var->vmode & FB_VMODE_CONUPDATE) {
936*4882a593Smuzhiyun 		var->vmode |= FB_VMODE_YWRAP;
937*4882a593Smuzhiyun 		var->xoffset = info->var.xoffset;
938*4882a593Smuzhiyun 		var->yoffset = info->var.yoffset;
939*4882a593Smuzhiyun 	}
940*4882a593Smuzhiyun 
941*4882a593Smuzhiyun 	/* No grayscale for now */
942*4882a593Smuzhiyun 	var->grayscale = 0;
943*4882a593Smuzhiyun 
944*4882a593Smuzhiyun 	/* Memory limit */
945*4882a593Smuzhiyun 	line_length = var->xres_virtual * var->bits_per_pixel / 8;
946*4882a593Smuzhiyun 	if (line_length * var->yres_virtual > gbe_mem_size)
947*4882a593Smuzhiyun 		return -ENOMEM;	/* Virtual resolution too high */
948*4882a593Smuzhiyun 
949*4882a593Smuzhiyun 	switch (var->bits_per_pixel) {
950*4882a593Smuzhiyun 	case 8:
951*4882a593Smuzhiyun 		var->red.offset = 0;
952*4882a593Smuzhiyun 		var->red.length = 8;
953*4882a593Smuzhiyun 		var->green.offset = 0;
954*4882a593Smuzhiyun 		var->green.length = 8;
955*4882a593Smuzhiyun 		var->blue.offset = 0;
956*4882a593Smuzhiyun 		var->blue.length = 8;
957*4882a593Smuzhiyun 		var->transp.offset = 0;
958*4882a593Smuzhiyun 		var->transp.length = 0;
959*4882a593Smuzhiyun 		break;
960*4882a593Smuzhiyun 	case 16:		/* RGB 1555 */
961*4882a593Smuzhiyun 		var->red.offset = 10;
962*4882a593Smuzhiyun 		var->red.length = 5;
963*4882a593Smuzhiyun 		var->green.offset = 5;
964*4882a593Smuzhiyun 		var->green.length = 5;
965*4882a593Smuzhiyun 		var->blue.offset = 0;
966*4882a593Smuzhiyun 		var->blue.length = 5;
967*4882a593Smuzhiyun 		var->transp.offset = 0;
968*4882a593Smuzhiyun 		var->transp.length = 0;
969*4882a593Smuzhiyun 		break;
970*4882a593Smuzhiyun 	case 32:		/* RGB 8888 */
971*4882a593Smuzhiyun 		var->red.offset = 24;
972*4882a593Smuzhiyun 		var->red.length = 8;
973*4882a593Smuzhiyun 		var->green.offset = 16;
974*4882a593Smuzhiyun 		var->green.length = 8;
975*4882a593Smuzhiyun 		var->blue.offset = 8;
976*4882a593Smuzhiyun 		var->blue.length = 8;
977*4882a593Smuzhiyun 		var->transp.offset = 0;
978*4882a593Smuzhiyun 		var->transp.length = 8;
979*4882a593Smuzhiyun 		break;
980*4882a593Smuzhiyun 	}
981*4882a593Smuzhiyun 	var->red.msb_right = 0;
982*4882a593Smuzhiyun 	var->green.msb_right = 0;
983*4882a593Smuzhiyun 	var->blue.msb_right = 0;
984*4882a593Smuzhiyun 	var->transp.msb_right = 0;
985*4882a593Smuzhiyun 
986*4882a593Smuzhiyun 	var->left_margin = timing.htotal - timing.hsync_end;
987*4882a593Smuzhiyun 	var->right_margin = timing.hsync_start - timing.width;
988*4882a593Smuzhiyun 	var->upper_margin = timing.vtotal - timing.vsync_end;
989*4882a593Smuzhiyun 	var->lower_margin = timing.vsync_start - timing.height;
990*4882a593Smuzhiyun 	var->hsync_len = timing.hsync_end - timing.hsync_start;
991*4882a593Smuzhiyun 	var->vsync_len = timing.vsync_end - timing.vsync_start;
992*4882a593Smuzhiyun 
993*4882a593Smuzhiyun 	return 0;
994*4882a593Smuzhiyun }
995*4882a593Smuzhiyun 
gbefb_mmap(struct fb_info * info,struct vm_area_struct * vma)996*4882a593Smuzhiyun static int gbefb_mmap(struct fb_info *info,
997*4882a593Smuzhiyun 			struct vm_area_struct *vma)
998*4882a593Smuzhiyun {
999*4882a593Smuzhiyun 	unsigned long size = vma->vm_end - vma->vm_start;
1000*4882a593Smuzhiyun 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
1001*4882a593Smuzhiyun 	unsigned long addr;
1002*4882a593Smuzhiyun 	unsigned long phys_addr, phys_size;
1003*4882a593Smuzhiyun 	u16 *tile;
1004*4882a593Smuzhiyun 
1005*4882a593Smuzhiyun 	/* check range */
1006*4882a593Smuzhiyun 	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
1007*4882a593Smuzhiyun 		return -EINVAL;
1008*4882a593Smuzhiyun 	if (size > gbe_mem_size)
1009*4882a593Smuzhiyun 		return -EINVAL;
1010*4882a593Smuzhiyun 	if (offset > gbe_mem_size - size)
1011*4882a593Smuzhiyun 		return -EINVAL;
1012*4882a593Smuzhiyun 
1013*4882a593Smuzhiyun 	/* remap using the fastest write-through mode on architecture */
1014*4882a593Smuzhiyun 	/* try not polluting the cache when possible */
1015*4882a593Smuzhiyun #ifdef CONFIG_MIPS
1016*4882a593Smuzhiyun 	pgprot_val(vma->vm_page_prot) =
1017*4882a593Smuzhiyun 		pgprot_fb(pgprot_val(vma->vm_page_prot));
1018*4882a593Smuzhiyun #endif
1019*4882a593Smuzhiyun 	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
1020*4882a593Smuzhiyun 
1021*4882a593Smuzhiyun 	/* look for the starting tile */
1022*4882a593Smuzhiyun 	tile = &gbe_tiles.cpu[offset >> TILE_SHIFT];
1023*4882a593Smuzhiyun 	addr = vma->vm_start;
1024*4882a593Smuzhiyun 	offset &= TILE_MASK;
1025*4882a593Smuzhiyun 
1026*4882a593Smuzhiyun 	/* remap each tile separately */
1027*4882a593Smuzhiyun 	do {
1028*4882a593Smuzhiyun 		phys_addr = (((unsigned long) (*tile)) << TILE_SHIFT) + offset;
1029*4882a593Smuzhiyun 		if ((offset + size) < TILE_SIZE)
1030*4882a593Smuzhiyun 			phys_size = size;
1031*4882a593Smuzhiyun 		else
1032*4882a593Smuzhiyun 			phys_size = TILE_SIZE - offset;
1033*4882a593Smuzhiyun 
1034*4882a593Smuzhiyun 		if (remap_pfn_range(vma, addr, phys_addr >> PAGE_SHIFT,
1035*4882a593Smuzhiyun 						phys_size, vma->vm_page_prot))
1036*4882a593Smuzhiyun 			return -EAGAIN;
1037*4882a593Smuzhiyun 
1038*4882a593Smuzhiyun 		offset = 0;
1039*4882a593Smuzhiyun 		size -= phys_size;
1040*4882a593Smuzhiyun 		addr += phys_size;
1041*4882a593Smuzhiyun 		tile++;
1042*4882a593Smuzhiyun 	} while (size);
1043*4882a593Smuzhiyun 
1044*4882a593Smuzhiyun 	return 0;
1045*4882a593Smuzhiyun }
1046*4882a593Smuzhiyun 
1047*4882a593Smuzhiyun static const struct fb_ops gbefb_ops = {
1048*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
1049*4882a593Smuzhiyun 	.fb_check_var	= gbefb_check_var,
1050*4882a593Smuzhiyun 	.fb_set_par	= gbefb_set_par,
1051*4882a593Smuzhiyun 	.fb_setcolreg	= gbefb_setcolreg,
1052*4882a593Smuzhiyun 	.fb_mmap	= gbefb_mmap,
1053*4882a593Smuzhiyun 	.fb_blank	= gbefb_blank,
1054*4882a593Smuzhiyun 	.fb_fillrect	= cfb_fillrect,
1055*4882a593Smuzhiyun 	.fb_copyarea	= cfb_copyarea,
1056*4882a593Smuzhiyun 	.fb_imageblit	= cfb_imageblit,
1057*4882a593Smuzhiyun };
1058*4882a593Smuzhiyun 
1059*4882a593Smuzhiyun /*
1060*4882a593Smuzhiyun  * sysfs
1061*4882a593Smuzhiyun  */
1062*4882a593Smuzhiyun 
gbefb_show_memsize(struct device * dev,struct device_attribute * attr,char * buf)1063*4882a593Smuzhiyun static ssize_t gbefb_show_memsize(struct device *dev, struct device_attribute *attr, char *buf)
1064*4882a593Smuzhiyun {
1065*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%u\n", gbe_mem_size);
1066*4882a593Smuzhiyun }
1067*4882a593Smuzhiyun 
1068*4882a593Smuzhiyun static DEVICE_ATTR(size, S_IRUGO, gbefb_show_memsize, NULL);
1069*4882a593Smuzhiyun 
gbefb_show_rev(struct device * device,struct device_attribute * attr,char * buf)1070*4882a593Smuzhiyun static ssize_t gbefb_show_rev(struct device *device, struct device_attribute *attr, char *buf)
1071*4882a593Smuzhiyun {
1072*4882a593Smuzhiyun 	return snprintf(buf, PAGE_SIZE, "%d\n", gbe_revision);
1073*4882a593Smuzhiyun }
1074*4882a593Smuzhiyun 
1075*4882a593Smuzhiyun static DEVICE_ATTR(revision, S_IRUGO, gbefb_show_rev, NULL);
1076*4882a593Smuzhiyun 
gbefb_remove_sysfs(struct device * dev)1077*4882a593Smuzhiyun static void gbefb_remove_sysfs(struct device *dev)
1078*4882a593Smuzhiyun {
1079*4882a593Smuzhiyun 	device_remove_file(dev, &dev_attr_size);
1080*4882a593Smuzhiyun 	device_remove_file(dev, &dev_attr_revision);
1081*4882a593Smuzhiyun }
1082*4882a593Smuzhiyun 
gbefb_create_sysfs(struct device * dev)1083*4882a593Smuzhiyun static void gbefb_create_sysfs(struct device *dev)
1084*4882a593Smuzhiyun {
1085*4882a593Smuzhiyun 	device_create_file(dev, &dev_attr_size);
1086*4882a593Smuzhiyun 	device_create_file(dev, &dev_attr_revision);
1087*4882a593Smuzhiyun }
1088*4882a593Smuzhiyun 
1089*4882a593Smuzhiyun /*
1090*4882a593Smuzhiyun  * Initialization
1091*4882a593Smuzhiyun  */
1092*4882a593Smuzhiyun 
gbefb_setup(char * options)1093*4882a593Smuzhiyun static int gbefb_setup(char *options)
1094*4882a593Smuzhiyun {
1095*4882a593Smuzhiyun 	char *this_opt;
1096*4882a593Smuzhiyun 
1097*4882a593Smuzhiyun 	if (!options || !*options)
1098*4882a593Smuzhiyun 		return 0;
1099*4882a593Smuzhiyun 
1100*4882a593Smuzhiyun 	while ((this_opt = strsep(&options, ",")) != NULL) {
1101*4882a593Smuzhiyun 		if (!strncmp(this_opt, "monitor:", 8)) {
1102*4882a593Smuzhiyun 			if (!strncmp(this_opt + 8, "crt", 3)) {
1103*4882a593Smuzhiyun 				flat_panel_enabled = 0;
1104*4882a593Smuzhiyun 				default_var = &default_var_CRT;
1105*4882a593Smuzhiyun 				default_mode = &default_mode_CRT;
1106*4882a593Smuzhiyun 			} else if (!strncmp(this_opt + 8, "1600sw", 6) ||
1107*4882a593Smuzhiyun 				   !strncmp(this_opt + 8, "lcd", 3)) {
1108*4882a593Smuzhiyun 				flat_panel_enabled = 1;
1109*4882a593Smuzhiyun 				default_var = &default_var_LCD;
1110*4882a593Smuzhiyun 				default_mode = &default_mode_LCD;
1111*4882a593Smuzhiyun 			}
1112*4882a593Smuzhiyun 		} else if (!strncmp(this_opt, "mem:", 4)) {
1113*4882a593Smuzhiyun 			gbe_mem_size = memparse(this_opt + 4, &this_opt);
1114*4882a593Smuzhiyun 			if (gbe_mem_size > CONFIG_FB_GBE_MEM * 1024 * 1024)
1115*4882a593Smuzhiyun 				gbe_mem_size = CONFIG_FB_GBE_MEM * 1024 * 1024;
1116*4882a593Smuzhiyun 			if (gbe_mem_size < TILE_SIZE)
1117*4882a593Smuzhiyun 				gbe_mem_size = TILE_SIZE;
1118*4882a593Smuzhiyun 		} else
1119*4882a593Smuzhiyun 			mode_option = this_opt;
1120*4882a593Smuzhiyun 	}
1121*4882a593Smuzhiyun 	return 0;
1122*4882a593Smuzhiyun }
1123*4882a593Smuzhiyun 
gbefb_probe(struct platform_device * p_dev)1124*4882a593Smuzhiyun static int gbefb_probe(struct platform_device *p_dev)
1125*4882a593Smuzhiyun {
1126*4882a593Smuzhiyun 	int i, ret = 0;
1127*4882a593Smuzhiyun 	struct fb_info *info;
1128*4882a593Smuzhiyun 	struct gbefb_par *par;
1129*4882a593Smuzhiyun #ifndef MODULE
1130*4882a593Smuzhiyun 	char *options = NULL;
1131*4882a593Smuzhiyun #endif
1132*4882a593Smuzhiyun 
1133*4882a593Smuzhiyun 	info = framebuffer_alloc(sizeof(struct gbefb_par), &p_dev->dev);
1134*4882a593Smuzhiyun 	if (!info)
1135*4882a593Smuzhiyun 		return -ENOMEM;
1136*4882a593Smuzhiyun 
1137*4882a593Smuzhiyun #ifndef MODULE
1138*4882a593Smuzhiyun 	if (fb_get_options("gbefb", &options)) {
1139*4882a593Smuzhiyun 		ret = -ENODEV;
1140*4882a593Smuzhiyun 		goto out_release_framebuffer;
1141*4882a593Smuzhiyun 	}
1142*4882a593Smuzhiyun 	gbefb_setup(options);
1143*4882a593Smuzhiyun #endif
1144*4882a593Smuzhiyun 
1145*4882a593Smuzhiyun 	if (!request_mem_region(GBE_BASE, sizeof(struct sgi_gbe), "GBE")) {
1146*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: couldn't reserve mmio region\n");
1147*4882a593Smuzhiyun 		ret = -EBUSY;
1148*4882a593Smuzhiyun 		goto out_release_framebuffer;
1149*4882a593Smuzhiyun 	}
1150*4882a593Smuzhiyun 
1151*4882a593Smuzhiyun 	gbe = (struct sgi_gbe *) devm_ioremap(&p_dev->dev, GBE_BASE,
1152*4882a593Smuzhiyun 					      sizeof(struct sgi_gbe));
1153*4882a593Smuzhiyun 	if (!gbe) {
1154*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: couldn't map mmio region\n");
1155*4882a593Smuzhiyun 		ret = -ENXIO;
1156*4882a593Smuzhiyun 		goto out_release_mem_region;
1157*4882a593Smuzhiyun 	}
1158*4882a593Smuzhiyun 	gbe_revision = gbe->ctrlstat & 15;
1159*4882a593Smuzhiyun 
1160*4882a593Smuzhiyun 	gbe_tiles.cpu = dmam_alloc_coherent(&p_dev->dev,
1161*4882a593Smuzhiyun 				GBE_TLB_SIZE * sizeof(uint16_t),
1162*4882a593Smuzhiyun 				&gbe_tiles.dma, GFP_KERNEL);
1163*4882a593Smuzhiyun 	if (!gbe_tiles.cpu) {
1164*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: couldn't allocate tiles table\n");
1165*4882a593Smuzhiyun 		ret = -ENOMEM;
1166*4882a593Smuzhiyun 		goto out_release_mem_region;
1167*4882a593Smuzhiyun 	}
1168*4882a593Smuzhiyun 
1169*4882a593Smuzhiyun 	if (gbe_mem_phys) {
1170*4882a593Smuzhiyun 		/* memory was allocated at boot time */
1171*4882a593Smuzhiyun 		gbe_mem = devm_ioremap_wc(&p_dev->dev, gbe_mem_phys,
1172*4882a593Smuzhiyun 					  gbe_mem_size);
1173*4882a593Smuzhiyun 		if (!gbe_mem) {
1174*4882a593Smuzhiyun 			printk(KERN_ERR "gbefb: couldn't map framebuffer\n");
1175*4882a593Smuzhiyun 			ret = -ENOMEM;
1176*4882a593Smuzhiyun 			goto out_release_mem_region;
1177*4882a593Smuzhiyun 		}
1178*4882a593Smuzhiyun 
1179*4882a593Smuzhiyun 		gbe_dma_addr = 0;
1180*4882a593Smuzhiyun 	} else {
1181*4882a593Smuzhiyun 		/* try to allocate memory with the classical allocator
1182*4882a593Smuzhiyun 		 * this has high chance to fail on low memory machines */
1183*4882a593Smuzhiyun 		gbe_mem = dmam_alloc_attrs(&p_dev->dev, gbe_mem_size,
1184*4882a593Smuzhiyun 				&gbe_dma_addr, GFP_KERNEL,
1185*4882a593Smuzhiyun 				DMA_ATTR_WRITE_COMBINE);
1186*4882a593Smuzhiyun 		if (!gbe_mem) {
1187*4882a593Smuzhiyun 			printk(KERN_ERR "gbefb: couldn't allocate framebuffer memory\n");
1188*4882a593Smuzhiyun 			ret = -ENOMEM;
1189*4882a593Smuzhiyun 			goto out_release_mem_region;
1190*4882a593Smuzhiyun 		}
1191*4882a593Smuzhiyun 
1192*4882a593Smuzhiyun 		gbe_mem_phys = (unsigned long) gbe_dma_addr;
1193*4882a593Smuzhiyun 	}
1194*4882a593Smuzhiyun 
1195*4882a593Smuzhiyun 	par = info->par;
1196*4882a593Smuzhiyun 	par->wc_cookie = arch_phys_wc_add(gbe_mem_phys, gbe_mem_size);
1197*4882a593Smuzhiyun 
1198*4882a593Smuzhiyun 	/* map framebuffer memory into tiles table */
1199*4882a593Smuzhiyun 	for (i = 0; i < (gbe_mem_size >> TILE_SHIFT); i++)
1200*4882a593Smuzhiyun 		gbe_tiles.cpu[i] = (gbe_mem_phys >> TILE_SHIFT) + i;
1201*4882a593Smuzhiyun 
1202*4882a593Smuzhiyun 	info->fbops = &gbefb_ops;
1203*4882a593Smuzhiyun 	info->pseudo_palette = pseudo_palette;
1204*4882a593Smuzhiyun 	info->flags = FBINFO_DEFAULT;
1205*4882a593Smuzhiyun 	info->screen_base = gbe_mem;
1206*4882a593Smuzhiyun 	fb_alloc_cmap(&info->cmap, 256, 0);
1207*4882a593Smuzhiyun 
1208*4882a593Smuzhiyun 	/* reset GBE */
1209*4882a593Smuzhiyun 	gbe_reset();
1210*4882a593Smuzhiyun 
1211*4882a593Smuzhiyun 	/* turn on default video mode */
1212*4882a593Smuzhiyun 	if (fb_find_mode(&par->var, info, mode_option, NULL, 0,
1213*4882a593Smuzhiyun 			 default_mode, 8) == 0)
1214*4882a593Smuzhiyun 		par->var = *default_var;
1215*4882a593Smuzhiyun 	info->var = par->var;
1216*4882a593Smuzhiyun 	gbefb_check_var(&par->var, info);
1217*4882a593Smuzhiyun 	gbefb_encode_fix(&info->fix, &info->var);
1218*4882a593Smuzhiyun 
1219*4882a593Smuzhiyun 	if (register_framebuffer(info) < 0) {
1220*4882a593Smuzhiyun 		printk(KERN_ERR "gbefb: couldn't register framebuffer\n");
1221*4882a593Smuzhiyun 		ret = -ENXIO;
1222*4882a593Smuzhiyun 		goto out_gbe_unmap;
1223*4882a593Smuzhiyun 	}
1224*4882a593Smuzhiyun 
1225*4882a593Smuzhiyun 	platform_set_drvdata(p_dev, info);
1226*4882a593Smuzhiyun 	gbefb_create_sysfs(&p_dev->dev);
1227*4882a593Smuzhiyun 
1228*4882a593Smuzhiyun 	fb_info(info, "%s rev %d @ 0x%08x using %dkB memory\n",
1229*4882a593Smuzhiyun 		info->fix.id, gbe_revision, (unsigned)GBE_BASE,
1230*4882a593Smuzhiyun 		gbe_mem_size >> 10);
1231*4882a593Smuzhiyun 
1232*4882a593Smuzhiyun 	return 0;
1233*4882a593Smuzhiyun 
1234*4882a593Smuzhiyun out_gbe_unmap:
1235*4882a593Smuzhiyun 	arch_phys_wc_del(par->wc_cookie);
1236*4882a593Smuzhiyun out_release_mem_region:
1237*4882a593Smuzhiyun 	release_mem_region(GBE_BASE, sizeof(struct sgi_gbe));
1238*4882a593Smuzhiyun out_release_framebuffer:
1239*4882a593Smuzhiyun 	framebuffer_release(info);
1240*4882a593Smuzhiyun 
1241*4882a593Smuzhiyun 	return ret;
1242*4882a593Smuzhiyun }
1243*4882a593Smuzhiyun 
gbefb_remove(struct platform_device * p_dev)1244*4882a593Smuzhiyun static int gbefb_remove(struct platform_device* p_dev)
1245*4882a593Smuzhiyun {
1246*4882a593Smuzhiyun 	struct fb_info *info = platform_get_drvdata(p_dev);
1247*4882a593Smuzhiyun 	struct gbefb_par *par = info->par;
1248*4882a593Smuzhiyun 
1249*4882a593Smuzhiyun 	unregister_framebuffer(info);
1250*4882a593Smuzhiyun 	gbe_turn_off();
1251*4882a593Smuzhiyun 	arch_phys_wc_del(par->wc_cookie);
1252*4882a593Smuzhiyun 	release_mem_region(GBE_BASE, sizeof(struct sgi_gbe));
1253*4882a593Smuzhiyun 	gbefb_remove_sysfs(&p_dev->dev);
1254*4882a593Smuzhiyun 	framebuffer_release(info);
1255*4882a593Smuzhiyun 
1256*4882a593Smuzhiyun 	return 0;
1257*4882a593Smuzhiyun }
1258*4882a593Smuzhiyun 
1259*4882a593Smuzhiyun static struct platform_driver gbefb_driver = {
1260*4882a593Smuzhiyun 	.probe = gbefb_probe,
1261*4882a593Smuzhiyun 	.remove = gbefb_remove,
1262*4882a593Smuzhiyun 	.driver	= {
1263*4882a593Smuzhiyun 		.name = "gbefb",
1264*4882a593Smuzhiyun 	},
1265*4882a593Smuzhiyun };
1266*4882a593Smuzhiyun 
1267*4882a593Smuzhiyun static struct platform_device *gbefb_device;
1268*4882a593Smuzhiyun 
gbefb_init(void)1269*4882a593Smuzhiyun static int __init gbefb_init(void)
1270*4882a593Smuzhiyun {
1271*4882a593Smuzhiyun 	int ret = platform_driver_register(&gbefb_driver);
1272*4882a593Smuzhiyun 	if (IS_ENABLED(CONFIG_SGI_IP32) && !ret) {
1273*4882a593Smuzhiyun 		gbefb_device = platform_device_alloc("gbefb", 0);
1274*4882a593Smuzhiyun 		if (gbefb_device) {
1275*4882a593Smuzhiyun 			ret = platform_device_add(gbefb_device);
1276*4882a593Smuzhiyun 		} else {
1277*4882a593Smuzhiyun 			ret = -ENOMEM;
1278*4882a593Smuzhiyun 		}
1279*4882a593Smuzhiyun 		if (ret) {
1280*4882a593Smuzhiyun 			platform_device_put(gbefb_device);
1281*4882a593Smuzhiyun 			platform_driver_unregister(&gbefb_driver);
1282*4882a593Smuzhiyun 		}
1283*4882a593Smuzhiyun 	}
1284*4882a593Smuzhiyun 	return ret;
1285*4882a593Smuzhiyun }
1286*4882a593Smuzhiyun 
gbefb_exit(void)1287*4882a593Smuzhiyun static void __exit gbefb_exit(void)
1288*4882a593Smuzhiyun {
1289*4882a593Smuzhiyun 	platform_device_unregister(gbefb_device);
1290*4882a593Smuzhiyun 	platform_driver_unregister(&gbefb_driver);
1291*4882a593Smuzhiyun }
1292*4882a593Smuzhiyun 
1293*4882a593Smuzhiyun module_init(gbefb_init);
1294*4882a593Smuzhiyun module_exit(gbefb_exit);
1295*4882a593Smuzhiyun 
1296*4882a593Smuzhiyun MODULE_LICENSE("GPL");
1297