Merge tag 'docs-4.12-2' of git://git.lwn.net/linux
[sfrench/cifs-2.6.git] / drivers / video / fbdev / 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  * FIXME: (things broken by the "new improved" FBCON API)
17  *  - Blanking 8bpp displays with VIDC
18  */
19
20 #include <linux/module.h>
21 #include <linux/kernel.h>
22 #include <linux/errno.h>
23 #include <linux/string.h>
24 #include <linux/ctype.h>
25 #include <linux/mm.h>
26 #include <linux/init.h>
27 #include <linux/fb.h>
28 #include <linux/platform_device.h>
29 #include <linux/dma-mapping.h>
30 #include <linux/io.h>
31 #include <linux/gfp.h>
32
33 #include <mach/hardware.h>
34 #include <asm/irq.h>
35 #include <asm/mach-types.h>
36 #include <asm/pgtable.h>
37
38 #include "acornfb.h"
39
40 /*
41  * Default resolution.
42  * NOTE that it has to be supported in the table towards
43  * the end of this file.
44  */
45 #define DEFAULT_XRES    640
46 #define DEFAULT_YRES    480
47 #define DEFAULT_BPP     4
48
49 /*
50  * define this to debug the video mode selection
51  */
52 #undef DEBUG_MODE_SELECTION
53
54 /*
55  * Translation from RISC OS monitor types to actual
56  * HSYNC and VSYNC frequency ranges.  These are
57  * probably not right, but they're the best info I
58  * have.  Allow 1% either way on the nominal for TVs.
59  */
60 #define NR_MONTYPES     6
61 static struct fb_monspecs monspecs[NR_MONTYPES] = {
62         {       /* TV           */
63                 .hfmin  = 15469,
64                 .hfmax  = 15781,
65                 .vfmin  = 49,
66                 .vfmax  = 51,
67         }, {    /* Multi Freq   */
68                 .hfmin  = 0,
69                 .hfmax  = 99999,
70                 .vfmin  = 0,
71                 .vfmax  = 199,
72         }, {    /* Hi-res mono  */
73                 .hfmin  = 58608,
74                 .hfmax  = 58608,
75                 .vfmin  = 64,
76                 .vfmax  = 64,
77         }, {    /* VGA          */
78                 .hfmin  = 30000,
79                 .hfmax  = 70000,
80                 .vfmin  = 60,
81                 .vfmax  = 60,
82         }, {    /* SVGA         */
83                 .hfmin  = 30000,
84                 .hfmax  = 70000,
85                 .vfmin  = 56,
86                 .vfmax  = 75,
87         }, {
88                 .hfmin  = 30000,
89                 .hfmax  = 70000,
90                 .vfmin  = 60,
91                 .vfmax  = 60,
92         }
93 };
94
95 static struct fb_info fb_info;
96 static struct acornfb_par current_par;
97 static struct vidc_timing current_vidc;
98
99 extern unsigned int vram_size;  /* set by setup.c */
100
101 #ifdef HAS_VIDC20
102 #include <mach/acornfb.h>
103
104 #define MAX_SIZE        (2*1024*1024)
105
106 /* VIDC20 has a different set of rules from the VIDC:
107  *  hcr  : must be multiple of 4
108  *  hswr : must be even
109  *  hdsr : must be even
110  *  hder : must be even
111  *  vcr  : >= 2, (interlace, must be odd)
112  *  vswr : >= 1
113  *  vdsr : >= 1
114  *  vder : >= vdsr
115  */
116 static void acornfb_set_timing(struct fb_info *info)
117 {
118         struct fb_var_screeninfo *var = &info->var;
119         struct vidc_timing vidc;
120         u_int vcr, fsize;
121         u_int ext_ctl, dat_ctl;
122         u_int words_per_line;
123
124         memset(&vidc, 0, sizeof(vidc));
125
126         vidc.h_sync_width       = var->hsync_len - 8;
127         vidc.h_border_start     = vidc.h_sync_width + var->left_margin + 8 - 12;
128         vidc.h_display_start    = vidc.h_border_start + 12 - 18;
129         vidc.h_display_end      = vidc.h_display_start + var->xres;
130         vidc.h_border_end       = vidc.h_display_end + 18 - 12;
131         vidc.h_cycle            = vidc.h_border_end + var->right_margin + 12 - 8;
132         vidc.h_interlace        = vidc.h_cycle / 2;
133         vidc.v_sync_width       = var->vsync_len - 1;
134         vidc.v_border_start     = vidc.v_sync_width + var->upper_margin;
135         vidc.v_display_start    = vidc.v_border_start;
136         vidc.v_display_end      = vidc.v_display_start + var->yres;
137         vidc.v_border_end       = vidc.v_display_end;
138         vidc.control            = acornfb_default_control();
139
140         vcr = var->vsync_len + var->upper_margin + var->yres +
141               var->lower_margin;
142
143         if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
144                 vidc.v_cycle = (vcr - 3) / 2;
145                 vidc.control |= VIDC20_CTRL_INT;
146         } else
147                 vidc.v_cycle = vcr - 2;
148
149         switch (var->bits_per_pixel) {
150         case  1: vidc.control |= VIDC20_CTRL_1BPP;      break;
151         case  2: vidc.control |= VIDC20_CTRL_2BPP;      break;
152         case  4: vidc.control |= VIDC20_CTRL_4BPP;      break;
153         default:
154         case  8: vidc.control |= VIDC20_CTRL_8BPP;      break;
155         case 16: vidc.control |= VIDC20_CTRL_16BPP;     break;
156         case 32: vidc.control |= VIDC20_CTRL_32BPP;     break;
157         }
158
159         acornfb_vidc20_find_rates(&vidc, var);
160         fsize = var->vsync_len + var->upper_margin + var->lower_margin - 1;
161
162         if (memcmp(&current_vidc, &vidc, sizeof(vidc))) {
163                 current_vidc = vidc;
164
165                 vidc_writel(VIDC20_CTRL | vidc.control);
166                 vidc_writel(0xd0000000 | vidc.pll_ctl);
167                 vidc_writel(0x80000000 | vidc.h_cycle);
168                 vidc_writel(0x81000000 | vidc.h_sync_width);
169                 vidc_writel(0x82000000 | vidc.h_border_start);
170                 vidc_writel(0x83000000 | vidc.h_display_start);
171                 vidc_writel(0x84000000 | vidc.h_display_end);
172                 vidc_writel(0x85000000 | vidc.h_border_end);
173                 vidc_writel(0x86000000);
174                 vidc_writel(0x87000000 | vidc.h_interlace);
175                 vidc_writel(0x90000000 | vidc.v_cycle);
176                 vidc_writel(0x91000000 | vidc.v_sync_width);
177                 vidc_writel(0x92000000 | vidc.v_border_start);
178                 vidc_writel(0x93000000 | vidc.v_display_start);
179                 vidc_writel(0x94000000 | vidc.v_display_end);
180                 vidc_writel(0x95000000 | vidc.v_border_end);
181                 vidc_writel(0x96000000);
182                 vidc_writel(0x97000000);
183         }
184
185         iomd_writel(fsize, IOMD_FSIZE);
186
187         ext_ctl = acornfb_default_econtrol();
188
189         if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */
190                 ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC;
191         else {
192                 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
193                         ext_ctl |= VIDC20_ECTL_HS_HSYNC;
194                 else
195                         ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
196
197                 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
198                         ext_ctl |= VIDC20_ECTL_VS_VSYNC;
199                 else
200                         ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
201         }
202
203         vidc_writel(VIDC20_ECTL | ext_ctl);
204
205         words_per_line = var->xres * var->bits_per_pixel / 32;
206
207         if (current_par.using_vram && info->fix.smem_len == 2048*1024)
208                 words_per_line /= 2;
209
210         /* RiscPC doesn't use the VIDC's VRAM control. */
211         dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line;
212
213         /* The data bus width is dependent on both the type
214          * and amount of video memory.
215          *     DRAM     32bit low
216          * 1MB VRAM     32bit
217          * 2MB VRAM     64bit
218          */
219         if (current_par.using_vram && current_par.vram_half_sam == 2048)
220                 dat_ctl |= VIDC20_DCTL_BUS_D63_0;
221         else
222                 dat_ctl |= VIDC20_DCTL_BUS_D31_0;
223
224         vidc_writel(VIDC20_DCTL | dat_ctl);
225
226 #ifdef DEBUG_MODE_SELECTION
227         printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,
228                var->yres, var->bits_per_pixel);
229         printk(KERN_DEBUG " H-cycle          : %d\n", vidc.h_cycle);
230         printk(KERN_DEBUG " H-sync-width     : %d\n", vidc.h_sync_width);
231         printk(KERN_DEBUG " H-border-start   : %d\n", vidc.h_border_start);
232         printk(KERN_DEBUG " H-display-start  : %d\n", vidc.h_display_start);
233         printk(KERN_DEBUG " H-display-end    : %d\n", vidc.h_display_end);
234         printk(KERN_DEBUG " H-border-end     : %d\n", vidc.h_border_end);
235         printk(KERN_DEBUG " H-interlace      : %d\n", vidc.h_interlace);
236         printk(KERN_DEBUG " V-cycle          : %d\n", vidc.v_cycle);
237         printk(KERN_DEBUG " V-sync-width     : %d\n", vidc.v_sync_width);
238         printk(KERN_DEBUG " V-border-start   : %d\n", vidc.v_border_start);
239         printk(KERN_DEBUG " V-display-start  : %d\n", vidc.v_display_start);
240         printk(KERN_DEBUG " V-display-end    : %d\n", vidc.v_display_end);
241         printk(KERN_DEBUG " V-border-end     : %d\n", vidc.v_border_end);
242         printk(KERN_DEBUG " Ext Ctrl  (C)    : 0x%08X\n", ext_ctl);
243         printk(KERN_DEBUG " PLL Ctrl  (D)    : 0x%08X\n", vidc.pll_ctl);
244         printk(KERN_DEBUG " Ctrl      (E)    : 0x%08X\n", vidc.control);
245         printk(KERN_DEBUG " Data Ctrl (F)    : 0x%08X\n", dat_ctl);
246         printk(KERN_DEBUG " Fsize            : 0x%08X\n", fsize);
247 #endif
248 }
249
250 /*
251  * We have to take note of the VIDC20's 16-bit palette here.
252  * The VIDC20 looks up a 16 bit pixel as follows:
253  *
254  *   bits   111111
255  *          5432109876543210
256  *   red            ++++++++  (8 bits,  7 to 0)
257  *  green       ++++++++      (8 bits, 11 to 4)
258  *   blue   ++++++++          (8 bits, 15 to 8)
259  *
260  * We use a pixel which looks like:
261  *
262  *   bits   111111
263  *          5432109876543210
264  *   red               +++++  (5 bits,  4 to  0)
265  *  green         +++++       (5 bits,  9 to  5)
266  *   blue    +++++            (5 bits, 14 to 10)
267  */
268 static int
269 acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
270                   u_int trans, struct fb_info *info)
271 {
272         union palette pal;
273
274         if (regno >= current_par.palette_size)
275                 return 1;
276
277         if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
278                 u32 pseudo_val;
279
280                 pseudo_val  = regno << info->var.red.offset;
281                 pseudo_val |= regno << info->var.green.offset;
282                 pseudo_val |= regno << info->var.blue.offset;
283
284                 ((u32 *)info->pseudo_palette)[regno] = pseudo_val;
285         }
286
287         pal.p = 0;
288         pal.vidc20.red   = red >> 8;
289         pal.vidc20.green = green >> 8;
290         pal.vidc20.blue  = blue >> 8;
291
292         current_par.palette[regno] = pal;
293
294         if (info->var.bits_per_pixel == 16) {
295                 int i;
296
297                 pal.p = 0;
298                 vidc_writel(0x10000000);
299                 for (i = 0; i < 256; i += 1) {
300                         pal.vidc20.red   = current_par.palette[i       & 31].vidc20.red;
301                         pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
302                         pal.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
303                         vidc_writel(pal.p);
304                         /* Palette register pointer auto-increments */
305                 }
306         } else {
307                 vidc_writel(0x10000000 | regno);
308                 vidc_writel(pal.p);
309         }
310
311         return 0;
312 }
313 #endif
314
315 /*
316  * Before selecting the timing parameters, adjust
317  * the resolution to fit the rules.
318  */
319 static int
320 acornfb_adjust_timing(struct fb_info *info, struct fb_var_screeninfo *var, u_int fontht)
321 {
322         u_int font_line_len, sam_size, min_size, size, nr_y;
323
324         /* xres must be even */
325         var->xres = (var->xres + 1) & ~1;
326
327         /*
328          * We don't allow xres_virtual to differ from xres
329          */
330         var->xres_virtual = var->xres;
331         var->xoffset = 0;
332
333         if (current_par.using_vram)
334                 sam_size = current_par.vram_half_sam * 2;
335         else
336                 sam_size = 16;
337
338         /*
339          * Now, find a value for yres_virtual which allows
340          * us to do ywrap scrolling.  The value of
341          * yres_virtual must be such that the end of the
342          * displayable frame buffer must be aligned with
343          * the start of a font line.
344          */
345         font_line_len = var->xres * var->bits_per_pixel * fontht / 8;
346         min_size = var->xres * var->yres * var->bits_per_pixel / 8;
347
348         /*
349          * If minimum screen size is greater than that we have
350          * available, reject it.
351          */
352         if (min_size > info->fix.smem_len)
353                 return -EINVAL;
354
355         /* Find int 'y', such that y * fll == s * sam < maxsize
356          * y = s * sam / fll; s = maxsize / sam
357          */
358         for (size = info->fix.smem_len;
359              nr_y = size / font_line_len, min_size <= size;
360              size -= sam_size) {
361                 if (nr_y * font_line_len == size)
362                         break;
363         }
364         nr_y *= fontht;
365
366         if (var->accel_flags & FB_ACCELF_TEXT) {
367                 if (min_size > size) {
368                         /*
369                          * failed, use ypan
370                          */
371                         size = info->fix.smem_len;
372                         var->yres_virtual = size / (font_line_len / fontht);
373                 } else
374                         var->yres_virtual = nr_y;
375         } else if (var->yres_virtual > nr_y)
376                 var->yres_virtual = nr_y;
377
378         current_par.screen_end = info->fix.smem_start + size;
379
380         /*
381          * Fix yres & yoffset if needed.
382          */
383         if (var->yres > var->yres_virtual)
384                 var->yres = var->yres_virtual;
385
386         if (var->vmode & FB_VMODE_YWRAP) {
387                 if (var->yoffset > var->yres_virtual)
388                         var->yoffset = var->yres_virtual;
389         } else {
390                 if (var->yoffset + var->yres > var->yres_virtual)
391                         var->yoffset = var->yres_virtual - var->yres;
392         }
393
394         /* hsync_len must be even */
395         var->hsync_len = (var->hsync_len + 1) & ~1;
396
397 #if defined(HAS_VIDC20)
398         /* left_margin must be even */
399         if (var->left_margin & 1) {
400                 var->left_margin += 1;
401                 var->right_margin -= 1;
402         }
403
404         /* right_margin must be even */
405         if (var->right_margin & 1)
406                 var->right_margin += 1;
407 #endif
408
409         if (var->vsync_len < 1)
410                 var->vsync_len = 1;
411
412         return 0;
413 }
414
415 static int
416 acornfb_validate_timing(struct fb_var_screeninfo *var,
417                         struct fb_monspecs *monspecs)
418 {
419         unsigned long hs, vs;
420
421         /*
422          * hs(Hz) = 10^12 / (pixclock * xtotal)
423          * vs(Hz) = hs(Hz) / ytotal
424          *
425          * No need to do long long divisions or anything
426          * like that if you factor it correctly
427          */
428         hs = 1953125000 / var->pixclock;
429         hs = hs * 512 /
430              (var->xres + var->left_margin + var->right_margin + var->hsync_len);
431         vs = hs /
432              (var->yres + var->upper_margin + var->lower_margin + var->vsync_len);
433
434         return (vs >= monspecs->vfmin && vs <= monspecs->vfmax &&
435                 hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL;
436 }
437
438 static inline void
439 acornfb_update_dma(struct fb_info *info, struct fb_var_screeninfo *var)
440 {
441         u_int off = var->yoffset * info->fix.line_length;
442
443 #if defined(HAS_MEMC)
444         memc_write(VDMA_INIT, off >> 2);
445 #elif defined(HAS_IOMD)
446         iomd_writel(info->fix.smem_start + off, IOMD_VIDINIT);
447 #endif
448 }
449
450 static int
451 acornfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
452 {
453         u_int fontht;
454         int err;
455
456         /*
457          * FIXME: Find the font height
458          */
459         fontht = 8;
460
461         var->red.msb_right = 0;
462         var->green.msb_right = 0;
463         var->blue.msb_right = 0;
464         var->transp.msb_right = 0;
465
466         switch (var->bits_per_pixel) {
467         case 1: case 2: case 4: case 8:
468                 var->red.offset    = 0;
469                 var->red.length    = var->bits_per_pixel;
470                 var->green         = var->red;
471                 var->blue          = var->red;
472                 var->transp.offset = 0;
473                 var->transp.length = 0;
474                 break;
475
476 #ifdef HAS_VIDC20
477         case 16:
478                 var->red.offset    = 0;
479                 var->red.length    = 5;
480                 var->green.offset  = 5;
481                 var->green.length  = 5;
482                 var->blue.offset   = 10;
483                 var->blue.length   = 5;
484                 var->transp.offset = 15;
485                 var->transp.length = 1;
486                 break;
487
488         case 32:
489                 var->red.offset    = 0;
490                 var->red.length    = 8;
491                 var->green.offset  = 8;
492                 var->green.length  = 8;
493                 var->blue.offset   = 16;
494                 var->blue.length   = 8;
495                 var->transp.offset = 24;
496                 var->transp.length = 4;
497                 break;
498 #endif
499         default:
500                 return -EINVAL;
501         }
502
503         /*
504          * Check to see if the pixel rate is valid.
505          */
506         if (!acornfb_valid_pixrate(var))
507                 return -EINVAL;
508
509         /*
510          * Validate and adjust the resolution to
511          * match the video generator hardware.
512          */
513         err = acornfb_adjust_timing(info, var, fontht);
514         if (err)
515                 return err;
516
517         /*
518          * Validate the timing against the
519          * monitor hardware.
520          */
521         return acornfb_validate_timing(var, &info->monspecs);
522 }
523
524 static int acornfb_set_par(struct fb_info *info)
525 {
526         switch (info->var.bits_per_pixel) {
527         case 1:
528                 current_par.palette_size = 2;
529                 info->fix.visual = FB_VISUAL_MONO10;
530                 break;
531         case 2:
532                 current_par.palette_size = 4;
533                 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
534                 break;
535         case 4:
536                 current_par.palette_size = 16;
537                 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
538                 break;
539         case 8:
540                 current_par.palette_size = VIDC_PALETTE_SIZE;
541                 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
542                 break;
543 #ifdef HAS_VIDC20
544         case 16:
545                 current_par.palette_size = 32;
546                 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
547                 break;
548         case 32:
549                 current_par.palette_size = VIDC_PALETTE_SIZE;
550                 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
551                 break;
552 #endif
553         default:
554                 BUG();
555         }
556
557         info->fix.line_length   = (info->var.xres * info->var.bits_per_pixel) / 8;
558
559 #if defined(HAS_MEMC)
560         {
561                 unsigned long size = info->fix.smem_len - VDMA_XFERSIZE;
562
563                 memc_write(VDMA_START, 0);
564                 memc_write(VDMA_END, size >> 2);
565         }
566 #elif defined(HAS_IOMD)
567         {
568                 unsigned long start, size;
569                 u_int control;
570
571                 start = info->fix.smem_start;
572                 size  = current_par.screen_end;
573
574                 if (current_par.using_vram) {
575                         size -= current_par.vram_half_sam;
576                         control = DMA_CR_E | (current_par.vram_half_sam / 256);
577                 } else {
578                         size -= 16;
579                         control = DMA_CR_E | DMA_CR_D | 16;
580                 }
581
582                 iomd_writel(start,   IOMD_VIDSTART);
583                 iomd_writel(size,    IOMD_VIDEND);
584                 iomd_writel(control, IOMD_VIDCR);
585         }
586 #endif
587
588         acornfb_update_dma(info, &info->var);
589         acornfb_set_timing(info);
590
591         return 0;
592 }
593
594 static int
595 acornfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
596 {
597         u_int y_bottom = var->yoffset;
598
599         if (!(var->vmode & FB_VMODE_YWRAP))
600                 y_bottom += info->var.yres;
601
602         if (y_bottom > info->var.yres_virtual)
603                 return -EINVAL;
604
605         acornfb_update_dma(info, var);
606
607         return 0;
608 }
609
610 static struct fb_ops acornfb_ops = {
611         .owner          = THIS_MODULE,
612         .fb_check_var   = acornfb_check_var,
613         .fb_set_par     = acornfb_set_par,
614         .fb_setcolreg   = acornfb_setcolreg,
615         .fb_pan_display = acornfb_pan_display,
616         .fb_fillrect    = cfb_fillrect,
617         .fb_copyarea    = cfb_copyarea,
618         .fb_imageblit   = cfb_imageblit,
619 };
620
621 /*
622  * Everything after here is initialisation!!!
623  */
624 static struct fb_videomode modedb[] = {
625         {       /* 320x256 @ 50Hz */
626                 NULL, 50,  320,  256, 125000,  92,  62,  35, 19,  38, 2,
627                 FB_SYNC_COMP_HIGH_ACT,
628                 FB_VMODE_NONINTERLACED
629         }, {    /* 640x250 @ 50Hz, 15.6 kHz hsync */
630                 NULL, 50,  640,  250,  62500, 185, 123,  38, 21,  76, 3,
631                 0,
632                 FB_VMODE_NONINTERLACED
633         }, {    /* 640x256 @ 50Hz, 15.6 kHz hsync */
634                 NULL, 50,  640,  256,  62500, 185, 123,  35, 18,  76, 3,
635                 0,
636                 FB_VMODE_NONINTERLACED
637         }, {    /* 640x512 @ 50Hz, 26.8 kHz hsync */
638                 NULL, 50,  640,  512,  41667, 113,  87,  18,  1,  56, 3,
639                 0,
640                 FB_VMODE_NONINTERLACED
641         }, {    /* 640x250 @ 70Hz, 31.5 kHz hsync */
642                 NULL, 70,  640,  250,  39722,  48,  16, 109, 88,  96, 2,
643                 0,
644                 FB_VMODE_NONINTERLACED
645         }, {    /* 640x256 @ 70Hz, 31.5 kHz hsync */
646                 NULL, 70,  640,  256,  39722,  48,  16, 106, 85,  96, 2,
647                 0,
648                 FB_VMODE_NONINTERLACED
649         }, {    /* 640x352 @ 70Hz, 31.5 kHz hsync */
650                 NULL, 70,  640,  352,  39722,  48,  16,  58, 37,  96, 2,
651                 0,
652                 FB_VMODE_NONINTERLACED
653         }, {    /* 640x480 @ 60Hz, 31.5 kHz hsync */
654                 NULL, 60,  640,  480,  39722,  48,  16,  32, 11,  96, 2,
655                 0,
656                 FB_VMODE_NONINTERLACED
657         }, {    /* 800x600 @ 56Hz, 35.2 kHz hsync */
658                 NULL, 56,  800,  600,  27778, 101,  23,  22,  1, 100, 2,
659                 0,
660                 FB_VMODE_NONINTERLACED
661         }, {    /* 896x352 @ 60Hz, 21.8 kHz hsync */
662                 NULL, 60,  896,  352,  41667,  59,  27,   9,  0, 118, 3,
663                 0,
664                 FB_VMODE_NONINTERLACED
665         }, {    /* 1024x 768 @ 60Hz, 48.4 kHz hsync */
666                 NULL, 60, 1024,  768,  15385, 160,  24,  29,  3, 136, 6,
667                 0,
668                 FB_VMODE_NONINTERLACED
669         }, {    /* 1280x1024 @ 60Hz, 63.8 kHz hsync */
670                 NULL, 60, 1280, 1024,   9090, 186,  96,  38,  1, 160, 3,
671                 0,
672                 FB_VMODE_NONINTERLACED
673         }
674 };
675
676 static struct fb_videomode acornfb_default_mode = {
677         .name =         NULL,
678         .refresh =      60,
679         .xres =         640,
680         .yres =         480,
681         .pixclock =     39722,
682         .left_margin =  56,
683         .right_margin = 16,
684         .upper_margin = 34,
685         .lower_margin = 9,
686         .hsync_len =    88,
687         .vsync_len =    2,
688         .sync =         0,
689         .vmode =        FB_VMODE_NONINTERLACED
690 };
691
692 static void acornfb_init_fbinfo(void)
693 {
694         static int first = 1;
695
696         if (!first)
697                 return;
698         first = 0;
699
700         fb_info.fbops           = &acornfb_ops;
701         fb_info.flags           = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
702         fb_info.pseudo_palette  = current_par.pseudo_palette;
703
704         strcpy(fb_info.fix.id, "Acorn");
705         fb_info.fix.type        = FB_TYPE_PACKED_PIXELS;
706         fb_info.fix.type_aux    = 0;
707         fb_info.fix.xpanstep    = 0;
708         fb_info.fix.ypanstep    = 1;
709         fb_info.fix.ywrapstep   = 1;
710         fb_info.fix.line_length = 0;
711         fb_info.fix.accel       = FB_ACCEL_NONE;
712
713         /*
714          * setup initial parameters
715          */
716         memset(&fb_info.var, 0, sizeof(fb_info.var));
717
718 #if defined(HAS_VIDC20)
719         fb_info.var.red.length     = 8;
720         fb_info.var.transp.length  = 4;
721 #endif
722         fb_info.var.green          = fb_info.var.red;
723         fb_info.var.blue           = fb_info.var.red;
724         fb_info.var.nonstd         = 0;
725         fb_info.var.activate       = FB_ACTIVATE_NOW;
726         fb_info.var.height         = -1;
727         fb_info.var.width          = -1;
728         fb_info.var.vmode          = FB_VMODE_NONINTERLACED;
729         fb_info.var.accel_flags    = FB_ACCELF_TEXT;
730
731         current_par.dram_size      = 0;
732         current_par.montype        = -1;
733         current_par.dpms           = 0;
734 }
735
736 /*
737  * setup acornfb options:
738  *
739  *  mon:hmin-hmax:vmin-vmax:dpms:width:height
740  *      Set monitor parameters:
741  *              hmin   = horizontal minimum frequency (Hz)
742  *              hmax   = horizontal maximum frequency (Hz)      (optional)
743  *              vmin   = vertical minimum frequency (Hz)
744  *              vmax   = vertical maximum frequency (Hz)        (optional)
745  *              dpms   = DPMS supported?                        (optional)
746  *              width  = width of picture in mm.                (optional)
747  *              height = height of picture in mm.               (optional)
748  *
749  * montype:type
750  *      Set RISC-OS style monitor type:
751  *              0 (or tv)       - TV frequency
752  *              1 (or multi)    - Multi frequency
753  *              2 (or hires)    - Hi-res monochrome
754  *              3 (or vga)      - VGA
755  *              4 (or svga)     - SVGA
756  *              auto, or option missing
757  *                              - try hardware detect
758  *
759  * dram:size
760  *      Set the amount of DRAM to use for the frame buffer
761  *      (even if you have VRAM).
762  *      size can optionally be followed by 'M' or 'K' for
763  *      MB or KB respectively.
764  */
765 static void acornfb_parse_mon(char *opt)
766 {
767         char *p = opt;
768
769         current_par.montype = -2;
770
771         fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
772         if (*p == '-')
773                 fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
774         else
775                 fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
776
777         if (*p != ':')
778                 goto bad;
779
780         fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
781         if (*p == '-')
782                 fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
783         else
784                 fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
785
786         if (*p != ':')
787                 goto check_values;
788
789         fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
790
791         if (*p != ':')
792                 goto check_values;
793
794         fb_info.var.width = simple_strtoul(p + 1, &p, 0);
795
796         if (*p != ':')
797                 goto check_values;
798
799         fb_info.var.height = simple_strtoul(p + 1, NULL, 0);
800
801 check_values:
802         if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
803             fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
804                 goto bad;
805         return;
806
807 bad:
808         printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt);
809         current_par.montype = -1;
810 }
811
812 static void acornfb_parse_montype(char *opt)
813 {
814         current_par.montype = -2;
815
816         if (strncmp(opt, "tv", 2) == 0) {
817                 opt += 2;
818                 current_par.montype = 0;
819         } else if (strncmp(opt, "multi", 5) == 0) {
820                 opt += 5;
821                 current_par.montype = 1;
822         } else if (strncmp(opt, "hires", 5) == 0) {
823                 opt += 5;
824                 current_par.montype = 2;
825         } else if (strncmp(opt, "vga", 3) == 0) {
826                 opt += 3;
827                 current_par.montype = 3;
828         } else if (strncmp(opt, "svga", 4) == 0) {
829                 opt += 4;
830                 current_par.montype = 4;
831         } else if (strncmp(opt, "auto", 4) == 0) {
832                 opt += 4;
833                 current_par.montype = -1;
834         } else if (isdigit(*opt))
835                 current_par.montype = simple_strtoul(opt, &opt, 0);
836
837         if (current_par.montype == -2 ||
838             current_par.montype > NR_MONTYPES) {
839                 printk(KERN_ERR "acornfb: unknown monitor type: %s\n",
840                         opt);
841                 current_par.montype = -1;
842         } else
843         if (opt && *opt) {
844                 if (strcmp(opt, ",dpms") == 0)
845                         current_par.dpms = 1;
846                 else
847                         printk(KERN_ERR
848                                "acornfb: unknown monitor option: %s\n",
849                                opt);
850         }
851 }
852
853 static void acornfb_parse_dram(char *opt)
854 {
855         unsigned int size;
856
857         size = simple_strtoul(opt, &opt, 0);
858
859         if (opt) {
860                 switch (*opt) {
861                 case 'M':
862                 case 'm':
863                         size *= 1024;
864                 case 'K':
865                 case 'k':
866                         size *= 1024;
867                 default:
868                         break;
869                 }
870         }
871
872         current_par.dram_size = size;
873 }
874
875 static struct options {
876         char *name;
877         void (*parse)(char *opt);
878 } opt_table[] = {
879         { "mon",     acornfb_parse_mon     },
880         { "montype", acornfb_parse_montype },
881         { "dram",    acornfb_parse_dram    },
882         { NULL, NULL }
883 };
884
885 static int acornfb_setup(char *options)
886 {
887         struct options *optp;
888         char *opt;
889
890         if (!options || !*options)
891                 return 0;
892
893         acornfb_init_fbinfo();
894
895         while ((opt = strsep(&options, ",")) != NULL) {
896                 if (!*opt)
897                         continue;
898
899                 for (optp = opt_table; optp->name; optp++) {
900                         int optlen;
901
902                         optlen = strlen(optp->name);
903
904                         if (strncmp(opt, optp->name, optlen) == 0 &&
905                             opt[optlen] == ':') {
906                                 optp->parse(opt + optlen + 1);
907                                 break;
908                         }
909                 }
910
911                 if (!optp->name)
912                         printk(KERN_ERR "acornfb: unknown parameter: %s\n",
913                                opt);
914         }
915         return 0;
916 }
917
918 /*
919  * Detect type of monitor connected
920  *  For now, we just assume SVGA
921  */
922 static int acornfb_detect_monitortype(void)
923 {
924         return 4;
925 }
926
927 /*
928  * This enables the unused memory to be freed on older Acorn machines.
929  * We are freeing memory on behalf of the architecture initialisation
930  * code here.
931  */
932 static inline void
933 free_unused_pages(unsigned int virtual_start, unsigned int virtual_end)
934 {
935         int mb_freed = 0;
936
937         /*
938          * Align addresses
939          */
940         virtual_start = PAGE_ALIGN(virtual_start);
941         virtual_end = PAGE_ALIGN(virtual_end);
942
943         while (virtual_start < virtual_end) {
944                 struct page *page;
945
946                 /*
947                  * Clear page reserved bit,
948                  * set count to 1, and free
949                  * the page.
950                  */
951                 page = virt_to_page(virtual_start);
952                 __free_reserved_page(page);
953
954                 virtual_start += PAGE_SIZE;
955                 mb_freed += PAGE_SIZE / 1024;
956         }
957
958         printk("acornfb: freed %dK memory\n", mb_freed);
959 }
960
961 static int acornfb_probe(struct platform_device *dev)
962 {
963         unsigned long size;
964         u_int h_sync, v_sync;
965         int rc, i;
966         char *option = NULL;
967
968         if (fb_get_options("acornfb", &option))
969                 return -ENODEV;
970         acornfb_setup(option);
971
972         acornfb_init_fbinfo();
973
974         current_par.dev = &dev->dev;
975
976         if (current_par.montype == -1)
977                 current_par.montype = acornfb_detect_monitortype();
978
979         if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
980                 current_par.montype = 4;
981
982         if (current_par.montype >= 0) {
983                 fb_info.monspecs = monspecs[current_par.montype];
984                 fb_info.monspecs.dpms = current_par.dpms;
985         }
986
987         /*
988          * Try to select a suitable default mode
989          */
990         for (i = 0; i < ARRAY_SIZE(modedb); i++) {
991                 unsigned long hs;
992
993                 hs = modedb[i].refresh *
994                      (modedb[i].yres + modedb[i].upper_margin +
995                       modedb[i].lower_margin + modedb[i].vsync_len);
996                 if (modedb[i].xres == DEFAULT_XRES &&
997                     modedb[i].yres == DEFAULT_YRES &&
998                     modedb[i].refresh >= fb_info.monspecs.vfmin &&
999                     modedb[i].refresh <= fb_info.monspecs.vfmax &&
1000                     hs                >= fb_info.monspecs.hfmin &&
1001                     hs                <= fb_info.monspecs.hfmax) {
1002                         acornfb_default_mode = modedb[i];
1003                         break;
1004                 }
1005         }
1006
1007         fb_info.screen_base    = (char *)SCREEN_BASE;
1008         fb_info.fix.smem_start = SCREEN_START;
1009         current_par.using_vram = 0;
1010
1011         /*
1012          * If vram_size is set, we are using VRAM in
1013          * a Risc PC.  However, if the user has specified
1014          * an amount of DRAM then use that instead.
1015          */
1016         if (vram_size && !current_par.dram_size) {
1017                 size = vram_size;
1018                 current_par.vram_half_sam = vram_size / 1024;
1019                 current_par.using_vram = 1;
1020         } else if (current_par.dram_size)
1021                 size = current_par.dram_size;
1022         else
1023                 size = MAX_SIZE;
1024
1025         /*
1026          * Limit maximum screen size.
1027          */
1028         if (size > MAX_SIZE)
1029                 size = MAX_SIZE;
1030
1031         size = PAGE_ALIGN(size);
1032
1033 #if defined(HAS_VIDC20)
1034         if (!current_par.using_vram) {
1035                 dma_addr_t handle;
1036                 void *base;
1037
1038                 /*
1039                  * RiscPC needs to allocate the DRAM memory
1040                  * for the framebuffer if we are not using
1041                  * VRAM.
1042                  */
1043                 base = dma_alloc_wc(current_par.dev, size, &handle,
1044                                     GFP_KERNEL);
1045                 if (base == NULL) {
1046                         printk(KERN_ERR "acornfb: unable to allocate screen memory\n");
1047                         return -ENOMEM;
1048                 }
1049
1050                 fb_info.screen_base = base;
1051                 fb_info.fix.smem_start = handle;
1052         }
1053 #endif
1054         fb_info.fix.smem_len = size;
1055         current_par.palette_size   = VIDC_PALETTE_SIZE;
1056
1057         /*
1058          * Lookup the timing for this resolution.  If we can't
1059          * find it, then we can't restore it if we change
1060          * the resolution, so we disable this feature.
1061          */
1062         do {
1063                 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1064                                  ARRAY_SIZE(modedb),
1065                                  &acornfb_default_mode, DEFAULT_BPP);
1066                 /*
1067                  * If we found an exact match, all ok.
1068                  */
1069                 if (rc == 1)
1070                         break;
1071
1072                 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1073                                   &acornfb_default_mode, DEFAULT_BPP);
1074                 /*
1075                  * If we found an exact match, all ok.
1076                  */
1077                 if (rc == 1)
1078                         break;
1079
1080                 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1081                                  ARRAY_SIZE(modedb),
1082                                  &acornfb_default_mode, DEFAULT_BPP);
1083                 if (rc)
1084                         break;
1085
1086                 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1087                                   &acornfb_default_mode, DEFAULT_BPP);
1088         } while (0);
1089
1090         /*
1091          * If we didn't find an exact match, try the
1092          * generic database.
1093          */
1094         if (rc == 0) {
1095                 printk("Acornfb: no valid mode found\n");
1096                 return -EINVAL;
1097         }
1098
1099         h_sync = 1953125000 / fb_info.var.pixclock;
1100         h_sync = h_sync * 512 / (fb_info.var.xres + fb_info.var.left_margin +
1101                  fb_info.var.right_margin + fb_info.var.hsync_len);
1102         v_sync = h_sync / (fb_info.var.yres + fb_info.var.upper_margin +
1103                  fb_info.var.lower_margin + fb_info.var.vsync_len);
1104
1105         printk(KERN_INFO "Acornfb: %dkB %cRAM, %s, using %dx%d, %d.%03dkHz, %dHz\n",
1106                 fb_info.fix.smem_len / 1024,
1107                 current_par.using_vram ? 'V' : 'D',
1108                 VIDC_NAME, fb_info.var.xres, fb_info.var.yres,
1109                 h_sync / 1000, h_sync % 1000, v_sync);
1110
1111         printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
1112                 fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
1113                 fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
1114                 fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
1115                 fb_info.monspecs.dpms ? ", DPMS" : "");
1116
1117         if (fb_set_var(&fb_info, &fb_info.var))
1118                 printk(KERN_ERR "Acornfb: unable to set display parameters\n");
1119
1120         if (register_framebuffer(&fb_info) < 0)
1121                 return -EINVAL;
1122         return 0;
1123 }
1124
1125 static struct platform_driver acornfb_driver = {
1126         .probe  = acornfb_probe,
1127         .driver = {
1128                 .name   = "acornfb",
1129         },
1130 };
1131
1132 static int __init acornfb_init(void)
1133 {
1134         return platform_driver_register(&acornfb_driver);
1135 }
1136
1137 module_init(acornfb_init);
1138
1139 MODULE_AUTHOR("Russell King");
1140 MODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver");
1141 MODULE_LICENSE("GPL");