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