File: /usr/src/linux/drivers/video/acornfb.c

1     /*
2      *  linux/drivers/video/acornfb.c
3      *
4      *  Copyright (C) 1998-2001 Russell King
5      *
6      * This program is free software; you can redistribute it and/or modify
7      * it under the terms of the GNU General Public License version 2 as
8      * published by the Free Software Foundation.
9      *
10      * Frame buffer code for Acorn platforms
11      *
12      * NOTE: Most of the modes with X!=640 will disappear shortly.
13      * NOTE: Startup setting of HS & VS polarity not supported.
14      *       (do we need to support it if we're coming up in 640x480?)
15      */
16     
17     #include <linux/config.h>
18     #include <linux/module.h>
19     #include <linux/kernel.h>
20     #include <linux/sched.h>
21     #include <linux/errno.h>
22     #include <linux/string.h>
23     #include <linux/ctype.h>
24     #include <linux/mm.h>
25     #include <linux/tty.h>
26     #include <linux/slab.h>
27     #include <linux/init.h>
28     #include <linux/fb.h>
29     
30     #include <asm/hardware.h>
31     #include <asm/io.h>
32     #include <asm/irq.h>
33     #include <asm/mach-types.h>
34     #include <asm/uaccess.h>
35     
36     #include <video/fbcon.h>
37     #include <video/fbcon-mfb.h>
38     #include <video/fbcon-cfb2.h>
39     #include <video/fbcon-cfb4.h>
40     #include <video/fbcon-cfb8.h>
41     #include <video/fbcon-cfb16.h>
42     #include <video/fbcon-cfb32.h>
43     
44     #include "acornfb.h"
45     
46     /*
47      * VIDC machines can't do 16 or 32BPP modes.
48      */
49     #ifdef HAS_VIDC
50     #undef FBCON_HAS_CFB16
51     #undef FBCON_HAS_CFB32
52     #endif
53     
54     /*
55      * Default resolution.
56      * NOTE that it has to be supported in the table towards
57      * the end of this file.
58      */
59     #define DEFAULT_XRES	640
60     #define DEFAULT_YRES	480
61     /*
62      * The order here defines which BPP we
63      * pick depending on which resolutions
64      * we have configured.
65      */
66     #if   defined(FBCON_HAS_CFB4)
67     # define DEFAULT_BPP	4
68     #elif defined(FBCON_HAS_CFB8)
69     # define DEFAULT_BPP	8
70     #elif defined(FBCON_HAS_CFB16)
71     # define DEFAULT_BPP	16
72     #elif defined(FBCON_HAS_CFB2)
73     # define DEFAULT_BPP	2
74     #elif defined(FBCON_HAS_MFB)
75     # define DEFAULT_BPP	1
76     #else
77     #error No suitable framebuffers configured
78     #endif
79     
80     
81     /*
82      * define this to debug the video mode selection
83      */
84     #undef DEBUG_MODE_SELECTION
85     
86     /*
87      * Translation from RISC OS monitor types to actual
88      * HSYNC and VSYNC frequency ranges.  These are
89      * probably not right, but they're the best info I
90      * have.  Allow 1% either way on the nominal for TVs.
91      */
92     #define NR_MONTYPES	6
93     static struct fb_monspecs monspecs[NR_MONTYPES] __initdata = {
94     	{ 15469, 15781, 49, 51, 0 },	/* TV		*/
95     	{     0, 99999,  0, 99, 0 },	/* Multi Freq	*/
96     	{ 58608, 58608, 64, 64, 0 },	/* Hi-res mono	*/
97     	{ 30000, 70000, 60, 60, 0 },	/* VGA		*/
98     	{ 30000, 70000, 56, 75, 0 },	/* SVGA		*/
99     	{ 30000, 70000, 60, 60, 0 }
100     };
101     
102     static struct display global_disp;
103     static struct fb_info fb_info;
104     static struct acornfb_par current_par;
105     static struct vidc_timing current_vidc;
106     static struct fb_var_screeninfo __initdata init_var = {};
107     
108     extern int acornfb_depth;	/* set by setup.c */
109     extern unsigned int vram_size;	/* set by setup.c */
110     
111     #ifdef HAS_VIDC
112     
113     #define MAX_SIZE	480*1024
114     
115     /* CTL     VIDC	Actual
116      * 24.000  0	 8.000
117      * 25.175  0	 8.392
118      * 36.000  0	12.000
119      * 24.000  1	12.000
120      * 25.175  1	12.588
121      * 24.000  2	16.000
122      * 25.175  2	16.783
123      * 36.000  1	18.000
124      * 24.000  3	24.000
125      * 36.000  2	24.000
126      * 25.175  3	25.175
127      * 36.000  3	36.000
128      */
129     struct pixclock {
130     	u_long	min_clock;
131     	u_long	max_clock;
132     	u_int	vidc_ctl;
133     	u_int	vid_ctl;
134     };
135     
136     static struct pixclock arc_clocks[] = {
137     	/* we allow +/-1% on these */
138     	{ 123750, 126250, VIDC_CTRL_DIV3,   VID_CTL_24MHz },	/*  8.000MHz */
139     	{  82500,  84167, VIDC_CTRL_DIV2,   VID_CTL_24MHz },	/* 12.000MHz */
140     	{  61875,  63125, VIDC_CTRL_DIV1_5, VID_CTL_24MHz },	/* 16.000MHz */
141     	{  41250,  42083, VIDC_CTRL_DIV1,   VID_CTL_24MHz },	/* 24.000MHz */
142     };
143     
144     #ifdef CONFIG_ARCH_A5K
145     static struct pixclock a5k_clocks[] = {
146     	{ 117974, 120357, VIDC_CTRL_DIV3,   VID_CTL_25MHz },	/*  8.392MHz */
147     	{  78649,  80238, VIDC_CTRL_DIV2,   VID_CTL_25MHz },	/* 12.588MHz */
148     	{  58987,  60178, VIDC_CTRL_DIV1_5, VID_CTL_25MHz },	/* 16.588MHz */
149     	{  55000,  56111, VIDC_CTRL_DIV2,   VID_CTL_36MHz },	/* 18.000MHz */
150     	{  39325,  40119, VIDC_CTRL_DIV1,   VID_CTL_25MHz },	/* 25.175MHz */
151     	{  27500,  28055, VIDC_CTRL_DIV1,   VID_CTL_36MHz },	/* 36.000MHz */
152     };
153     #endif
154     
155     static struct pixclock *
156     acornfb_valid_pixrate(u_long pixclock)
157     {
158     	u_int i;
159     
160     	for (i = 0; i < ARRAY_SIZE(arc_clocks); i++)
161     		if (pixclock > arc_clocks[i].min_clock &&
162     		    pixclock < arc_clocks[i].max_clock)
163     			return arc_clocks + i;
164     
165     #ifdef CONFIG_ARCH_A5K
166     	if (machine_is_a5k()) {
167     		for (i = 0; i < ARRAY_SIZE(a5k_clocks); i++)
168     			if (pixclock > a5k_clocks[i].min_clock &&
169     			    pixclock < a5k_clocks[i].max_clock)
170     				return a5k_clocks + i;
171     	}
172     #endif
173     
174     	return NULL;
175     }
176     
177     /* VIDC Rules:
178      * hcr  : must be even (interlace, hcr/2 must be even)
179      * hswr : must be even
180      * hdsr : must be odd
181      * hder : must be odd
182      *
183      * vcr  : must be odd
184      * vswr : >= 1
185      * vdsr : >= 1
186      * vder : >= vdsr
187      * if interlaced, then hcr/2 must be even
188      */
189     static void
190     acornfb_set_timing(struct fb_var_screeninfo *var)
191     {
192     	struct pixclock *pclk;
193     	struct vidc_timing vidc;
194     	u_int horiz_correction;
195     	u_int sync_len, display_start, display_end, cycle;
196     	u_int is_interlaced;
197     	u_int vid_ctl, vidc_ctl;
198     	u_int bandwidth;
199     
200     	memset(&vidc, 0, sizeof(vidc));
201     
202     	pclk = acornfb_valid_pixrate(var->pixclock);
203     	vidc_ctl = pclk->vidc_ctl;
204     	vid_ctl  = pclk->vid_ctl;
205     
206     	bandwidth = var->pixclock * 8 / var->bits_per_pixel;
207     	/* 25.175, 4bpp = 79.444ns per byte, 317.776ns per word: fifo = 2,6 */
208     	if (bandwidth > 143500)
209     		vidc_ctl |= VIDC_CTRL_FIFO_3_7;
210     	else if (bandwidth > 71750)
211     		vidc_ctl |= VIDC_CTRL_FIFO_2_6;
212     	else if (bandwidth > 35875)
213     		vidc_ctl |= VIDC_CTRL_FIFO_1_5;
214     	else
215     		vidc_ctl |= VIDC_CTRL_FIFO_0_4;
216     
217     	switch (var->bits_per_pixel) {
218     	case 1:
219     		horiz_correction = 19;
220     		vidc_ctl |= VIDC_CTRL_1BPP;
221     		break;
222     
223     	case 2:
224     		horiz_correction = 11;
225     		vidc_ctl |= VIDC_CTRL_2BPP;
226     		break;
227     
228     	case 4:
229     		horiz_correction = 7;
230     		vidc_ctl |= VIDC_CTRL_4BPP;
231     		break;
232     
233     	default:
234     	case 8:
235     		horiz_correction = 5;
236     		vidc_ctl |= VIDC_CTRL_8BPP;
237     		break;
238     	}
239     
240     	if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */
241     		vidc_ctl |= VIDC_CTRL_CSYNC;
242     	else {
243     		if (!(var->sync & FB_SYNC_HOR_HIGH_ACT))
244     			vid_ctl |= VID_CTL_HS_NHSYNC;
245     
246     		if (!(var->sync & FB_SYNC_VERT_HIGH_ACT))
247     			vid_ctl |= VID_CTL_VS_NVSYNC;
248     	}
249     
250     	sync_len	= var->hsync_len;
251     	display_start	= sync_len + var->left_margin;
252     	display_end	= display_start + var->xres;
253     	cycle		= display_end + var->right_margin;
254     
255     	/* if interlaced, then hcr/2 must be even */
256     	is_interlaced = (var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED;
257     
258     	if (is_interlaced) {
259     		vidc_ctl |= VIDC_CTRL_INTERLACE;
260     		if (cycle & 2) {
261     			cycle += 2;
262     			var->right_margin += 2;
263     		}
264     	}
265     
266     	vidc.h_cycle		= (cycle - 2) / 2;
267     	vidc.h_sync_width	= (sync_len - 2) / 2;
268     	vidc.h_border_start	= (display_start - 1) / 2;
269     	vidc.h_display_start	= (display_start - horiz_correction) / 2;
270     	vidc.h_display_end	= (display_end - horiz_correction) / 2;
271     	vidc.h_border_end	= (display_end - 1) / 2;
272     	vidc.h_interlace	= (vidc.h_cycle + 1) / 2;
273     
274     	sync_len	= var->vsync_len;
275     	display_start	= sync_len + var->upper_margin;
276     	display_end	= display_start + var->yres;
277     	cycle		= display_end + var->lower_margin;
278     
279     	if (is_interlaced)
280     		cycle = (cycle - 3) / 2;
281     	else
282     		cycle = cycle - 1;
283     
284     	vidc.v_cycle		= cycle;
285     	vidc.v_sync_width	= sync_len - 1;
286     	vidc.v_border_start	= display_start - 1;
287     	vidc.v_display_start	= vidc.v_border_start;
288     	vidc.v_display_end	= display_end - 1;
289     	vidc.v_border_end	= vidc.v_display_end;
290     
291     	if (machine_is_a5k())
292     		__raw_writeb(vid_ctl, IOEB_VID_CTL);
293     
294     	if (memcmp(&current_vidc, &vidc, sizeof(vidc))) {
295     		current_vidc = vidc;
296     
297     		vidc_writel(0xe0000000 | vidc_ctl);
298     		vidc_writel(0x80000000 | (vidc.h_cycle << 14));
299     		vidc_writel(0x84000000 | (vidc.h_sync_width << 14));
300     		vidc_writel(0x88000000 | (vidc.h_border_start << 14));
301     		vidc_writel(0x8c000000 | (vidc.h_display_start << 14));
302     		vidc_writel(0x90000000 | (vidc.h_display_end << 14));
303     		vidc_writel(0x94000000 | (vidc.h_border_end << 14));
304     		vidc_writel(0x98000000);
305     		vidc_writel(0x9c000000 | (vidc.h_interlace << 14));
306     		vidc_writel(0xa0000000 | (vidc.v_cycle << 14));
307     		vidc_writel(0xa4000000 | (vidc.v_sync_width << 14));
308     		vidc_writel(0xa8000000 | (vidc.v_border_start << 14));
309     		vidc_writel(0xac000000 | (vidc.v_display_start << 14));
310     		vidc_writel(0xb0000000 | (vidc.v_display_end << 14));
311     		vidc_writel(0xb4000000 | (vidc.v_border_end << 14));
312     		vidc_writel(0xb8000000);
313     		vidc_writel(0xbc000000);
314     	}
315     #ifdef DEBUG_MODE_SELECTION
316     	printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,
317     	       var->yres, var->bits_per_pixel);
318     	printk(KERN_DEBUG " H-cycle          : %d\n", vidc.h_cycle);
319     	printk(KERN_DEBUG " H-sync-width     : %d\n", vidc.h_sync_width);
320     	printk(KERN_DEBUG " H-border-start   : %d\n", vidc.h_border_start);
321     	printk(KERN_DEBUG " H-display-start  : %d\n", vidc.h_display_start);
322     	printk(KERN_DEBUG " H-display-end    : %d\n", vidc.h_display_end);
323     	printk(KERN_DEBUG " H-border-end     : %d\n", vidc.h_border_end);
324     	printk(KERN_DEBUG " H-interlace      : %d\n", vidc.h_interlace);
325     	printk(KERN_DEBUG " V-cycle          : %d\n", vidc.v_cycle);
326     	printk(KERN_DEBUG " V-sync-width     : %d\n", vidc.v_sync_width);
327     	printk(KERN_DEBUG " V-border-start   : %d\n", vidc.v_border_start);
328     	printk(KERN_DEBUG " V-display-start  : %d\n", vidc.v_display_start);
329     	printk(KERN_DEBUG " V-display-end    : %d\n", vidc.v_display_end);
330     	printk(KERN_DEBUG " V-border-end     : %d\n", vidc.v_border_end);
331     	printk(KERN_DEBUG " VIDC Ctrl (E)    : 0x%08X\n", vidc_ctl);
332     	printk(KERN_DEBUG " IOEB Ctrl        : 0x%08X\n", vid_ctl);
333     #endif
334     }
335     
336     static inline void
337     acornfb_palette_write(u_int regno, union palette pal)
338     {
339     	vidc_writel(pal.p);
340     }
341     
342     static inline union palette
343     acornfb_palette_encode(u_int regno, u_int red, u_int green, u_int blue,
344     		       u_int trans)
345     {
346     	union palette pal;
347     
348     	pal.p = 0;
349     	pal.vidc.reg   = regno;
350     	pal.vidc.red   = red >> 12;
351     	pal.vidc.green = green >> 12;
352     	pal.vidc.blue  = blue >> 12;
353     	return pal;
354     }
355     
356     static void
357     acornfb_palette_decode(u_int regno, u_int *red, u_int *green, u_int *blue,
358     		       u_int *trans)
359     {
360     	*red   = EXTEND4(current_par.palette[regno].vidc.red);
361     	*green = EXTEND4(current_par.palette[regno].vidc.green);
362     	*blue  = EXTEND4(current_par.palette[regno].vidc.blue);
363     	*trans = current_par.palette[regno].vidc.trans ? -1 : 0;
364     }
365     #endif
366     
367     #ifdef HAS_VIDC20
368     #include <asm/arch/acornfb.h>
369     
370     #define MAX_SIZE	2*1024*1024
371     
372     /* VIDC20 has a different set of rules from the VIDC:
373      *  hcr  : must be multiple of 4
374      *  hswr : must be even
375      *  hdsr : must be even
376      *  hder : must be even
377      *  vcr  : >= 2, (interlace, must be odd)
378      *  vswr : >= 1
379      *  vdsr : >= 1
380      *  vder : >= vdsr
381      */
382     static void
383     acornfb_set_timing(struct fb_var_screeninfo *var)
384     {
385     	struct vidc_timing vidc;
386     	u_int vcr, fsize;
387     	u_int ext_ctl, dat_ctl;
388     	u_int words_per_line;
389     
390     	memset(&vidc, 0, sizeof(vidc));
391     
392     	vidc.h_sync_width	= var->hsync_len - 8;
393     	vidc.h_border_start	= vidc.h_sync_width + var->left_margin + 8 - 12;
394     	vidc.h_display_start	= vidc.h_border_start + 12 - 18;
395     	vidc.h_display_end	= vidc.h_display_start + var->xres;
396     	vidc.h_border_end	= vidc.h_display_end + 18 - 12;
397     	vidc.h_cycle		= vidc.h_border_end + var->right_margin + 12 - 8;
398     	vidc.h_interlace	= vidc.h_cycle / 2;
399     	vidc.v_sync_width	= var->vsync_len - 1;
400     	vidc.v_border_start	= vidc.v_sync_width + var->upper_margin;
401     	vidc.v_display_start	= vidc.v_border_start;
402     	vidc.v_display_end	= vidc.v_display_start + var->yres;
403     	vidc.v_border_end	= vidc.v_display_end;
404     	vidc.control		= acornfb_default_control();
405     
406     	vcr = var->vsync_len + var->upper_margin + var->yres +
407     	      var->lower_margin;
408     
409     	if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
410     		vidc.v_cycle = (vcr - 3) / 2;
411     		vidc.control |= VIDC20_CTRL_INT;
412     	} else
413     		vidc.v_cycle = vcr - 2;
414     
415     	switch (var->bits_per_pixel) {
416     	case  1: vidc.control |= VIDC20_CTRL_1BPP;	break;
417     	case  2: vidc.control |= VIDC20_CTRL_2BPP;	break;
418     	case  4: vidc.control |= VIDC20_CTRL_4BPP;	break;
419     	default:
420     	case  8: vidc.control |= VIDC20_CTRL_8BPP;	break;
421     	case 16: vidc.control |= VIDC20_CTRL_16BPP;	break;
422     	case 32: vidc.control |= VIDC20_CTRL_32BPP;	break;
423     	}
424     
425     	acornfb_vidc20_find_rates(&vidc, var);
426     	fsize = var->vsync_len + var->upper_margin + var->lower_margin - 1;
427     
428     	if (memcmp(&current_vidc, &vidc, sizeof(vidc))) {
429     		current_vidc = vidc;
430     
431     		vidc_writel(VIDC20_CTRL| vidc.control);
432     		vidc_writel(0xd0000000 | vidc.pll_ctl);
433     		vidc_writel(0x80000000 | vidc.h_cycle);
434     		vidc_writel(0x81000000 | vidc.h_sync_width);
435     		vidc_writel(0x82000000 | vidc.h_border_start);
436     		vidc_writel(0x83000000 | vidc.h_display_start);
437     		vidc_writel(0x84000000 | vidc.h_display_end);
438     		vidc_writel(0x85000000 | vidc.h_border_end);
439     		vidc_writel(0x86000000);
440     		vidc_writel(0x87000000 | vidc.h_interlace);
441     		vidc_writel(0x90000000 | vidc.v_cycle);
442     		vidc_writel(0x91000000 | vidc.v_sync_width);
443     		vidc_writel(0x92000000 | vidc.v_border_start);
444     		vidc_writel(0x93000000 | vidc.v_display_start);
445     		vidc_writel(0x94000000 | vidc.v_display_end);
446     		vidc_writel(0x95000000 | vidc.v_border_end);
447     		vidc_writel(0x96000000);
448     		vidc_writel(0x97000000);
449     	}
450     
451     	iomd_writel(fsize, IOMD_FSIZE);
452     
453     	ext_ctl = acornfb_default_econtrol();
454     
455     	if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */
456     		ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC;
457     	else {
458     		if (var->sync & FB_SYNC_HOR_HIGH_ACT)
459     			ext_ctl |= VIDC20_ECTL_HS_HSYNC;
460     		else
461     			ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
462     
463     		if (var->sync & FB_SYNC_VERT_HIGH_ACT)
464     			ext_ctl |= VIDC20_ECTL_VS_VSYNC;
465     		else
466     			ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
467     	}
468     
469     	vidc_writel(VIDC20_ECTL | ext_ctl);
470     
471     	words_per_line = var->xres * var->bits_per_pixel / 32;
472     
473     	if (current_par.using_vram && current_par.screen_size == 2048*1024)
474     		words_per_line /= 2;
475     
476     	/* RiscPC doesn't use the VIDC's VRAM control. */
477     	dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line;
478     
479     	/* The data bus width is dependent on both the type
480     	 * and amount of video memory.
481     	 *     DRAM	32bit low
482     	 * 1MB VRAM	32bit
483     	 * 2MB VRAM	64bit
484     	 */
485     	if (current_par.using_vram && current_par.vram_half_sam == 2048) {
486     		dat_ctl |= VIDC20_DCTL_BUS_D63_0;
487     	} else 
488     		dat_ctl |= VIDC20_DCTL_BUS_D31_0;
489     
490     	vidc_writel(VIDC20_DCTL | dat_ctl);
491     
492     #ifdef DEBUG_MODE_SELECTION
493     	printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,
494     	       var->yres, var->bits_per_pixel);
495     	printk(KERN_DEBUG " H-cycle          : %d\n", vidc.h_cycle);
496     	printk(KERN_DEBUG " H-sync-width     : %d\n", vidc.h_sync_width);
497     	printk(KERN_DEBUG " H-border-start   : %d\n", vidc.h_border_start);
498     	printk(KERN_DEBUG " H-display-start  : %d\n", vidc.h_display_start);
499     	printk(KERN_DEBUG " H-display-end    : %d\n", vidc.h_display_end);
500     	printk(KERN_DEBUG " H-border-end     : %d\n", vidc.h_border_end);
501     	printk(KERN_DEBUG " H-interlace      : %d\n", vidc.h_interlace);
502     	printk(KERN_DEBUG " V-cycle          : %d\n", vidc.v_cycle);
503     	printk(KERN_DEBUG " V-sync-width     : %d\n", vidc.v_sync_width);
504     	printk(KERN_DEBUG " V-border-start   : %d\n", vidc.v_border_start);
505     	printk(KERN_DEBUG " V-display-start  : %d\n", vidc.v_display_start);
506     	printk(KERN_DEBUG " V-display-end    : %d\n", vidc.v_display_end);
507     	printk(KERN_DEBUG " V-border-end     : %d\n", vidc.v_border_end);
508     	printk(KERN_DEBUG " Ext Ctrl  (C)    : 0x%08X\n", ext_ctl);
509     	printk(KERN_DEBUG " PLL Ctrl  (D)    : 0x%08X\n", vidc.pll_ctl);
510     	printk(KERN_DEBUG " Ctrl      (E)    : 0x%08X\n", vidc.control);
511     	printk(KERN_DEBUG " Data Ctrl (F)    : 0x%08X\n", dat_ctl);
512     	printk(KERN_DEBUG " Fsize            : 0x%08X\n", fsize);
513     #endif
514     }
515     
516     static inline void
517     acornfb_palette_write(u_int regno, union palette pal)
518     {
519     	vidc_writel(0x10000000 | regno);
520     	vidc_writel(pal.p);
521     }
522     
523     static inline union palette
524     acornfb_palette_encode(u_int regno, u_int red, u_int green, u_int blue,
525     		       u_int trans)
526     {
527     	union palette pal;
528     
529     	pal.p = 0;
530     	pal.vidc20.red   = red >> 8;
531     	pal.vidc20.green = green >> 8;
532     	pal.vidc20.blue  = blue >> 8;
533     	return pal;
534     }
535     
536     static void
537     acornfb_palette_decode(u_int regno, u_int *red, u_int *green, u_int *blue,
538     		       u_int *trans)
539     {
540     	*red   = EXTEND8(current_par.palette[regno].vidc20.red);
541     	*green = EXTEND8(current_par.palette[regno].vidc20.green);
542     	*blue  = EXTEND8(current_par.palette[regno].vidc20.blue);
543     	*trans = EXTEND4(current_par.palette[regno].vidc20.ext);
544     }
545     #endif
546     
547     /*
548      * Before selecting the timing parameters, adjust
549      * the resolution to fit the rules.
550      */
551     static int
552     acornfb_adjust_timing(struct fb_var_screeninfo *var, int con)
553     {
554     	u_int font_line_len;
555     	u_int fontht;
556     	u_int sam_size, min_size, size;
557     	u_int nr_y;
558     
559     	/* xres must be even */
560     	var->xres = (var->xres + 1) & ~1;
561     
562     	/*
563     	 * We don't allow xres_virtual to differ from xres
564     	 */
565     	var->xres_virtual = var->xres;
566     	var->xoffset = 0;
567     
568     	/*
569     	 * Find the font height
570     	 */
571     	if (con == -1)
572     		fontht = fontheight(&global_disp);
573     	else
574     		fontht = fontheight(fb_display + con);
575     
576     	if (fontht == 0)
577     		fontht = 8;
578     
579     	if (current_par.using_vram)
580     		sam_size = current_par.vram_half_sam * 2;
581     	else
582     		sam_size = 16;
583     
584     	/*
585     	 * Now, find a value for yres_virtual which allows
586     	 * us to do ywrap scrolling.  The value of
587     	 * yres_virtual must be such that the end of the
588     	 * displayable frame buffer must be aligned with
589     	 * the start of a font line.
590     	 */
591     	font_line_len = var->xres * var->bits_per_pixel * fontht / 8;
592     	min_size = var->xres * var->yres * var->bits_per_pixel / 8;
593     
594     	/*
595     	 * If minimum screen size is greater than that we have
596     	 * available, reject it.
597     	 */
598     	if (min_size > current_par.screen_size)
599     		return -EINVAL;
600     
601     	/* Find int 'y', such that y * fll == s * sam < maxsize
602     	 * y = s * sam / fll; s = maxsize / sam
603     	 */
604     	for (size = current_par.screen_size; min_size <= size;
605     	     size -= sam_size) {
606     		nr_y = size / font_line_len;
607     
608     		if (nr_y * font_line_len == size)
609     			break;
610     	}
611     
612     	if (var->accel_flags & FB_ACCELF_TEXT) {
613     		if (min_size > size) {
614     			/*
615     			 * failed, use ypan
616     			 */
617     			size = current_par.screen_size;
618     			var->yres_virtual = size / (font_line_len / fontht);
619     		} else
620     			var->yres_virtual = nr_y * fontht;
621     	}
622     
623     	current_par.screen_end = current_par.screen_base_p + size;
624     
625     	/*
626     	 * Fix yres & yoffset if needed.
627     	 */
628     	if (var->yres > var->yres_virtual)
629     		var->yres = var->yres_virtual;
630     
631     	if (var->vmode & FB_VMODE_YWRAP) {
632     		if (var->yoffset > var->yres_virtual)
633     			var->yoffset = var->yres_virtual;
634     	} else {
635     		if (var->yoffset + var->yres > var->yres_virtual)
636     			var->yoffset = var->yres_virtual - var->yres;
637     	}
638     
639     	/* hsync_len must be even */
640     	var->hsync_len = (var->hsync_len + 1) & ~1;
641     
642     #ifdef HAS_VIDC
643     	/* left_margin must be odd */
644     	if ((var->left_margin & 1) == 0) {
645     		var->left_margin -= 1;
646     		var->right_margin += 1;
647     	}
648     
649     	/* right_margin must be odd */
650     	var->right_margin |= 1;
651     #elif defined(HAS_VIDC20)
652     	/* left_margin must be even */
653     	if (var->left_margin & 1) {
654     		var->left_margin += 1;
655     		var->right_margin -= 1;
656     	}
657     
658     	/* right_margin must be even */
659     	if (var->right_margin & 1)
660     		var->right_margin += 1;
661     #endif
662     
663     	if (var->vsync_len < 1)
664     		var->vsync_len = 1;
665     
666     	return 0;
667     }
668     
669     static int
670     acornfb_validate_timing(struct fb_var_screeninfo *var,
671     			struct fb_monspecs *monspecs)
672     {
673     	unsigned long hs, vs;
674     
675     	/*
676     	 * hs(Hz) = 10^12 / (pixclock * xtotal)
677     	 * vs(Hz) = hs(Hz) / ytotal
678     	 *
679     	 * No need to do long long divisions or anything
680     	 * like that if you factor it correctly
681     	 */
682     	hs = 1953125000 / var->pixclock;
683     	hs = hs * 512 /
684     	     (var->xres + var->left_margin + var->right_margin + var->hsync_len);
685     	vs = hs /
686     	     (var->yres + var->upper_margin + var->lower_margin + var->vsync_len);
687     
688     	return (vs >= monspecs->vfmin && vs <= monspecs->vfmax &&
689     		hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL;
690     }
691     
692     static inline void
693     acornfb_update_dma(struct fb_var_screeninfo *var)
694     {
695     	int off = (var->yoffset * var->xres_virtual *
696     		   var->bits_per_pixel) >> 3;
697     
698     #if defined(HAS_MEMC)
699     	memc_write(VDMA_INIT, off >> 2);
700     #elif defined(HAS_IOMD)
701     	iomd_writel(current_par.screen_base_p + off, IOMD_VIDINIT);
702     #endif
703     }
704     
705     static int
706     acornfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
707     		  u_int *trans, struct fb_info *info)
708     {
709     	if (regno >= current_par.palette_size)
710     		return 1;
711     
712     	acornfb_palette_decode(regno, red, green, blue, trans);
713     
714     	return 0;
715     }
716     
717     /*
718      * We have to take note of the VIDC20's 16-bit palette here.
719      * The VIDC20 looks up a 16 bit pixel as follows:
720      *
721      *   bits   111111
722      *          5432109876543210
723      *   red            ++++++++  (8 bits,  7 to 0)
724      *  green       ++++++++      (8 bits, 11 to 4)
725      *   blue   ++++++++          (8 bits, 15 to 8)
726      *
727      * We use a pixel which looks like:
728      *
729      *   bits   111111
730      *          5432109876543210
731      *   red               +++++  (5 bits,  4 to  0)
732      *  green         +++++       (5 bits,  9 to  5)
733      *   blue    +++++            (5 bits, 14 to 10)
734      */
735     static int
736     acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
737     		  u_int trans, struct fb_info *info)
738     {
739     	union palette pal;
740     	int bpp = fb_display[current_par.currcon].var.bits_per_pixel;
741     
742     	if (regno >= current_par.palette_size)
743     		return 1;
744     
745     	pal = acornfb_palette_encode(regno, red, green, blue, trans);
746     	current_par.palette[regno] = pal;
747     
748     #ifdef FBCON_HAS_CFB32
749     	if (bpp == 32 && regno < 16) {
750     		current_par.cmap.cfb32[regno] =
751     				regno | regno << 8 | regno << 16;
752     	}
753     #endif
754     #ifdef FBCON_HAS_CFB16
755     	if (bpp == 16 && regno < 16) {
756     		int i;
757     
758     		current_par.cmap.cfb16[regno] =
759     				regno | regno << 5 | regno << 10;
760     
761     		pal.p = 0;
762     		vidc_writel(0x10000000);
763     		for (i = 0; i < 256; i += 1) {
764     			pal.vidc20.red   = current_par.palette[ i       & 31].vidc20.red;
765     			pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
766     			pal.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
767     			vidc_writel(pal.p);
768     			/* Palette register pointer auto-increments */
769     		}
770     	} else
771     #endif
772     		acornfb_palette_write(regno, pal);
773     
774     	return 0;
775     }
776     
777     static int
778     acornfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
779     		 struct fb_info *info)
780     {
781     	int err = 0;
782     
783     	if (con == current_par.currcon)
784     		err = fb_get_cmap(cmap, kspc, acornfb_getcolreg, info);
785     	else if (fb_display[con].cmap.len)
786     		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
787     	else
788     		fb_copy_cmap(fb_default_cmap(current_par.palette_size),
789     			     cmap, kspc ? 0 : 2);
790     	return err;
791     }
792     
793     static int
794     acornfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
795     		 struct fb_info *info)
796     {
797     	int err = 0;
798     
799     	if (!fb_display[con].cmap.len)
800     		err = fb_alloc_cmap(&fb_display[con].cmap,
801     				    current_par.palette_size, 0);
802     	if (!err) {
803     		if (con == current_par.currcon)
804     			err = fb_set_cmap(cmap, kspc, acornfb_setcolreg,
805     					  info);
806     		else
807     			fb_copy_cmap(cmap, &fb_display[con].cmap,
808     				     kspc ? 0 : 1);
809     	}
810     	return err;
811     }
812     
813     static int
814     acornfb_decode_var(struct fb_var_screeninfo *var, int con)
815     {
816     	int err;
817     
818     #if defined(HAS_VIDC20)
819     	var->red.offset    = 0;
820     	var->red.length    = 8;
821     	var->green         = var->red;
822     	var->blue          = var->red;
823     	var->transp.offset = 0;
824     	var->transp.length = 4;
825     #elif defined(HAS_VIDC)
826     	var->red.length	   = 4;
827     	var->green         = var->red;
828     	var->blue          = var->red;
829     	var->transp.length = 1;
830     #endif
831     
832     	switch (var->bits_per_pixel) {
833     #ifdef FBCON_HAS_MFB
834     	case 1:
835     		break;
836     #endif
837     #ifdef FBCON_HAS_CFB2
838     	case 2:
839     		break;
840     #endif
841     #ifdef FBCON_HAS_CFB4
842     	case 4:
843     		break;
844     #endif
845     #ifdef FBCON_HAS_CFB8
846     	case 8:
847     		break;
848     #endif
849     #ifdef FBCON_HAS_CFB16
850     	case 16:
851     		var->red.offset    = 0;
852     		var->red.length    = 5;
853     		var->green.offset  = 5;
854     		var->green.length  = 5;
855     		var->blue.offset   = 10;
856     		var->blue.length   = 5;
857     		var->transp.offset = 15;
858     		var->transp.length = 1;
859     		break;
860     #endif
861     #ifdef FBCON_HAS_CFB32
862     	case 32:
863     		var->red.offset    = 0;
864     		var->red.length    = 8;
865     		var->green.offset  = 8;
866     		var->green.length  = 8;
867     		var->blue.offset   = 16;
868     		var->blue.length   = 8;
869     		var->transp.offset = 24;
870     		var->transp.length = 4;
871     		break;
872     #endif
873     	default:
874     		return -EINVAL;
875     	}
876     
877     	/*
878     	 * Check to see if the pixel rate is valid.
879     	 */
880     	if (!var->pixclock || !acornfb_valid_pixrate(var->pixclock))
881     		return -EINVAL;
882     
883     	/*
884     	 * Validate and adjust the resolution to
885     	 * match the video generator hardware.
886     	 */
887     	err = acornfb_adjust_timing(var, con);
888     	if (err)
889     		return err;
890     
891     	/*
892     	 * Validate the timing against the
893     	 * monitor hardware.
894     	 */
895     	return acornfb_validate_timing(var, &fb_info.monspecs);
896     }
897     
898     static int
899     acornfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
900     {
901     	struct display *display;
902     
903     	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
904     	strcpy(fix->id, "Acorn");
905     
906     	if (con >= 0)
907     		display = fb_display + con;
908     	else
909     		display = &global_disp;
910     
911     	fix->smem_start	 = current_par.screen_base_p;
912     	fix->smem_len	 = current_par.screen_size;
913     	fix->type	 = display->type;
914     	fix->type_aux	 = display->type_aux;
915     	fix->xpanstep	 = 0;
916     	fix->ypanstep	 = display->ypanstep;
917     	fix->ywrapstep	 = display->ywrapstep;
918     	fix->visual	 = display->visual;
919     	fix->line_length = display->line_length;
920     	fix->accel	 = FB_ACCEL_NONE;
921     
922     	return 0;
923     }
924     
925     static int
926     acornfb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
927     {
928     	if (con == -1) {
929     		*var = global_disp.var;
930     	} else
931     		*var = fb_display[con].var;
932     
933     	return 0;
934     }
935     
936     static int
937     acornfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
938     {
939     	struct display *display;
940     	int err, chgvar = 0;
941     
942     	if (con >= 0)
943     		display = fb_display + con;
944     	else
945     		display = &global_disp;
946     
947     	err = acornfb_decode_var(var, con);
948     	if (err)
949     		return err;
950     
951     	switch (var->activate & FB_ACTIVATE_MASK) {
952     	case FB_ACTIVATE_TEST:
953     		return 0;
954     
955     	case FB_ACTIVATE_NXTOPEN:
956     	case FB_ACTIVATE_NOW:
957     		break;
958     
959     	default:
960     		return -EINVAL;
961     	}
962     
963     	if (con >= 0) {
964     		if (display->var.xres != var->xres)
965     			chgvar = 1;
966     		if (display->var.yres != var->yres)
967     			chgvar = 1;
968     		if (display->var.xres_virtual != var->xres_virtual)
969     			chgvar = 1;
970     		if (display->var.yres_virtual != var->yres_virtual)
971     			chgvar = 1;
972     		if (memcmp(&display->var.red, &var->red, sizeof(var->red)))
973     			chgvar = 1;
974     		if (memcmp(&display->var.green, &var->green, sizeof(var->green)))
975     			chgvar = 1;
976     		if (memcmp(&display->var.blue, &var->blue, sizeof(var->blue)))
977     			chgvar = 1;
978     	}
979     
980     	display->var = *var;
981     	display->var.activate &= ~FB_ACTIVATE_ALL;
982     
983     	if (var->activate & FB_ACTIVATE_ALL)
984     		global_disp.var = display->var;
985     
986     	switch (display->var.bits_per_pixel) {
987     #ifdef FBCON_HAS_MFB
988     	case 1:
989     		current_par.palette_size = 2;
990     		display->dispsw = &fbcon_mfb;
991     		display->visual = FB_VISUAL_MONO10;
992     		break;
993     #endif
994     #ifdef FBCON_HAS_CFB2
995     	case 2:
996     		current_par.palette_size = 4;
997     		display->dispsw = &fbcon_cfb2;
998     		display->visual = FB_VISUAL_PSEUDOCOLOR;
999     		break;
1000     #endif
1001     #ifdef FBCON_HAS_CFB4
1002     	case 4:
1003     		current_par.palette_size = 16;
1004     		display->dispsw = &fbcon_cfb4;
1005     		display->visual = FB_VISUAL_PSEUDOCOLOR;
1006     		break;
1007     #endif
1008     #ifdef FBCON_HAS_CFB8
1009     	case 8:
1010     		current_par.palette_size = VIDC_PALETTE_SIZE;
1011     		display->dispsw = &fbcon_cfb8;
1012     #ifdef HAS_VIDC
1013     		display->visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
1014     #else
1015     		display->visual = FB_VISUAL_PSEUDOCOLOR;
1016     #endif
1017     		break;
1018     #endif
1019     #ifdef FBCON_HAS_CFB16
1020     	case 16:
1021     		current_par.palette_size = 32;
1022     		display->dispsw = &fbcon_cfb16;
1023     		display->dispsw_data = current_par.cmap.cfb16;
1024     		display->visual = FB_VISUAL_DIRECTCOLOR;
1025     		break;
1026     #endif
1027     #ifdef FBCON_HAS_CFB32
1028     	case 32:
1029     		current_par.palette_size = VIDC_PALETTE_SIZE;
1030     		display->dispsw = &fbcon_cfb32;
1031     		display->dispsw_data = current_par.cmap.cfb32;
1032     		display->visual = FB_VISUAL_TRUECOLOR;
1033     		break;
1034     #endif
1035     	default:
1036     		display->dispsw = &fbcon_dummy;
1037     		break;
1038     	}
1039     
1040     	display->screen_base	= (char *)current_par.screen_base;
1041     	display->type		= FB_TYPE_PACKED_PIXELS;
1042     	display->type_aux	= 0;
1043     	display->ypanstep	= 1;
1044     	display->ywrapstep	= 1;
1045     	display->line_length	=
1046     	display->next_line      = (var->xres * var->bits_per_pixel) / 8;
1047     	display->can_soft_blank	= display->visual == FB_VISUAL_PSEUDOCOLOR ? 1 : 0;
1048     	display->inverse	= 0;
1049     
1050     	if (chgvar && info && info->changevar)
1051     		info->changevar(con);
1052     
1053     	if (con == current_par.currcon) {
1054     		struct fb_cmap *cmap;
1055     		unsigned long start, size;
1056     		int control;
1057     
1058     #if defined(HAS_MEMC)
1059     		start   = 0;
1060     		size    = current_par.screen_size - VDMA_XFERSIZE;
1061     		control = 0;
1062     
1063     		memc_write(VDMA_START, start);
1064     		memc_write(VDMA_END, size >> 2);
1065     #elif defined(HAS_IOMD)
1066     
1067     		start = current_par.screen_base_p;
1068     		size  = current_par.screen_end;
1069     
1070     		if (current_par.using_vram) {
1071     			size -= current_par.vram_half_sam;
1072     			control = DMA_CR_E | (current_par.vram_half_sam / 256);
1073     		} else {
1074     			size -= 16;
1075     			control = DMA_CR_E | DMA_CR_D | 16;
1076     		}
1077     
1078     		iomd_writel(start,   IOMD_VIDSTART);
1079     		iomd_writel(size,    IOMD_VIDEND);
1080     		iomd_writel(control, IOMD_VIDCR);
1081     #endif
1082     		acornfb_update_dma(var);
1083     		acornfb_set_timing(var);
1084     
1085     		if (display->cmap.len)
1086     			cmap = &display->cmap;
1087     		else
1088     			cmap = fb_default_cmap(current_par.palette_size);
1089     
1090     		fb_set_cmap(cmap, 1, acornfb_setcolreg, info);
1091     	}
1092     	return 0;
1093     }
1094     
1095     static int
1096     acornfb_pan_display(struct fb_var_screeninfo *var, int con,
1097     		    struct fb_info *info)
1098     {
1099     	u_int y_bottom;
1100     
1101     	if (var->xoffset)
1102     		return -EINVAL;
1103     
1104     	y_bottom = var->yoffset;
1105     
1106     	if (!(var->vmode & FB_VMODE_YWRAP))
1107     		y_bottom += var->yres;
1108     
1109     	if (y_bottom > fb_display[con].var.yres_virtual)
1110     		return -EINVAL;
1111     
1112     	acornfb_update_dma(var);
1113     
1114     	fb_display[con].var.yoffset = var->yoffset;
1115     	if (var->vmode & FB_VMODE_YWRAP)
1116     		fb_display[con].var.vmode |= FB_VMODE_YWRAP;
1117     	else
1118     		fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
1119     
1120     	return 0;
1121     }
1122     
1123     /*
1124      * Note that we are entered with the kernel locked.
1125      */
1126     static int
1127     acornfb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
1128     {
1129     	unsigned long off, start;
1130     	u32 len;
1131     
1132     	off = vma->vm_pgoff << PAGE_SHIFT;
1133     
1134     	start = current_par.screen_base_p;
1135     	len = PAGE_ALIGN(start & ~PAGE_MASK) + current_par.screen_size;
1136     	start &= PAGE_MASK;
1137     	if ((vma->vm_end - vma->vm_start + off) > len)
1138     		return -EINVAL;
1139     	off += start;
1140     	vma->vm_pgoff = off >> PAGE_SHIFT;
1141     
1142     	/* This is an IO map - tell maydump to skip this VMA */
1143     	vma->vm_flags |= VM_IO;
1144     
1145     #ifdef CONFIG_CPU_32
1146     	pgprot_val(vma->vm_page_prot) &= ~L_PTE_CACHEABLE;
1147     #endif
1148     
1149     	/*
1150     	 * Don't alter the page protection flags; we want to keep the area
1151     	 * cached for better performance.  This does mean that we may miss
1152     	 * some updates to the screen occasionally, but process switches
1153     	 * should cause the caches and buffers to be flushed often enough.
1154     	 */
1155     	if (io_remap_page_range(vma->vm_start, off,
1156     				vma->vm_end - vma->vm_start,
1157     				vma->vm_page_prot))
1158     		return -EAGAIN;
1159     	return 0;
1160     }
1161     
1162     static struct fb_ops acornfb_ops = {
1163     	owner:		THIS_MODULE,
1164     	fb_get_fix:	acornfb_get_fix,
1165     	fb_get_var:	acornfb_get_var,
1166     	fb_set_var:	acornfb_set_var,
1167     	fb_get_cmap:	acornfb_get_cmap,
1168     	fb_set_cmap:	acornfb_set_cmap,
1169     	fb_pan_display:	acornfb_pan_display,
1170     	fb_mmap:	acornfb_mmap,
1171     };
1172     
1173     static int
1174     acornfb_updatevar(int con, struct fb_info *info)
1175     {
1176     	if (con == current_par.currcon)
1177     		acornfb_update_dma(&fb_display[con].var);
1178     
1179     	return 0;
1180     }
1181     
1182     static int
1183     acornfb_switch(int con, struct fb_info *info)
1184     {
1185     	struct fb_cmap *cmap;
1186     
1187     	if (current_par.currcon >= 0) {
1188     		cmap = &fb_display[current_par.currcon].cmap;
1189     
1190     		if (cmap->len)
1191     			fb_get_cmap(cmap, 1, acornfb_getcolreg, info);
1192     	}
1193     
1194     	current_par.currcon = con;
1195     
1196     	fb_display[con].var.activate = FB_ACTIVATE_NOW;
1197     
1198     	acornfb_set_var(&fb_display[con].var, con, info);
1199     
1200     	return 0;
1201     }
1202     
1203     static void
1204     acornfb_blank(int blank, struct fb_info *info)
1205     {
1206     	union palette p;
1207     	int i, bpp = fb_display[current_par.currcon].var.bits_per_pixel;
1208     
1209     #ifdef FBCON_HAS_CFB16
1210     	if (bpp == 16) {
1211     		p.p = 0;
1212     
1213     		for (i = 0; i < 256; i++) {
1214     			if (blank)
1215     				p = acornfb_palette_encode(i, 0, 0, 0, 0);
1216     			else {
1217     				p.vidc20.red   = current_par.palette[ i       & 31].vidc20.red;
1218     				p.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
1219     				p.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
1220     			}
1221     			acornfb_palette_write(i, current_par.palette[i]);
1222     		}
1223     	} else
1224     #endif
1225     	{
1226     		for (i = 0; i < current_par.palette_size; i++) {
1227     			if (blank)
1228     				p = acornfb_palette_encode(i, 0, 0, 0, 0);
1229     			else
1230     				p = current_par.palette[i];
1231     
1232     			acornfb_palette_write(i, p);
1233     		}
1234     	}
1235     }
1236     
1237     /*
1238      * Everything after here is initialisation!!!
1239      */
1240     static struct fb_videomode modedb[] __initdata = {
1241     	{	/* 320x256 @ 50Hz */
1242     		NULL, 50,  320,  256, 125000,  92,  62,  35, 19,  38, 2,
1243     		FB_SYNC_COMP_HIGH_ACT,
1244     		FB_VMODE_NONINTERLACED
1245     	}, {	/* 640x250 @ 50Hz, 15.6 kHz hsync */
1246     		NULL, 50,  640,  250,  62500, 185, 123,  38, 21,  76, 3,
1247     		0,
1248     		FB_VMODE_NONINTERLACED
1249     	}, {	/* 640x256 @ 50Hz, 15.6 kHz hsync */
1250     		NULL, 50,  640,  256,  62500, 185, 123,  35, 18,  76, 3,
1251     		0,
1252     		FB_VMODE_NONINTERLACED
1253     	}, {	/* 640x512 @ 50Hz, 26.8 kHz hsync */
1254     		NULL, 50,  640,  512,  41667, 113,  87,  18,  1,  56, 3,
1255     		0,
1256     		FB_VMODE_NONINTERLACED
1257     	}, {	/* 640x250 @ 70Hz, 31.5 kHz hsync */
1258     		NULL, 70,  640,  250,  39722,  48,  16, 109, 88,  96, 2,
1259     		0,
1260     		FB_VMODE_NONINTERLACED
1261     	}, {	/* 640x256 @ 70Hz, 31.5 kHz hsync */
1262     		NULL, 70,  640,  256,  39722,  48,  16, 106, 85,  96, 2,
1263     		0,
1264     		FB_VMODE_NONINTERLACED
1265     	}, {	/* 640x352 @ 70Hz, 31.5 kHz hsync */
1266     		NULL, 70,  640,  352,  39722,  48,  16,  58, 37,  96, 2,
1267     		0,
1268     		FB_VMODE_NONINTERLACED
1269     	}, {	/* 640x480 @ 60Hz, 31.5 kHz hsync */
1270     		NULL, 60,  640,  480,  39722,  48,  16,  32, 11,  96, 2,
1271     		0,
1272     		FB_VMODE_NONINTERLACED
1273     	}, {	/* 800x600 @ 56Hz, 35.2 kHz hsync */
1274     		NULL, 56,  800,  600,  27778, 101,  23,  22,  1, 100, 2,
1275     		0,
1276     		FB_VMODE_NONINTERLACED
1277     	}, {	/* 896x352 @ 60Hz, 21.8 kHz hsync */
1278     		NULL, 60,  896,  352,  41667,  59,  27,   9,  0, 118, 3,
1279     		0,
1280     		FB_VMODE_NONINTERLACED
1281     	}, {	/* 1024x 768 @ 60Hz, 48.4 kHz hsync */
1282     		NULL, 60, 1024,  768,  15385, 160,  24,  29,  3, 136, 6,
1283     		0,
1284     		FB_VMODE_NONINTERLACED
1285     	}, {	/* 1280x1024 @ 60Hz, 63.8 kHz hsync */
1286     		NULL, 60, 1280, 1024,   9090, 186,  96,  38,  1, 160, 3,
1287     		0,
1288     		FB_VMODE_NONINTERLACED
1289     	}
1290     };
1291     
1292     static struct fb_videomode __initdata
1293     acornfb_default_mode = {
1294     	name:		NULL,
1295     	refresh:	60,
1296     	xres:		640,
1297     	yres:		480,
1298     	pixclock:	39722,
1299     	left_margin:	56,
1300     	right_margin:	16,
1301     	upper_margin:	34,
1302     	lower_margin:	9,
1303     	hsync_len:	88,
1304     	vsync_len:	2,
1305     	sync:		0,
1306     	vmode:		FB_VMODE_NONINTERLACED
1307     };
1308     
1309     static void __init
1310     acornfb_init_fbinfo(void)
1311     {
1312     	static int first = 1;
1313     
1314     	if (!first)
1315     		return;
1316     	first = 0;
1317     
1318     	strcpy(fb_info.modename, "Acorn");
1319     	strcpy(fb_info.fontname, "Acorn8x8");
1320     
1321     	fb_info.node		   = -1;
1322     	fb_info.fbops		   = &acornfb_ops;
1323     	fb_info.disp		   = &global_disp;
1324     	fb_info.changevar	   = NULL;
1325     	fb_info.switch_con	   = acornfb_switch;
1326     	fb_info.updatevar	   = acornfb_updatevar;
1327     	fb_info.blank		   = acornfb_blank;
1328     	fb_info.flags		   = FBINFO_FLAG_DEFAULT;
1329     
1330     	global_disp.dispsw	   = &fbcon_dummy;
1331     
1332     	/*
1333     	 * setup initial parameters
1334     	 */
1335     	memset(&init_var, 0, sizeof(init_var));
1336     
1337     #if defined(HAS_VIDC20)
1338     	init_var.red.length	   = 8;
1339     	init_var.transp.length	   = 4;
1340     #elif defined(HAS_VIDC)
1341     	init_var.red.length	   = 4;
1342     	init_var.transp.length	   = 1;
1343     #endif
1344     	init_var.green		   = init_var.red;
1345     	init_var.blue		   = init_var.red;
1346     	init_var.nonstd		   = 0;
1347     	init_var.activate	   = FB_ACTIVATE_NOW;
1348     	init_var.height		   = -1;
1349     	init_var.width		   = -1;
1350     	init_var.vmode		   = FB_VMODE_NONINTERLACED;
1351     	init_var.accel_flags	   = FB_ACCELF_TEXT;
1352     
1353     	current_par.dram_size	   = 0;
1354     	current_par.montype	   = -1;
1355     	current_par.dpms	   = 0;
1356     }
1357     
1358     /*
1359      * setup acornfb options:
1360      *
1361      *  font:fontname
1362      *	Set fontname
1363      *
1364      *  mon:hmin-hmax:vmin-vmax:dpms:width:height
1365      *	Set monitor parameters:
1366      *		hmin   = horizontal minimum frequency (Hz)
1367      *		hmax   = horizontal maximum frequency (Hz)	(optional)
1368      *		vmin   = vertical minimum frequency (Hz)
1369      *		vmax   = vertical maximum frequency (Hz)	(optional)
1370      *		dpms   = DPMS supported?			(optional)
1371      *		width  = width of picture in mm.		(optional)
1372      *		height = height of picture in mm.		(optional)
1373      *
1374      * montype:type
1375      *	Set RISC-OS style monitor type:
1376      *		0 (or tv)	- TV frequency
1377      *		1 (or multi)	- Multi frequency
1378      *		2 (or hires)	- Hi-res monochrome
1379      *		3 (or vga)	- VGA
1380      *		4 (or svga)	- SVGA
1381      *		auto, or option missing
1382      *				- try hardware detect
1383      *
1384      * dram:size
1385      *	Set the amount of DRAM to use for the frame buffer
1386      *	(even if you have VRAM).
1387      *	size can optionally be followed by 'M' or 'K' for
1388      *	MB or KB respectively.
1389      */
1390     static void __init
1391     acornfb_parse_font(char *opt)
1392     {
1393     	strcpy(fb_info.fontname, opt);
1394     }
1395     
1396     static void __init
1397     acornfb_parse_mon(char *opt)
1398     {
1399     	char *p = opt;
1400     
1401     	current_par.montype = -2;
1402     
1403     	fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
1404     	if (*p == '-')
1405     		fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
1406     	else
1407     		fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
1408     
1409     	if (*p != ':')
1410     		goto bad;
1411     
1412     	fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
1413     	if (*p == '-')
1414     		fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
1415     	else
1416     		fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
1417     
1418     	if (*p != ':')
1419     		goto check_values;
1420     
1421     	fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
1422     
1423     	if (*p != ':')
1424     		goto check_values;
1425     
1426     	init_var.width = simple_strtoul(p + 1, &p, 0);
1427     
1428     	if (*p != ':')
1429     		goto check_values;
1430     
1431     	init_var.height = simple_strtoul(p + 1, NULL, 0);
1432     
1433     check_values:
1434     	if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
1435     	    fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
1436     		goto bad;
1437     	return;
1438     
1439     bad:
1440     	printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt);
1441     	current_par.montype = -1;
1442     }
1443     
1444     static void __init
1445     acornfb_parse_montype(char *opt)
1446     {
1447     	current_par.montype = -2;
1448     
1449     	if (strncmp(opt, "tv", 2) == 0) {
1450     		opt += 2;
1451     		current_par.montype = 0;
1452     	} else if (strncmp(opt, "multi", 5) == 0) {
1453     		opt += 5;
1454     		current_par.montype = 1;
1455     	} else if (strncmp(opt, "hires", 5) == 0) {
1456     		opt += 5;
1457     		current_par.montype = 2;
1458     	} else if (strncmp(opt, "vga", 3) == 0) {
1459     		opt += 3;
1460     		current_par.montype = 3;
1461     	} else if (strncmp(opt, "svga", 4) == 0) {
1462     		opt += 4;
1463     		current_par.montype = 4;
1464     	} else if (strncmp(opt, "auto", 4) == 0) {
1465     		opt += 4;
1466     		current_par.montype = -1;
1467     	} else if (isdigit(*opt))
1468     		current_par.montype = simple_strtoul(opt, &opt, 0);
1469     
1470     	if (current_par.montype == -2 ||
1471     	    current_par.montype > NR_MONTYPES) {
1472     		printk(KERN_ERR "acornfb: unknown monitor type: %s\n",
1473     			opt);
1474     		current_par.montype = -1;
1475     	} else
1476     	if (opt && *opt) {
1477     		if (strcmp(opt, ",dpms") == 0)
1478     			current_par.dpms = 1;
1479     		else
1480     			printk(KERN_ERR
1481     			       "acornfb: unknown monitor option: %s\n",
1482     			       opt);
1483     	}
1484     }
1485     
1486     static void __init
1487     acornfb_parse_dram(char *opt)
1488     {
1489     	unsigned int size;
1490     
1491     	size = simple_strtoul(opt, &opt, 0);
1492     
1493     	if (opt) {
1494     		switch (*opt) {
1495     		case 'M':
1496     		case 'm':
1497     			size *= 1024;
1498     		case 'K':
1499     		case 'k':
1500     			size *= 1024;
1501     		default:
1502     			break;
1503     		}
1504     	}
1505     
1506     	current_par.dram_size = size;
1507     }
1508     
1509     static struct options {
1510     	char *name;
1511     	void (*parse)(char *opt);
1512     } opt_table[] __initdata = {
1513     	{ "font",    acornfb_parse_font    },
1514     	{ "mon",     acornfb_parse_mon     },
1515     	{ "montype", acornfb_parse_montype },
1516     	{ "dram",    acornfb_parse_dram    },
1517     	{ NULL, NULL }
1518     };
1519     
1520     int __init
1521     acornfb_setup(char *options)
1522     {
1523     	struct options *optp;
1524     	char *opt;
1525     
1526     	if (!options || !*options)
1527     		return 0;
1528     
1529     	acornfb_init_fbinfo();
1530     
1531     	for (opt = strtok(options, ","); opt; opt = strtok(NULL, ",")) {
1532     		if (!*opt)
1533     			continue;
1534     
1535     		for (optp = opt_table; optp->name; optp++) {
1536     			int optlen;
1537     
1538     			optlen = strlen(optp->name);
1539     
1540     			if (strncmp(opt, optp->name, optlen) == 0 &&
1541     			    opt[optlen] == ':') {
1542     				optp->parse(opt + optlen + 1);
1543     				break;
1544     			}
1545     		}
1546     
1547     		if (!optp->name)
1548     			printk(KERN_ERR "acornfb: unknown parameter: %s\n",
1549     			       opt);
1550     	}
1551     	return 0;
1552     }
1553     
1554     /*
1555      * Detect type of monitor connected
1556      *  For now, we just assume SVGA
1557      */
1558     static int __init
1559     acornfb_detect_monitortype(void)
1560     {
1561     	return 4;
1562     }
1563     
1564     /*
1565      * This enables the unused memory to be freed on older Acorn machines.
1566      */
1567     static inline void
1568     free_unused_pages(unsigned int virtual_start, unsigned int virtual_end)
1569     {
1570     	int mb_freed = 0;
1571     
1572     	/*
1573     	 * Align addresses
1574     	 */
1575     	virtual_start = PAGE_ALIGN(virtual_start);
1576     	virtual_end = PAGE_ALIGN(virtual_end);
1577     
1578     	while (virtual_start < virtual_end) {
1579     		struct page *page;
1580     
1581     		/*
1582     		 * Clear page reserved bit,
1583     		 * set count to 1, and free
1584     		 * the page.
1585     		 */
1586     		page = virt_to_page(virtual_start);
1587     		ClearPageReserved(page);
1588     		atomic_set(&page->count, 1);
1589     		free_page(virtual_start);
1590     
1591     		virtual_start += PAGE_SIZE;
1592     		mb_freed += PAGE_SIZE / 1024;
1593     	}
1594     
1595     	printk("acornfb: freed %dK memory\n", mb_freed);
1596     }
1597     
1598     int __init
1599     acornfb_init(void)
1600     {
1601     	unsigned long size;
1602     	u_int h_sync, v_sync;
1603     	int rc, i;
1604     
1605     	acornfb_init_fbinfo();
1606     
1607     	if (current_par.montype == -1)
1608     		current_par.montype = acornfb_detect_monitortype();
1609     
1610     	if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
1611     		current_par.montype = 4;
1612     
1613     	if (current_par.montype >= 0) {
1614     		fb_info.monspecs = monspecs[current_par.montype];
1615     		fb_info.monspecs.dpms = current_par.dpms;
1616     	}
1617     
1618     	/*
1619     	 * Try to select a suitable default mode
1620     	 */
1621     	for (i = 0; i < sizeof(modedb) / sizeof(*modedb); i++) {
1622     		unsigned long hs;
1623     
1624     		hs = modedb[i].refresh *
1625     		     (modedb[i].yres + modedb[i].upper_margin +
1626     		      modedb[i].lower_margin + modedb[i].vsync_len);
1627     		if (modedb[i].xres == DEFAULT_XRES &&
1628     		    modedb[i].yres == DEFAULT_YRES &&
1629     		    modedb[i].refresh >= fb_info.monspecs.vfmin &&
1630     		    modedb[i].refresh <= fb_info.monspecs.vfmax &&
1631     		    hs                >= fb_info.monspecs.hfmin &&
1632     		    hs                <= fb_info.monspecs.hfmax) {
1633     			acornfb_default_mode = modedb[i];
1634     			break;
1635     		}
1636     	}
1637     
1638     	current_par.currcon	   = -1;
1639     	current_par.screen_base	   = SCREEN_BASE;
1640     	current_par.screen_base_p  = SCREEN_START;
1641     	current_par.using_vram     = 0;
1642     
1643     	/*
1644     	 * If vram_size is set, we are using VRAM in
1645     	 * a Risc PC.  However, if the user has specified
1646     	 * an amount of DRAM then use that instead.
1647     	 */
1648     	if (vram_size && !current_par.dram_size) {
1649     		size = vram_size;
1650     		current_par.vram_half_sam = vram_size / 1024;
1651     		current_par.using_vram = 1;
1652     	} else if (current_par.dram_size)
1653     		size = current_par.dram_size;
1654     	else
1655     		size = MAX_SIZE;
1656     
1657     	/*
1658     	 * Limit maximum screen size.
1659     	 */
1660     	if (size > MAX_SIZE)
1661     		size = MAX_SIZE;
1662     
1663     	size = PAGE_ALIGN(size);
1664     
1665     #if defined(HAS_VIDC20)
1666     	if (!current_par.using_vram) {
1667     		/*
1668     		 * RiscPC needs to allocate the DRAM memory
1669     		 * for the framebuffer if we are not using
1670     		 * VRAM.  Archimedes/A5000 machines use a
1671     		 * fixed address for their framebuffers.
1672     		 */
1673     		int order = 0;
1674     		unsigned long page, top;
1675     		while (size > (PAGE_SIZE * (1 << order)))
1676     			order++;
1677     		current_par.screen_base = __get_free_pages(GFP_KERNEL, order);
1678     		if (current_par.screen_base == 0) {
1679     			printk(KERN_ERR "acornfb: unable to allocate screen "
1680     			       "memory\n");
1681     			return -ENOMEM;
1682     		}
1683     		top = current_par.screen_base + (PAGE_SIZE * (1 << order));
1684     		/* Mark the framebuffer pages as reserved so mmap will work. */
1685     		for (page = current_par.screen_base; 
1686     		     page < PAGE_ALIGN(current_par.screen_base + size);
1687     		     page += PAGE_SIZE)
1688     			SetPageReserved(virt_to_page(page));
1689     		/* Hand back any excess pages that we allocated. */
1690     		for (page = current_par.screen_base + size; page < top; page += PAGE_SIZE)
1691     			free_page(page);
1692     		current_par.screen_base_p =
1693     			virt_to_phys((void *)current_par.screen_base);
1694     	}
1695     #endif
1696     #if defined(HAS_VIDC)
1697     	/*
1698     	 * Free unused pages
1699     	 */
1700     	free_unused_pages(PAGE_OFFSET + size, PAGE_OFFSET + MAX_SIZE);
1701     #endif
1702     	
1703     	current_par.screen_size	   = size;
1704     	current_par.palette_size   = VIDC_PALETTE_SIZE;
1705     
1706     	/*
1707     	 * Lookup the timing for this resolution.  If we can't
1708     	 * find it, then we can't restore it if we change
1709     	 * the resolution, so we disable this feature.
1710     	 */
1711     	do {
1712     		rc = fb_find_mode(&init_var, &fb_info, NULL, modedb,
1713     				 sizeof(modedb) / sizeof(*modedb),
1714     				 &acornfb_default_mode, DEFAULT_BPP);
1715     		/*
1716     		 * If we found an exact match, all ok.
1717     		 */
1718     		if (rc == 1)
1719     			break;
1720     
1721     		rc = fb_find_mode(&init_var, &fb_info, NULL, NULL, 0,
1722     				  &acornfb_default_mode, DEFAULT_BPP);
1723     		/*
1724     		 * If we found an exact match, all ok.
1725     		 */
1726     		if (rc == 1)
1727     			break;
1728     
1729     		rc = fb_find_mode(&init_var, &fb_info, NULL, modedb,
1730     				 sizeof(modedb) / sizeof(*modedb),
1731     				 &acornfb_default_mode, DEFAULT_BPP);
1732     		if (rc)
1733     			break;
1734     
1735     		rc = fb_find_mode(&init_var, &fb_info, NULL, NULL, 0,
1736     				  &acornfb_default_mode, DEFAULT_BPP);
1737     	} while (0);
1738     
1739     	/*
1740     	 * If we didn't find an exact match, try the
1741     	 * generic database.
1742     	 */
1743     	if (rc == 0) {
1744     		printk("Acornfb: no valid mode found\n");
1745     		return -EINVAL;
1746     	}
1747     
1748     	h_sync = 1953125000 / init_var.pixclock;
1749     	h_sync = h_sync * 512 / (init_var.xres + init_var.left_margin +
1750     		 init_var.right_margin + init_var.hsync_len);
1751     	v_sync = h_sync / (init_var.yres + init_var.upper_margin +
1752     		 init_var.lower_margin + init_var.vsync_len);
1753     
1754     	printk(KERN_INFO "Acornfb: %ldkB %cRAM, %s, using %dx%d, "
1755     		"%d.%03dkHz, %dHz\n",
1756     		current_par.screen_size / 1024,
1757     		current_par.using_vram ? 'V' : 'D',
1758     		VIDC_NAME, init_var.xres, init_var.yres,
1759     		h_sync / 1000, h_sync % 1000, v_sync);
1760     
1761     	printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
1762     		fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
1763     		fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
1764     		fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
1765     		fb_info.monspecs.dpms ? ", DPMS" : "");
1766     
1767     	if (acornfb_set_var(&init_var, -1, &fb_info))
1768     		printk(KERN_ERR "Acornfb: unable to set display parameters\n");
1769     
1770     	if (register_framebuffer(&fb_info) < 0)
1771     		return -EINVAL;
1772     	return 0;
1773     }
1774     
1775     MODULE_LICENSE("GPL");
1776