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