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