clean
[linux-2.4.21-pre4.git] / 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 #ifdef CONFIG_CPU_32
1143         pgprot_val(vma->vm_page_prot) &= ~L_PTE_CACHEABLE;
1144 #endif
1145
1146         /*
1147          * Don't alter the page protection flags; we want to keep the area
1148          * cached for better performance.  This does mean that we may miss
1149          * some updates to the screen occasionally, but process switches
1150          * should cause the caches and buffers to be flushed often enough.
1151          */
1152         if (io_remap_page_range(vma->vm_start, off,
1153                                 vma->vm_end - vma->vm_start,
1154                                 vma->vm_page_prot))
1155                 return -EAGAIN;
1156         return 0;
1157 }
1158
1159 static struct fb_ops acornfb_ops = {
1160         owner:          THIS_MODULE,
1161         fb_get_fix:     acornfb_get_fix,
1162         fb_get_var:     acornfb_get_var,
1163         fb_set_var:     acornfb_set_var,
1164         fb_get_cmap:    acornfb_get_cmap,
1165         fb_set_cmap:    acornfb_set_cmap,
1166         fb_pan_display: acornfb_pan_display,
1167         fb_mmap:        acornfb_mmap,
1168 };
1169
1170 static int
1171 acornfb_updatevar(int con, struct fb_info *info)
1172 {
1173         if (con == current_par.currcon)
1174                 acornfb_update_dma(&fb_display[con].var);
1175
1176         return 0;
1177 }
1178
1179 static int
1180 acornfb_switch(int con, struct fb_info *info)
1181 {
1182         struct fb_cmap *cmap;
1183
1184         if (current_par.currcon >= 0) {
1185                 cmap = &fb_display[current_par.currcon].cmap;
1186
1187                 if (cmap->len)
1188                         fb_get_cmap(cmap, 1, acornfb_getcolreg, info);
1189         }
1190
1191         current_par.currcon = con;
1192
1193         fb_display[con].var.activate = FB_ACTIVATE_NOW;
1194
1195         acornfb_set_var(&fb_display[con].var, con, info);
1196
1197         return 0;
1198 }
1199
1200 static void
1201 acornfb_blank(int blank, struct fb_info *info)
1202 {
1203         union palette p;
1204         int i, bpp = fb_display[current_par.currcon].var.bits_per_pixel;
1205
1206 #ifdef FBCON_HAS_CFB16
1207         if (bpp == 16) {
1208                 p.p = 0;
1209
1210                 for (i = 0; i < 256; i++) {
1211                         if (blank)
1212                                 p = acornfb_palette_encode(i, 0, 0, 0, 0);
1213                         else {
1214                                 p.vidc20.red   = current_par.palette[ i       & 31].vidc20.red;
1215                                 p.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
1216                                 p.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
1217                         }
1218                         acornfb_palette_write(i, current_par.palette[i]);
1219                 }
1220         } else
1221 #endif
1222         {
1223                 for (i = 0; i < current_par.palette_size; i++) {
1224                         if (blank)
1225                                 p = acornfb_palette_encode(i, 0, 0, 0, 0);
1226                         else
1227                                 p = current_par.palette[i];
1228
1229                         acornfb_palette_write(i, p);
1230                 }
1231         }
1232 }
1233
1234 /*
1235  * Everything after here is initialisation!!!
1236  */
1237 static struct fb_videomode modedb[] __initdata = {
1238         {       /* 320x256 @ 50Hz */
1239                 NULL, 50,  320,  256, 125000,  92,  62,  35, 19,  38, 2,
1240                 FB_SYNC_COMP_HIGH_ACT,
1241                 FB_VMODE_NONINTERLACED
1242         }, {    /* 640x250 @ 50Hz, 15.6 kHz hsync */
1243                 NULL, 50,  640,  250,  62500, 185, 123,  38, 21,  76, 3,
1244                 0,
1245                 FB_VMODE_NONINTERLACED
1246         }, {    /* 640x256 @ 50Hz, 15.6 kHz hsync */
1247                 NULL, 50,  640,  256,  62500, 185, 123,  35, 18,  76, 3,
1248                 0,
1249                 FB_VMODE_NONINTERLACED
1250         }, {    /* 640x512 @ 50Hz, 26.8 kHz hsync */
1251                 NULL, 50,  640,  512,  41667, 113,  87,  18,  1,  56, 3,
1252                 0,
1253                 FB_VMODE_NONINTERLACED
1254         }, {    /* 640x250 @ 70Hz, 31.5 kHz hsync */
1255                 NULL, 70,  640,  250,  39722,  48,  16, 109, 88,  96, 2,
1256                 0,
1257                 FB_VMODE_NONINTERLACED
1258         }, {    /* 640x256 @ 70Hz, 31.5 kHz hsync */
1259                 NULL, 70,  640,  256,  39722,  48,  16, 106, 85,  96, 2,
1260                 0,
1261                 FB_VMODE_NONINTERLACED
1262         }, {    /* 640x352 @ 70Hz, 31.5 kHz hsync */
1263                 NULL, 70,  640,  352,  39722,  48,  16,  58, 37,  96, 2,
1264                 0,
1265                 FB_VMODE_NONINTERLACED
1266         }, {    /* 640x480 @ 60Hz, 31.5 kHz hsync */
1267                 NULL, 60,  640,  480,  39722,  48,  16,  32, 11,  96, 2,
1268                 0,
1269                 FB_VMODE_NONINTERLACED
1270         }, {    /* 800x600 @ 56Hz, 35.2 kHz hsync */
1271                 NULL, 56,  800,  600,  27778, 101,  23,  22,  1, 100, 2,
1272                 0,
1273                 FB_VMODE_NONINTERLACED
1274         }, {    /* 896x352 @ 60Hz, 21.8 kHz hsync */
1275                 NULL, 60,  896,  352,  41667,  59,  27,   9,  0, 118, 3,
1276                 0,
1277                 FB_VMODE_NONINTERLACED
1278         }, {    /* 1024x 768 @ 60Hz, 48.4 kHz hsync */
1279                 NULL, 60, 1024,  768,  15385, 160,  24,  29,  3, 136, 6,
1280                 0,
1281                 FB_VMODE_NONINTERLACED
1282         }, {    /* 1280x1024 @ 60Hz, 63.8 kHz hsync */
1283                 NULL, 60, 1280, 1024,   9090, 186,  96,  38,  1, 160, 3,
1284                 0,
1285                 FB_VMODE_NONINTERLACED
1286         }
1287 };
1288
1289 static struct fb_videomode __initdata
1290 acornfb_default_mode = {
1291         name:           NULL,
1292         refresh:        60,
1293         xres:           640,
1294         yres:           480,
1295         pixclock:       39722,
1296         left_margin:    56,
1297         right_margin:   16,
1298         upper_margin:   34,
1299         lower_margin:   9,
1300         hsync_len:      88,
1301         vsync_len:      2,
1302         sync:           0,
1303         vmode:          FB_VMODE_NONINTERLACED
1304 };
1305
1306 static void __init
1307 acornfb_init_fbinfo(void)
1308 {
1309         static int first = 1;
1310
1311         if (!first)
1312                 return;
1313         first = 0;
1314
1315         strcpy(fb_info.modename, "Acorn");
1316         strcpy(fb_info.fontname, "Acorn8x8");
1317
1318         fb_info.node               = -1;
1319         fb_info.fbops              = &acornfb_ops;
1320         fb_info.disp               = &global_disp;
1321         fb_info.changevar          = NULL;
1322         fb_info.switch_con         = acornfb_switch;
1323         fb_info.updatevar          = acornfb_updatevar;
1324         fb_info.blank              = acornfb_blank;
1325         fb_info.flags              = FBINFO_FLAG_DEFAULT;
1326
1327         global_disp.dispsw         = &fbcon_dummy;
1328
1329         /*
1330          * setup initial parameters
1331          */
1332         memset(&init_var, 0, sizeof(init_var));
1333
1334 #if defined(HAS_VIDC20)
1335         init_var.red.length        = 8;
1336         init_var.transp.length     = 4;
1337 #elif defined(HAS_VIDC)
1338         init_var.red.length        = 4;
1339         init_var.transp.length     = 1;
1340 #endif
1341         init_var.green             = init_var.red;
1342         init_var.blue              = init_var.red;
1343         init_var.nonstd            = 0;
1344         init_var.activate          = FB_ACTIVATE_NOW;
1345         init_var.height            = -1;
1346         init_var.width             = -1;
1347         init_var.vmode             = FB_VMODE_NONINTERLACED;
1348         init_var.accel_flags       = FB_ACCELF_TEXT;
1349
1350         current_par.dram_size      = 0;
1351         current_par.montype        = -1;
1352         current_par.dpms           = 0;
1353 }
1354
1355 /*
1356  * setup acornfb options:
1357  *
1358  *  font:fontname
1359  *      Set fontname
1360  *
1361  *  mon:hmin-hmax:vmin-vmax:dpms:width:height
1362  *      Set monitor parameters:
1363  *              hmin   = horizontal minimum frequency (Hz)
1364  *              hmax   = horizontal maximum frequency (Hz)      (optional)
1365  *              vmin   = vertical minimum frequency (Hz)
1366  *              vmax   = vertical maximum frequency (Hz)        (optional)
1367  *              dpms   = DPMS supported?                        (optional)
1368  *              width  = width of picture in mm.                (optional)
1369  *              height = height of picture in mm.               (optional)
1370  *
1371  * montype:type
1372  *      Set RISC-OS style monitor type:
1373  *              0 (or tv)       - TV frequency
1374  *              1 (or multi)    - Multi frequency
1375  *              2 (or hires)    - Hi-res monochrome
1376  *              3 (or vga)      - VGA
1377  *              4 (or svga)     - SVGA
1378  *              auto, or option missing
1379  *                              - try hardware detect
1380  *
1381  * dram:size
1382  *      Set the amount of DRAM to use for the frame buffer
1383  *      (even if you have VRAM).
1384  *      size can optionally be followed by 'M' or 'K' for
1385  *      MB or KB respectively.
1386  */
1387 static void __init
1388 acornfb_parse_font(char *opt)
1389 {
1390         strcpy(fb_info.fontname, opt);
1391 }
1392
1393 static void __init
1394 acornfb_parse_mon(char *opt)
1395 {
1396         char *p = opt;
1397
1398         current_par.montype = -2;
1399
1400         fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
1401         if (*p == '-')
1402                 fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
1403         else
1404                 fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
1405
1406         if (*p != ':')
1407                 goto bad;
1408
1409         fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
1410         if (*p == '-')
1411                 fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
1412         else
1413                 fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
1414
1415         if (*p != ':')
1416                 goto check_values;
1417
1418         fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
1419
1420         if (*p != ':')
1421                 goto check_values;
1422
1423         init_var.width = simple_strtoul(p + 1, &p, 0);
1424
1425         if (*p != ':')
1426                 goto check_values;
1427
1428         init_var.height = simple_strtoul(p + 1, NULL, 0);
1429
1430 check_values:
1431         if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
1432             fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
1433                 goto bad;
1434         return;
1435
1436 bad:
1437         printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt);
1438         current_par.montype = -1;
1439 }
1440
1441 static void __init
1442 acornfb_parse_montype(char *opt)
1443 {
1444         current_par.montype = -2;
1445
1446         if (strncmp(opt, "tv", 2) == 0) {
1447                 opt += 2;
1448                 current_par.montype = 0;
1449         } else if (strncmp(opt, "multi", 5) == 0) {
1450                 opt += 5;
1451                 current_par.montype = 1;
1452         } else if (strncmp(opt, "hires", 5) == 0) {
1453                 opt += 5;
1454                 current_par.montype = 2;
1455         } else if (strncmp(opt, "vga", 3) == 0) {
1456                 opt += 3;
1457                 current_par.montype = 3;
1458         } else if (strncmp(opt, "svga", 4) == 0) {
1459                 opt += 4;
1460                 current_par.montype = 4;
1461         } else if (strncmp(opt, "auto", 4) == 0) {
1462                 opt += 4;
1463                 current_par.montype = -1;
1464         } else if (isdigit(*opt))
1465                 current_par.montype = simple_strtoul(opt, &opt, 0);
1466
1467         if (current_par.montype == -2 ||
1468             current_par.montype > NR_MONTYPES) {
1469                 printk(KERN_ERR "acornfb: unknown monitor type: %s\n",
1470                         opt);
1471                 current_par.montype = -1;
1472         } else
1473         if (opt && *opt) {
1474                 if (strcmp(opt, ",dpms") == 0)
1475                         current_par.dpms = 1;
1476                 else
1477                         printk(KERN_ERR
1478                                "acornfb: unknown monitor option: %s\n",
1479                                opt);
1480         }
1481 }
1482
1483 static void __init
1484 acornfb_parse_dram(char *opt)
1485 {
1486         unsigned int size;
1487
1488         size = simple_strtoul(opt, &opt, 0);
1489
1490         if (opt) {
1491                 switch (*opt) {
1492                 case 'M':
1493                 case 'm':
1494                         size *= 1024;
1495                 case 'K':
1496                 case 'k':
1497                         size *= 1024;
1498                 default:
1499                         break;
1500                 }
1501         }
1502
1503         current_par.dram_size = size;
1504 }
1505
1506 static struct options {
1507         char *name;
1508         void (*parse)(char *opt);
1509 } opt_table[] __initdata = {
1510         { "font",    acornfb_parse_font    },
1511         { "mon",     acornfb_parse_mon     },
1512         { "montype", acornfb_parse_montype },
1513         { "dram",    acornfb_parse_dram    },
1514         { NULL, NULL }
1515 };
1516
1517 int __init
1518 acornfb_setup(char *options)
1519 {
1520         struct options *optp;
1521         char *opt;
1522
1523         if (!options || !*options)
1524                 return 0;
1525
1526         acornfb_init_fbinfo();
1527
1528         while ((opt = strsep(&options, ",")) != NULL) {
1529                 if (!*opt)
1530                         continue;
1531
1532                 for (optp = opt_table; optp->name; optp++) {
1533                         int optlen;
1534
1535                         optlen = strlen(optp->name);
1536
1537                         if (strncmp(opt, optp->name, optlen) == 0 &&
1538                             opt[optlen] == ':') {
1539                                 optp->parse(opt + optlen + 1);
1540                                 break;
1541                         }
1542                 }
1543
1544                 if (!optp->name)
1545                         printk(KERN_ERR "acornfb: unknown parameter: %s\n",
1546                                opt);
1547         }
1548         return 0;
1549 }
1550
1551 /*
1552  * Detect type of monitor connected
1553  *  For now, we just assume SVGA
1554  */
1555 static int __init
1556 acornfb_detect_monitortype(void)
1557 {
1558         return 4;
1559 }
1560
1561 /*
1562  * This enables the unused memory to be freed on older Acorn machines.
1563  */
1564 static inline void
1565 free_unused_pages(unsigned int virtual_start, unsigned int virtual_end)
1566 {
1567         int mb_freed = 0;
1568
1569         /*
1570          * Align addresses
1571          */
1572         virtual_start = PAGE_ALIGN(virtual_start);
1573         virtual_end = PAGE_ALIGN(virtual_end);
1574
1575         while (virtual_start < virtual_end) {
1576                 struct page *page;
1577
1578                 /*
1579                  * Clear page reserved bit,
1580                  * set count to 1, and free
1581                  * the page.
1582                  */
1583                 page = virt_to_page(virtual_start);
1584                 ClearPageReserved(page);
1585                 atomic_set(&page->count, 1);
1586                 free_page(virtual_start);
1587
1588                 virtual_start += PAGE_SIZE;
1589                 mb_freed += PAGE_SIZE / 1024;
1590         }
1591
1592         printk("acornfb: freed %dK memory\n", mb_freed);
1593 }
1594
1595 int __init
1596 acornfb_init(void)
1597 {
1598         unsigned long size;
1599         u_int h_sync, v_sync;
1600         int rc, i;
1601
1602         acornfb_init_fbinfo();
1603
1604         if (current_par.montype == -1)
1605                 current_par.montype = acornfb_detect_monitortype();
1606
1607         if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
1608                 current_par.montype = 4;
1609
1610         if (current_par.montype >= 0) {
1611                 fb_info.monspecs = monspecs[current_par.montype];
1612                 fb_info.monspecs.dpms = current_par.dpms;
1613         }
1614
1615         /*
1616          * Try to select a suitable default mode
1617          */
1618         for (i = 0; i < sizeof(modedb) / sizeof(*modedb); i++) {
1619                 unsigned long hs;
1620
1621                 hs = modedb[i].refresh *
1622                      (modedb[i].yres + modedb[i].upper_margin +
1623                       modedb[i].lower_margin + modedb[i].vsync_len);
1624                 if (modedb[i].xres == DEFAULT_XRES &&
1625                     modedb[i].yres == DEFAULT_YRES &&
1626                     modedb[i].refresh >= fb_info.monspecs.vfmin &&
1627                     modedb[i].refresh <= fb_info.monspecs.vfmax &&
1628                     hs                >= fb_info.monspecs.hfmin &&
1629                     hs                <= fb_info.monspecs.hfmax) {
1630                         acornfb_default_mode = modedb[i];
1631                         break;
1632                 }
1633         }
1634
1635         current_par.currcon        = -1;
1636         current_par.screen_base    = SCREEN_BASE;
1637         current_par.screen_base_p  = SCREEN_START;
1638         current_par.using_vram     = 0;
1639
1640         /*
1641          * If vram_size is set, we are using VRAM in
1642          * a Risc PC.  However, if the user has specified
1643          * an amount of DRAM then use that instead.
1644          */
1645         if (vram_size && !current_par.dram_size) {
1646                 size = vram_size;
1647                 current_par.vram_half_sam = vram_size / 1024;
1648                 current_par.using_vram = 1;
1649         } else if (current_par.dram_size)
1650                 size = current_par.dram_size;
1651         else
1652                 size = MAX_SIZE;
1653
1654         /*
1655          * Limit maximum screen size.
1656          */
1657         if (size > MAX_SIZE)
1658                 size = MAX_SIZE;
1659
1660         size = PAGE_ALIGN(size);
1661
1662 #if defined(HAS_VIDC20)
1663         if (!current_par.using_vram) {
1664                 /*
1665                  * RiscPC needs to allocate the DRAM memory
1666                  * for the framebuffer if we are not using
1667                  * VRAM.  Archimedes/A5000 machines use a
1668                  * fixed address for their framebuffers.
1669                  */
1670                 int order = 0;
1671                 unsigned long page, top;
1672                 while (size > (PAGE_SIZE * (1 << order)))
1673                         order++;
1674                 current_par.screen_base = __get_free_pages(GFP_KERNEL, order);
1675                 if (current_par.screen_base == 0) {
1676                         printk(KERN_ERR "acornfb: unable to allocate screen "
1677                                "memory\n");
1678                         return -ENOMEM;
1679                 }
1680                 top = current_par.screen_base + (PAGE_SIZE * (1 << order));
1681                 /* Mark the framebuffer pages as reserved so mmap will work. */
1682                 for (page = current_par.screen_base; 
1683                      page < PAGE_ALIGN(current_par.screen_base + size);
1684                      page += PAGE_SIZE)
1685                         SetPageReserved(virt_to_page(page));
1686                 /* Hand back any excess pages that we allocated. */
1687                 for (page = current_par.screen_base + size; page < top; page += PAGE_SIZE)
1688                         free_page(page);
1689                 current_par.screen_base_p =
1690                         virt_to_phys((void *)current_par.screen_base);
1691         }
1692 #endif
1693 #if defined(HAS_VIDC)
1694         /*
1695          * Free unused pages
1696          */
1697         free_unused_pages(PAGE_OFFSET + size, PAGE_OFFSET + MAX_SIZE);
1698 #endif
1699         
1700         current_par.screen_size    = size;
1701         current_par.palette_size   = VIDC_PALETTE_SIZE;
1702
1703         /*
1704          * Lookup the timing for this resolution.  If we can't
1705          * find it, then we can't restore it if we change
1706          * the resolution, so we disable this feature.
1707          */
1708         do {
1709                 rc = fb_find_mode(&init_var, &fb_info, NULL, modedb,
1710                                  sizeof(modedb) / sizeof(*modedb),
1711                                  &acornfb_default_mode, DEFAULT_BPP);
1712                 /*
1713                  * If we found an exact match, all ok.
1714                  */
1715                 if (rc == 1)
1716                         break;
1717
1718                 rc = fb_find_mode(&init_var, &fb_info, NULL, NULL, 0,
1719                                   &acornfb_default_mode, DEFAULT_BPP);
1720                 /*
1721                  * If we found an exact match, all ok.
1722                  */
1723                 if (rc == 1)
1724                         break;
1725
1726                 rc = fb_find_mode(&init_var, &fb_info, NULL, modedb,
1727                                  sizeof(modedb) / sizeof(*modedb),
1728                                  &acornfb_default_mode, DEFAULT_BPP);
1729                 if (rc)
1730                         break;
1731
1732                 rc = fb_find_mode(&init_var, &fb_info, NULL, NULL, 0,
1733                                   &acornfb_default_mode, DEFAULT_BPP);
1734         } while (0);
1735
1736         /*
1737          * If we didn't find an exact match, try the
1738          * generic database.
1739          */
1740         if (rc == 0) {
1741                 printk("Acornfb: no valid mode found\n");
1742                 return -EINVAL;
1743         }
1744
1745         h_sync = 1953125000 / init_var.pixclock;
1746         h_sync = h_sync * 512 / (init_var.xres + init_var.left_margin +
1747                  init_var.right_margin + init_var.hsync_len);
1748         v_sync = h_sync / (init_var.yres + init_var.upper_margin +
1749                  init_var.lower_margin + init_var.vsync_len);
1750
1751         printk(KERN_INFO "Acornfb: %ldkB %cRAM, %s, using %dx%d, "
1752                 "%d.%03dkHz, %dHz\n",
1753                 current_par.screen_size / 1024,
1754                 current_par.using_vram ? 'V' : 'D',
1755                 VIDC_NAME, init_var.xres, init_var.yres,
1756                 h_sync / 1000, h_sync % 1000, v_sync);
1757
1758         printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
1759                 fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
1760                 fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
1761                 fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
1762                 fb_info.monspecs.dpms ? ", DPMS" : "");
1763
1764         if (acornfb_set_var(&init_var, -1, &fb_info))
1765                 printk(KERN_ERR "Acornfb: unable to set display parameters\n");
1766
1767         if (register_framebuffer(&fb_info) < 0)
1768                 return -EINVAL;
1769         return 0;
1770 }
1771
1772 MODULE_AUTHOR("Russell King");
1773 MODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver");
1774 MODULE_LICENSE("GPL");
1775 EXPORT_NO_SYMBOLS;