1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
42
43
44
45#define DEFAULT_XRES 640
46#define DEFAULT_YRES 480
47#define DEFAULT_BPP 4
48
49
50
51
52#undef DEBUG_MODE_SELECTION
53
54
55
56
57
58
59
60#define NR_MONTYPES 6
61static struct fb_monspecs monspecs[NR_MONTYPES] = {
62 {
63 .hfmin = 15469,
64 .hfmax = 15781,
65 .vfmin = 49,
66 .vfmax = 51,
67 }, {
68 .hfmin = 0,
69 .hfmax = 99999,
70 .vfmin = 0,
71 .vfmax = 199,
72 }, {
73 .hfmin = 58608,
74 .hfmax = 58608,
75 .vfmin = 64,
76 .vfmax = 64,
77 }, {
78 .hfmin = 30000,
79 .hfmax = 70000,
80 .vfmin = 60,
81 .vfmax = 60,
82 }, {
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
95static struct fb_info fb_info;
96static struct acornfb_par current_par;
97static struct vidc_timing current_vidc;
98
99extern unsigned int vram_size;
100
101#ifdef HAS_VIDC20
102#include <mach/acornfb.h>
103
104#define MAX_SIZE 2*1024*1024
105
106
107
108
109
110
111
112
113
114
115
116static 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(¤t_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)
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
211 dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line;
212
213
214
215
216
217
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268static int
269acornfb_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
305 }
306 } else {
307 vidc_writel(0x10000000 | regno);
308 vidc_writel(pal.p);
309 }
310
311 return 0;
312}
313#endif
314
315
316
317
318
319static int
320acornfb_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
325 var->xres = (var->xres + 1) & ~1;
326
327
328
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
340
341
342
343
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
350
351
352 if (min_size > info->fix.smem_len)
353 return -EINVAL;
354
355
356
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
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
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
395 var->hsync_len = (var->hsync_len + 1) & ~1;
396
397#if defined(HAS_VIDC20)
398
399 if (var->left_margin & 1) {
400 var->left_margin += 1;
401 var->right_margin -= 1;
402 }
403
404
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
415static int
416acornfb_validate_timing(struct fb_var_screeninfo *var,
417 struct fb_monspecs *monspecs)
418{
419 unsigned long hs, vs;
420
421
422
423
424
425
426
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
438static inline void
439acornfb_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
450static int
451acornfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
452{
453 u_int fontht;
454 int err;
455
456
457
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
505
506 if (!acornfb_valid_pixrate(var))
507 return -EINVAL;
508
509
510
511
512
513 err = acornfb_adjust_timing(info, var, fontht);
514 if (err)
515 return err;
516
517
518
519
520
521 return acornfb_validate_timing(var, &info->monspecs);
522}
523
524static 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
594static int
595acornfb_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
610static 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
623
624static struct fb_videomode modedb[] = {
625 {
626 NULL, 50, 320, 256, 125000, 92, 62, 35, 19, 38, 2,
627 FB_SYNC_COMP_HIGH_ACT,
628 FB_VMODE_NONINTERLACED
629 }, {
630 NULL, 50, 640, 250, 62500, 185, 123, 38, 21, 76, 3,
631 0,
632 FB_VMODE_NONINTERLACED
633 }, {
634 NULL, 50, 640, 256, 62500, 185, 123, 35, 18, 76, 3,
635 0,
636 FB_VMODE_NONINTERLACED
637 }, {
638 NULL, 50, 640, 512, 41667, 113, 87, 18, 1, 56, 3,
639 0,
640 FB_VMODE_NONINTERLACED
641 }, {
642 NULL, 70, 640, 250, 39722, 48, 16, 109, 88, 96, 2,
643 0,
644 FB_VMODE_NONINTERLACED
645 }, {
646 NULL, 70, 640, 256, 39722, 48, 16, 106, 85, 96, 2,
647 0,
648 FB_VMODE_NONINTERLACED
649 }, {
650 NULL, 70, 640, 352, 39722, 48, 16, 58, 37, 96, 2,
651 0,
652 FB_VMODE_NONINTERLACED
653 }, {
654 NULL, 60, 640, 480, 39722, 48, 16, 32, 11, 96, 2,
655 0,
656 FB_VMODE_NONINTERLACED
657 }, {
658 NULL, 56, 800, 600, 27778, 101, 23, 22, 1, 100, 2,
659 0,
660 FB_VMODE_NONINTERLACED
661 }, {
662 NULL, 60, 896, 352, 41667, 59, 27, 9, 0, 118, 3,
663 0,
664 FB_VMODE_NONINTERLACED
665 }, {
666 NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
667 0,
668 FB_VMODE_NONINTERLACED
669 }, {
670 NULL, 60, 1280, 1024, 9090, 186, 96, 38, 1, 160, 3,
671 0,
672 FB_VMODE_NONINTERLACED
673 }
674};
675
676static 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
692static 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
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
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765static 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
801check_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
807bad:
808 printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt);
809 current_par.montype = -1;
810}
811
812static 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
853static 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
875static 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
885static 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
920
921
922static int acornfb_detect_monitortype(void)
923{
924 return 4;
925}
926
927
928
929
930
931
932static inline void
933free_unused_pages(unsigned int virtual_start, unsigned int virtual_end)
934{
935 int mb_freed = 0;
936
937
938
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
948
949
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
961static 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
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
1013
1014
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
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
1040
1041
1042
1043 base = dma_alloc_writecombine(current_par.dev, size, &handle,
1044 GFP_KERNEL);
1045 if (base == NULL) {
1046 printk(KERN_ERR "acornfb: unable to allocate screen "
1047 "memory\n");
1048 return -ENOMEM;
1049 }
1050
1051 fb_info.screen_base = base;
1052 fb_info.fix.smem_start = handle;
1053 }
1054#endif
1055 fb_info.fix.smem_len = size;
1056 current_par.palette_size = VIDC_PALETTE_SIZE;
1057
1058
1059
1060
1061
1062
1063 do {
1064 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1065 ARRAY_SIZE(modedb),
1066 &acornfb_default_mode, DEFAULT_BPP);
1067
1068
1069
1070 if (rc == 1)
1071 break;
1072
1073 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1074 &acornfb_default_mode, DEFAULT_BPP);
1075
1076
1077
1078 if (rc == 1)
1079 break;
1080
1081 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1082 ARRAY_SIZE(modedb),
1083 &acornfb_default_mode, DEFAULT_BPP);
1084 if (rc)
1085 break;
1086
1087 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1088 &acornfb_default_mode, DEFAULT_BPP);
1089 } while (0);
1090
1091
1092
1093
1094
1095 if (rc == 0) {
1096 printk("Acornfb: no valid mode found\n");
1097 return -EINVAL;
1098 }
1099
1100 h_sync = 1953125000 / fb_info.var.pixclock;
1101 h_sync = h_sync * 512 / (fb_info.var.xres + fb_info.var.left_margin +
1102 fb_info.var.right_margin + fb_info.var.hsync_len);
1103 v_sync = h_sync / (fb_info.var.yres + fb_info.var.upper_margin +
1104 fb_info.var.lower_margin + fb_info.var.vsync_len);
1105
1106 printk(KERN_INFO "Acornfb: %dkB %cRAM, %s, using %dx%d, "
1107 "%d.%03dkHz, %dHz\n",
1108 fb_info.fix.smem_len / 1024,
1109 current_par.using_vram ? 'V' : 'D',
1110 VIDC_NAME, fb_info.var.xres, fb_info.var.yres,
1111 h_sync / 1000, h_sync % 1000, v_sync);
1112
1113 printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
1114 fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
1115 fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
1116 fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
1117 fb_info.monspecs.dpms ? ", DPMS" : "");
1118
1119 if (fb_set_var(&fb_info, &fb_info.var))
1120 printk(KERN_ERR "Acornfb: unable to set display parameters\n");
1121
1122 if (register_framebuffer(&fb_info) < 0)
1123 return -EINVAL;
1124 return 0;
1125}
1126
1127static struct platform_driver acornfb_driver = {
1128 .probe = acornfb_probe,
1129 .driver = {
1130 .name = "acornfb",
1131 },
1132};
1133
1134static int __init acornfb_init(void)
1135{
1136 return platform_driver_register(&acornfb_driver);
1137}
1138
1139module_init(acornfb_init);
1140
1141MODULE_AUTHOR("Russell King");
1142MODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver");
1143MODULE_LICENSE("GPL");
1144