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/slab.h>
26#include <linux/mm.h>
27#include <linux/init.h>
28#include <linux/fb.h>
29#include <linux/platform_device.h>
30#include <linux/dma-mapping.h>
31
32#include <mach/hardware.h>
33#include <asm/io.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#ifdef HAS_VIDC
44#undef FBCON_HAS_CFB16
45#undef FBCON_HAS_CFB32
46#endif
47
48
49
50
51
52
53#define DEFAULT_XRES 640
54#define DEFAULT_YRES 480
55#define DEFAULT_BPP 4
56
57
58
59
60#undef DEBUG_MODE_SELECTION
61
62
63
64
65
66
67
68#define NR_MONTYPES 6
69static struct fb_monspecs monspecs[NR_MONTYPES] __initdata = {
70 {
71 .hfmin = 15469,
72 .hfmax = 15781,
73 .vfmin = 49,
74 .vfmax = 51,
75 }, {
76 .hfmin = 0,
77 .hfmax = 99999,
78 .vfmin = 0,
79 .vfmax = 199,
80 }, {
81 .hfmin = 58608,
82 .hfmax = 58608,
83 .vfmin = 64,
84 .vfmax = 64,
85 }, {
86 .hfmin = 30000,
87 .hfmax = 70000,
88 .vfmin = 60,
89 .vfmax = 60,
90 }, {
91 .hfmin = 30000,
92 .hfmax = 70000,
93 .vfmin = 56,
94 .vfmax = 75,
95 }, {
96 .hfmin = 30000,
97 .hfmax = 70000,
98 .vfmin = 60,
99 .vfmax = 60,
100 }
101};
102
103static struct fb_info fb_info;
104static struct acornfb_par current_par;
105static struct vidc_timing current_vidc;
106
107extern unsigned int vram_size;
108
109#ifdef HAS_VIDC
110
111#define MAX_SIZE 480*1024
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127struct pixclock {
128 u_long min_clock;
129 u_long max_clock;
130 u_int vidc_ctl;
131 u_int vid_ctl;
132};
133
134static struct pixclock arc_clocks[] = {
135
136 { 123750, 126250, VIDC_CTRL_DIV3, VID_CTL_24MHz },
137 { 82500, 84167, VIDC_CTRL_DIV2, VID_CTL_24MHz },
138 { 61875, 63125, VIDC_CTRL_DIV1_5, VID_CTL_24MHz },
139 { 41250, 42083, VIDC_CTRL_DIV1, VID_CTL_24MHz },
140};
141
142static struct pixclock *
143acornfb_valid_pixrate(struct fb_var_screeninfo *var)
144{
145 u_long pixclock = var->pixclock;
146 u_int i;
147
148 if (!var->pixclock)
149 return NULL;
150
151 for (i = 0; i < ARRAY_SIZE(arc_clocks); i++)
152 if (pixclock > arc_clocks[i].min_clock &&
153 pixclock < arc_clocks[i].max_clock)
154 return arc_clocks + i;
155
156 return NULL;
157}
158
159
160
161
162
163
164
165
166
167
168
169
170
171static void
172acornfb_set_timing(struct fb_var_screeninfo *var)
173{
174 struct pixclock *pclk;
175 struct vidc_timing vidc;
176 u_int horiz_correction;
177 u_int sync_len, display_start, display_end, cycle;
178 u_int is_interlaced;
179 u_int vid_ctl, vidc_ctl;
180 u_int bandwidth;
181
182 memset(&vidc, 0, sizeof(vidc));
183
184 pclk = acornfb_valid_pixrate(var);
185 vidc_ctl = pclk->vidc_ctl;
186 vid_ctl = pclk->vid_ctl;
187
188 bandwidth = var->pixclock * 8 / var->bits_per_pixel;
189
190 if (bandwidth > 143500)
191 vidc_ctl |= VIDC_CTRL_FIFO_3_7;
192 else if (bandwidth > 71750)
193 vidc_ctl |= VIDC_CTRL_FIFO_2_6;
194 else if (bandwidth > 35875)
195 vidc_ctl |= VIDC_CTRL_FIFO_1_5;
196 else
197 vidc_ctl |= VIDC_CTRL_FIFO_0_4;
198
199 switch (var->bits_per_pixel) {
200 case 1:
201 horiz_correction = 19;
202 vidc_ctl |= VIDC_CTRL_1BPP;
203 break;
204
205 case 2:
206 horiz_correction = 11;
207 vidc_ctl |= VIDC_CTRL_2BPP;
208 break;
209
210 case 4:
211 horiz_correction = 7;
212 vidc_ctl |= VIDC_CTRL_4BPP;
213 break;
214
215 default:
216 case 8:
217 horiz_correction = 5;
218 vidc_ctl |= VIDC_CTRL_8BPP;
219 break;
220 }
221
222 if (var->sync & FB_SYNC_COMP_HIGH_ACT)
223 vidc_ctl |= VIDC_CTRL_CSYNC;
224 else {
225 if (!(var->sync & FB_SYNC_HOR_HIGH_ACT))
226 vid_ctl |= VID_CTL_HS_NHSYNC;
227
228 if (!(var->sync & FB_SYNC_VERT_HIGH_ACT))
229 vid_ctl |= VID_CTL_VS_NVSYNC;
230 }
231
232 sync_len = var->hsync_len;
233 display_start = sync_len + var->left_margin;
234 display_end = display_start + var->xres;
235 cycle = display_end + var->right_margin;
236
237
238 is_interlaced = (var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED;
239
240 if (is_interlaced) {
241 vidc_ctl |= VIDC_CTRL_INTERLACE;
242 if (cycle & 2) {
243 cycle += 2;
244 var->right_margin += 2;
245 }
246 }
247
248 vidc.h_cycle = (cycle - 2) / 2;
249 vidc.h_sync_width = (sync_len - 2) / 2;
250 vidc.h_border_start = (display_start - 1) / 2;
251 vidc.h_display_start = (display_start - horiz_correction) / 2;
252 vidc.h_display_end = (display_end - horiz_correction) / 2;
253 vidc.h_border_end = (display_end - 1) / 2;
254 vidc.h_interlace = (vidc.h_cycle + 1) / 2;
255
256 sync_len = var->vsync_len;
257 display_start = sync_len + var->upper_margin;
258 display_end = display_start + var->yres;
259 cycle = display_end + var->lower_margin;
260
261 if (is_interlaced)
262 cycle = (cycle - 3) / 2;
263 else
264 cycle = cycle - 1;
265
266 vidc.v_cycle = cycle;
267 vidc.v_sync_width = sync_len - 1;
268 vidc.v_border_start = display_start - 1;
269 vidc.v_display_start = vidc.v_border_start;
270 vidc.v_display_end = display_end - 1;
271 vidc.v_border_end = vidc.v_display_end;
272
273 if (machine_is_a5k())
274 __raw_writeb(vid_ctl, IOEB_VID_CTL);
275
276 if (memcmp(¤t_vidc, &vidc, sizeof(vidc))) {
277 current_vidc = vidc;
278
279 vidc_writel(0xe0000000 | vidc_ctl);
280 vidc_writel(0x80000000 | (vidc.h_cycle << 14));
281 vidc_writel(0x84000000 | (vidc.h_sync_width << 14));
282 vidc_writel(0x88000000 | (vidc.h_border_start << 14));
283 vidc_writel(0x8c000000 | (vidc.h_display_start << 14));
284 vidc_writel(0x90000000 | (vidc.h_display_end << 14));
285 vidc_writel(0x94000000 | (vidc.h_border_end << 14));
286 vidc_writel(0x98000000);
287 vidc_writel(0x9c000000 | (vidc.h_interlace << 14));
288 vidc_writel(0xa0000000 | (vidc.v_cycle << 14));
289 vidc_writel(0xa4000000 | (vidc.v_sync_width << 14));
290 vidc_writel(0xa8000000 | (vidc.v_border_start << 14));
291 vidc_writel(0xac000000 | (vidc.v_display_start << 14));
292 vidc_writel(0xb0000000 | (vidc.v_display_end << 14));
293 vidc_writel(0xb4000000 | (vidc.v_border_end << 14));
294 vidc_writel(0xb8000000);
295 vidc_writel(0xbc000000);
296 }
297#ifdef DEBUG_MODE_SELECTION
298 printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,
299 var->yres, var->bits_per_pixel);
300 printk(KERN_DEBUG " H-cycle : %d\n", vidc.h_cycle);
301 printk(KERN_DEBUG " H-sync-width : %d\n", vidc.h_sync_width);
302 printk(KERN_DEBUG " H-border-start : %d\n", vidc.h_border_start);
303 printk(KERN_DEBUG " H-display-start : %d\n", vidc.h_display_start);
304 printk(KERN_DEBUG " H-display-end : %d\n", vidc.h_display_end);
305 printk(KERN_DEBUG " H-border-end : %d\n", vidc.h_border_end);
306 printk(KERN_DEBUG " H-interlace : %d\n", vidc.h_interlace);
307 printk(KERN_DEBUG " V-cycle : %d\n", vidc.v_cycle);
308 printk(KERN_DEBUG " V-sync-width : %d\n", vidc.v_sync_width);
309 printk(KERN_DEBUG " V-border-start : %d\n", vidc.v_border_start);
310 printk(KERN_DEBUG " V-display-start : %d\n", vidc.v_display_start);
311 printk(KERN_DEBUG " V-display-end : %d\n", vidc.v_display_end);
312 printk(KERN_DEBUG " V-border-end : %d\n", vidc.v_border_end);
313 printk(KERN_DEBUG " VIDC Ctrl (E) : 0x%08X\n", vidc_ctl);
314 printk(KERN_DEBUG " IOEB Ctrl : 0x%08X\n", vid_ctl);
315#endif
316}
317
318static int
319acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
320 u_int trans, struct fb_info *info)
321{
322 union palette pal;
323
324 if (regno >= current_par.palette_size)
325 return 1;
326
327 pal.p = 0;
328 pal.vidc.reg = regno;
329 pal.vidc.red = red >> 12;
330 pal.vidc.green = green >> 12;
331 pal.vidc.blue = blue >> 12;
332
333 current_par.palette[regno] = pal;
334
335 vidc_writel(pal.p);
336
337 return 0;
338}
339#endif
340
341#ifdef HAS_VIDC20
342#include <mach/acornfb.h>
343
344#define MAX_SIZE 2*1024*1024
345
346
347
348
349
350
351
352
353
354
355
356static void acornfb_set_timing(struct fb_info *info)
357{
358 struct fb_var_screeninfo *var = &info->var;
359 struct vidc_timing vidc;
360 u_int vcr, fsize;
361 u_int ext_ctl, dat_ctl;
362 u_int words_per_line;
363
364 memset(&vidc, 0, sizeof(vidc));
365
366 vidc.h_sync_width = var->hsync_len - 8;
367 vidc.h_border_start = vidc.h_sync_width + var->left_margin + 8 - 12;
368 vidc.h_display_start = vidc.h_border_start + 12 - 18;
369 vidc.h_display_end = vidc.h_display_start + var->xres;
370 vidc.h_border_end = vidc.h_display_end + 18 - 12;
371 vidc.h_cycle = vidc.h_border_end + var->right_margin + 12 - 8;
372 vidc.h_interlace = vidc.h_cycle / 2;
373 vidc.v_sync_width = var->vsync_len - 1;
374 vidc.v_border_start = vidc.v_sync_width + var->upper_margin;
375 vidc.v_display_start = vidc.v_border_start;
376 vidc.v_display_end = vidc.v_display_start + var->yres;
377 vidc.v_border_end = vidc.v_display_end;
378 vidc.control = acornfb_default_control();
379
380 vcr = var->vsync_len + var->upper_margin + var->yres +
381 var->lower_margin;
382
383 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
384 vidc.v_cycle = (vcr - 3) / 2;
385 vidc.control |= VIDC20_CTRL_INT;
386 } else
387 vidc.v_cycle = vcr - 2;
388
389 switch (var->bits_per_pixel) {
390 case 1: vidc.control |= VIDC20_CTRL_1BPP; break;
391 case 2: vidc.control |= VIDC20_CTRL_2BPP; break;
392 case 4: vidc.control |= VIDC20_CTRL_4BPP; break;
393 default:
394 case 8: vidc.control |= VIDC20_CTRL_8BPP; break;
395 case 16: vidc.control |= VIDC20_CTRL_16BPP; break;
396 case 32: vidc.control |= VIDC20_CTRL_32BPP; break;
397 }
398
399 acornfb_vidc20_find_rates(&vidc, var);
400 fsize = var->vsync_len + var->upper_margin + var->lower_margin - 1;
401
402 if (memcmp(¤t_vidc, &vidc, sizeof(vidc))) {
403 current_vidc = vidc;
404
405 vidc_writel(VIDC20_CTRL| vidc.control);
406 vidc_writel(0xd0000000 | vidc.pll_ctl);
407 vidc_writel(0x80000000 | vidc.h_cycle);
408 vidc_writel(0x81000000 | vidc.h_sync_width);
409 vidc_writel(0x82000000 | vidc.h_border_start);
410 vidc_writel(0x83000000 | vidc.h_display_start);
411 vidc_writel(0x84000000 | vidc.h_display_end);
412 vidc_writel(0x85000000 | vidc.h_border_end);
413 vidc_writel(0x86000000);
414 vidc_writel(0x87000000 | vidc.h_interlace);
415 vidc_writel(0x90000000 | vidc.v_cycle);
416 vidc_writel(0x91000000 | vidc.v_sync_width);
417 vidc_writel(0x92000000 | vidc.v_border_start);
418 vidc_writel(0x93000000 | vidc.v_display_start);
419 vidc_writel(0x94000000 | vidc.v_display_end);
420 vidc_writel(0x95000000 | vidc.v_border_end);
421 vidc_writel(0x96000000);
422 vidc_writel(0x97000000);
423 }
424
425 iomd_writel(fsize, IOMD_FSIZE);
426
427 ext_ctl = acornfb_default_econtrol();
428
429 if (var->sync & FB_SYNC_COMP_HIGH_ACT)
430 ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC;
431 else {
432 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
433 ext_ctl |= VIDC20_ECTL_HS_HSYNC;
434 else
435 ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
436
437 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
438 ext_ctl |= VIDC20_ECTL_VS_VSYNC;
439 else
440 ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
441 }
442
443 vidc_writel(VIDC20_ECTL | ext_ctl);
444
445 words_per_line = var->xres * var->bits_per_pixel / 32;
446
447 if (current_par.using_vram && info->fix.smem_len == 2048*1024)
448 words_per_line /= 2;
449
450
451 dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line;
452
453
454
455
456
457
458
459 if (current_par.using_vram && current_par.vram_half_sam == 2048)
460 dat_ctl |= VIDC20_DCTL_BUS_D63_0;
461 else
462 dat_ctl |= VIDC20_DCTL_BUS_D31_0;
463
464 vidc_writel(VIDC20_DCTL | dat_ctl);
465
466#ifdef DEBUG_MODE_SELECTION
467 printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,
468 var->yres, var->bits_per_pixel);
469 printk(KERN_DEBUG " H-cycle : %d\n", vidc.h_cycle);
470 printk(KERN_DEBUG " H-sync-width : %d\n", vidc.h_sync_width);
471 printk(KERN_DEBUG " H-border-start : %d\n", vidc.h_border_start);
472 printk(KERN_DEBUG " H-display-start : %d\n", vidc.h_display_start);
473 printk(KERN_DEBUG " H-display-end : %d\n", vidc.h_display_end);
474 printk(KERN_DEBUG " H-border-end : %d\n", vidc.h_border_end);
475 printk(KERN_DEBUG " H-interlace : %d\n", vidc.h_interlace);
476 printk(KERN_DEBUG " V-cycle : %d\n", vidc.v_cycle);
477 printk(KERN_DEBUG " V-sync-width : %d\n", vidc.v_sync_width);
478 printk(KERN_DEBUG " V-border-start : %d\n", vidc.v_border_start);
479 printk(KERN_DEBUG " V-display-start : %d\n", vidc.v_display_start);
480 printk(KERN_DEBUG " V-display-end : %d\n", vidc.v_display_end);
481 printk(KERN_DEBUG " V-border-end : %d\n", vidc.v_border_end);
482 printk(KERN_DEBUG " Ext Ctrl (C) : 0x%08X\n", ext_ctl);
483 printk(KERN_DEBUG " PLL Ctrl (D) : 0x%08X\n", vidc.pll_ctl);
484 printk(KERN_DEBUG " Ctrl (E) : 0x%08X\n", vidc.control);
485 printk(KERN_DEBUG " Data Ctrl (F) : 0x%08X\n", dat_ctl);
486 printk(KERN_DEBUG " Fsize : 0x%08X\n", fsize);
487#endif
488}
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508static int
509acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
510 u_int trans, struct fb_info *info)
511{
512 union palette pal;
513
514 if (regno >= current_par.palette_size)
515 return 1;
516
517 if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
518 u32 pseudo_val;
519
520 pseudo_val = regno << info->var.red.offset;
521 pseudo_val |= regno << info->var.green.offset;
522 pseudo_val |= regno << info->var.blue.offset;
523
524 ((u32 *)info->pseudo_palette)[regno] = pseudo_val;
525 }
526
527 pal.p = 0;
528 pal.vidc20.red = red >> 8;
529 pal.vidc20.green = green >> 8;
530 pal.vidc20.blue = blue >> 8;
531
532 current_par.palette[regno] = pal;
533
534 if (info->var.bits_per_pixel == 16) {
535 int i;
536
537 pal.p = 0;
538 vidc_writel(0x10000000);
539 for (i = 0; i < 256; i += 1) {
540 pal.vidc20.red = current_par.palette[ i & 31].vidc20.red;
541 pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
542 pal.vidc20.blue = current_par.palette[(i >> 2) & 31].vidc20.blue;
543 vidc_writel(pal.p);
544
545 }
546 } else {
547 vidc_writel(0x10000000 | regno);
548 vidc_writel(pal.p);
549 }
550
551 return 0;
552}
553#endif
554
555
556
557
558
559static int
560acornfb_adjust_timing(struct fb_info *info, struct fb_var_screeninfo *var, u_int fontht)
561{
562 u_int font_line_len, sam_size, min_size, size, nr_y;
563
564
565 var->xres = (var->xres + 1) & ~1;
566
567
568
569
570 var->xres_virtual = var->xres;
571 var->xoffset = 0;
572
573 if (current_par.using_vram)
574 sam_size = current_par.vram_half_sam * 2;
575 else
576 sam_size = 16;
577
578
579
580
581
582
583
584
585 font_line_len = var->xres * var->bits_per_pixel * fontht / 8;
586 min_size = var->xres * var->yres * var->bits_per_pixel / 8;
587
588
589
590
591
592 if (min_size > info->fix.smem_len)
593 return -EINVAL;
594
595
596
597
598 for (size = info->fix.smem_len;
599 nr_y = size / font_line_len, min_size <= size;
600 size -= sam_size) {
601 if (nr_y * font_line_len == size)
602 break;
603 }
604 nr_y *= fontht;
605
606 if (var->accel_flags & FB_ACCELF_TEXT) {
607 if (min_size > size) {
608
609
610
611 size = info->fix.smem_len;
612 var->yres_virtual = size / (font_line_len / fontht);
613 } else
614 var->yres_virtual = nr_y;
615 } else if (var->yres_virtual > nr_y)
616 var->yres_virtual = nr_y;
617
618 current_par.screen_end = info->fix.smem_start + size;
619
620
621
622
623 if (var->yres > var->yres_virtual)
624 var->yres = var->yres_virtual;
625
626 if (var->vmode & FB_VMODE_YWRAP) {
627 if (var->yoffset > var->yres_virtual)
628 var->yoffset = var->yres_virtual;
629 } else {
630 if (var->yoffset + var->yres > var->yres_virtual)
631 var->yoffset = var->yres_virtual - var->yres;
632 }
633
634
635 var->hsync_len = (var->hsync_len + 1) & ~1;
636
637#ifdef HAS_VIDC
638
639 if ((var->left_margin & 1) == 0) {
640 var->left_margin -= 1;
641 var->right_margin += 1;
642 }
643
644
645 var->right_margin |= 1;
646#elif defined(HAS_VIDC20)
647
648 if (var->left_margin & 1) {
649 var->left_margin += 1;
650 var->right_margin -= 1;
651 }
652
653
654 if (var->right_margin & 1)
655 var->right_margin += 1;
656#endif
657
658 if (var->vsync_len < 1)
659 var->vsync_len = 1;
660
661 return 0;
662}
663
664static int
665acornfb_validate_timing(struct fb_var_screeninfo *var,
666 struct fb_monspecs *monspecs)
667{
668 unsigned long hs, vs;
669
670
671
672
673
674
675
676
677 hs = 1953125000 / var->pixclock;
678 hs = hs * 512 /
679 (var->xres + var->left_margin + var->right_margin + var->hsync_len);
680 vs = hs /
681 (var->yres + var->upper_margin + var->lower_margin + var->vsync_len);
682
683 return (vs >= monspecs->vfmin && vs <= monspecs->vfmax &&
684 hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL;
685}
686
687static inline void
688acornfb_update_dma(struct fb_info *info, struct fb_var_screeninfo *var)
689{
690 u_int off = var->yoffset * info->fix.line_length;
691
692#if defined(HAS_MEMC)
693 memc_write(VDMA_INIT, off >> 2);
694#elif defined(HAS_IOMD)
695 iomd_writel(info->fix.smem_start + off, IOMD_VIDINIT);
696#endif
697}
698
699static int
700acornfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
701{
702 u_int fontht;
703 int err;
704
705
706
707
708 fontht = 8;
709
710 var->red.msb_right = 0;
711 var->green.msb_right = 0;
712 var->blue.msb_right = 0;
713 var->transp.msb_right = 0;
714
715 switch (var->bits_per_pixel) {
716 case 1: case 2: case 4: case 8:
717 var->red.offset = 0;
718 var->red.length = var->bits_per_pixel;
719 var->green = var->red;
720 var->blue = var->red;
721 var->transp.offset = 0;
722 var->transp.length = 0;
723 break;
724
725#ifdef HAS_VIDC20
726 case 16:
727 var->red.offset = 0;
728 var->red.length = 5;
729 var->green.offset = 5;
730 var->green.length = 5;
731 var->blue.offset = 10;
732 var->blue.length = 5;
733 var->transp.offset = 15;
734 var->transp.length = 1;
735 break;
736
737 case 32:
738 var->red.offset = 0;
739 var->red.length = 8;
740 var->green.offset = 8;
741 var->green.length = 8;
742 var->blue.offset = 16;
743 var->blue.length = 8;
744 var->transp.offset = 24;
745 var->transp.length = 4;
746 break;
747#endif
748 default:
749 return -EINVAL;
750 }
751
752
753
754
755 if (!acornfb_valid_pixrate(var))
756 return -EINVAL;
757
758
759
760
761
762 err = acornfb_adjust_timing(info, var, fontht);
763 if (err)
764 return err;
765
766
767
768
769
770 return acornfb_validate_timing(var, &info->monspecs);
771}
772
773static int acornfb_set_par(struct fb_info *info)
774{
775 switch (info->var.bits_per_pixel) {
776 case 1:
777 current_par.palette_size = 2;
778 info->fix.visual = FB_VISUAL_MONO10;
779 break;
780 case 2:
781 current_par.palette_size = 4;
782 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
783 break;
784 case 4:
785 current_par.palette_size = 16;
786 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
787 break;
788 case 8:
789 current_par.palette_size = VIDC_PALETTE_SIZE;
790#ifdef HAS_VIDC
791 info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
792#else
793 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
794#endif
795 break;
796#ifdef HAS_VIDC20
797 case 16:
798 current_par.palette_size = 32;
799 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
800 break;
801 case 32:
802 current_par.palette_size = VIDC_PALETTE_SIZE;
803 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
804 break;
805#endif
806 default:
807 BUG();
808 }
809
810 info->fix.line_length = (info->var.xres * info->var.bits_per_pixel) / 8;
811
812#if defined(HAS_MEMC)
813 {
814 unsigned long size = info->fix.smem_len - VDMA_XFERSIZE;
815
816 memc_write(VDMA_START, 0);
817 memc_write(VDMA_END, size >> 2);
818 }
819#elif defined(HAS_IOMD)
820 {
821 unsigned long start, size;
822 u_int control;
823
824 start = info->fix.smem_start;
825 size = current_par.screen_end;
826
827 if (current_par.using_vram) {
828 size -= current_par.vram_half_sam;
829 control = DMA_CR_E | (current_par.vram_half_sam / 256);
830 } else {
831 size -= 16;
832 control = DMA_CR_E | DMA_CR_D | 16;
833 }
834
835 iomd_writel(start, IOMD_VIDSTART);
836 iomd_writel(size, IOMD_VIDEND);
837 iomd_writel(control, IOMD_VIDCR);
838 }
839#endif
840
841 acornfb_update_dma(info, &info->var);
842 acornfb_set_timing(info);
843
844 return 0;
845}
846
847static int
848acornfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
849{
850 u_int y_bottom = var->yoffset;
851
852 if (!(var->vmode & FB_VMODE_YWRAP))
853 y_bottom += var->yres;
854
855 BUG_ON(y_bottom > var->yres_virtual);
856
857 acornfb_update_dma(info, var);
858
859 return 0;
860}
861
862
863
864
865static int
866acornfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
867{
868 unsigned long off, start;
869 u32 len;
870
871 off = vma->vm_pgoff << PAGE_SHIFT;
872
873 start = info->fix.smem_start;
874 len = PAGE_ALIGN(start & ~PAGE_MASK) + info->fix.smem_len;
875 start &= PAGE_MASK;
876 if ((vma->vm_end - vma->vm_start + off) > len)
877 return -EINVAL;
878 off += start;
879 vma->vm_pgoff = off >> PAGE_SHIFT;
880
881
882 vma->vm_flags |= VM_IO;
883
884 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
885
886
887
888
889
890
891
892 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
893 vma->vm_end - vma->vm_start,
894 vma->vm_page_prot))
895 return -EAGAIN;
896 return 0;
897}
898
899static struct fb_ops acornfb_ops = {
900 .owner = THIS_MODULE,
901 .fb_check_var = acornfb_check_var,
902 .fb_set_par = acornfb_set_par,
903 .fb_setcolreg = acornfb_setcolreg,
904 .fb_pan_display = acornfb_pan_display,
905 .fb_fillrect = cfb_fillrect,
906 .fb_copyarea = cfb_copyarea,
907 .fb_imageblit = cfb_imageblit,
908 .fb_mmap = acornfb_mmap,
909};
910
911
912
913
914static struct fb_videomode modedb[] __initdata = {
915 {
916 NULL, 50, 320, 256, 125000, 92, 62, 35, 19, 38, 2,
917 FB_SYNC_COMP_HIGH_ACT,
918 FB_VMODE_NONINTERLACED
919 }, {
920 NULL, 50, 640, 250, 62500, 185, 123, 38, 21, 76, 3,
921 0,
922 FB_VMODE_NONINTERLACED
923 }, {
924 NULL, 50, 640, 256, 62500, 185, 123, 35, 18, 76, 3,
925 0,
926 FB_VMODE_NONINTERLACED
927 }, {
928 NULL, 50, 640, 512, 41667, 113, 87, 18, 1, 56, 3,
929 0,
930 FB_VMODE_NONINTERLACED
931 }, {
932 NULL, 70, 640, 250, 39722, 48, 16, 109, 88, 96, 2,
933 0,
934 FB_VMODE_NONINTERLACED
935 }, {
936 NULL, 70, 640, 256, 39722, 48, 16, 106, 85, 96, 2,
937 0,
938 FB_VMODE_NONINTERLACED
939 }, {
940 NULL, 70, 640, 352, 39722, 48, 16, 58, 37, 96, 2,
941 0,
942 FB_VMODE_NONINTERLACED
943 }, {
944 NULL, 60, 640, 480, 39722, 48, 16, 32, 11, 96, 2,
945 0,
946 FB_VMODE_NONINTERLACED
947 }, {
948 NULL, 56, 800, 600, 27778, 101, 23, 22, 1, 100, 2,
949 0,
950 FB_VMODE_NONINTERLACED
951 }, {
952 NULL, 60, 896, 352, 41667, 59, 27, 9, 0, 118, 3,
953 0,
954 FB_VMODE_NONINTERLACED
955 }, {
956 NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
957 0,
958 FB_VMODE_NONINTERLACED
959 }, {
960 NULL, 60, 1280, 1024, 9090, 186, 96, 38, 1, 160, 3,
961 0,
962 FB_VMODE_NONINTERLACED
963 }
964};
965
966static struct fb_videomode __initdata
967acornfb_default_mode = {
968 .name = NULL,
969 .refresh = 60,
970 .xres = 640,
971 .yres = 480,
972 .pixclock = 39722,
973 .left_margin = 56,
974 .right_margin = 16,
975 .upper_margin = 34,
976 .lower_margin = 9,
977 .hsync_len = 88,
978 .vsync_len = 2,
979 .sync = 0,
980 .vmode = FB_VMODE_NONINTERLACED
981};
982
983static void __init acornfb_init_fbinfo(void)
984{
985 static int first = 1;
986
987 if (!first)
988 return;
989 first = 0;
990
991 fb_info.fbops = &acornfb_ops;
992 fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
993 fb_info.pseudo_palette = current_par.pseudo_palette;
994
995 strcpy(fb_info.fix.id, "Acorn");
996 fb_info.fix.type = FB_TYPE_PACKED_PIXELS;
997 fb_info.fix.type_aux = 0;
998 fb_info.fix.xpanstep = 0;
999 fb_info.fix.ypanstep = 1;
1000 fb_info.fix.ywrapstep = 1;
1001 fb_info.fix.line_length = 0;
1002 fb_info.fix.accel = FB_ACCEL_NONE;
1003
1004
1005
1006
1007 memset(&fb_info.var, 0, sizeof(fb_info.var));
1008
1009#if defined(HAS_VIDC20)
1010 fb_info.var.red.length = 8;
1011 fb_info.var.transp.length = 4;
1012#elif defined(HAS_VIDC)
1013 fb_info.var.red.length = 4;
1014 fb_info.var.transp.length = 1;
1015#endif
1016 fb_info.var.green = fb_info.var.red;
1017 fb_info.var.blue = fb_info.var.red;
1018 fb_info.var.nonstd = 0;
1019 fb_info.var.activate = FB_ACTIVATE_NOW;
1020 fb_info.var.height = -1;
1021 fb_info.var.width = -1;
1022 fb_info.var.vmode = FB_VMODE_NONINTERLACED;
1023 fb_info.var.accel_flags = FB_ACCELF_TEXT;
1024
1025 current_par.dram_size = 0;
1026 current_par.montype = -1;
1027 current_par.dpms = 0;
1028}
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059static void __init
1060acornfb_parse_mon(char *opt)
1061{
1062 char *p = opt;
1063
1064 current_par.montype = -2;
1065
1066 fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
1067 if (*p == '-')
1068 fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
1069 else
1070 fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
1071
1072 if (*p != ':')
1073 goto bad;
1074
1075 fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
1076 if (*p == '-')
1077 fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
1078 else
1079 fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
1080
1081 if (*p != ':')
1082 goto check_values;
1083
1084 fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
1085
1086 if (*p != ':')
1087 goto check_values;
1088
1089 fb_info.var.width = simple_strtoul(p + 1, &p, 0);
1090
1091 if (*p != ':')
1092 goto check_values;
1093
1094 fb_info.var.height = simple_strtoul(p + 1, NULL, 0);
1095
1096check_values:
1097 if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
1098 fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
1099 goto bad;
1100 return;
1101
1102bad:
1103 printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt);
1104 current_par.montype = -1;
1105}
1106
1107static void __init
1108acornfb_parse_montype(char *opt)
1109{
1110 current_par.montype = -2;
1111
1112 if (strncmp(opt, "tv", 2) == 0) {
1113 opt += 2;
1114 current_par.montype = 0;
1115 } else if (strncmp(opt, "multi", 5) == 0) {
1116 opt += 5;
1117 current_par.montype = 1;
1118 } else if (strncmp(opt, "hires", 5) == 0) {
1119 opt += 5;
1120 current_par.montype = 2;
1121 } else if (strncmp(opt, "vga", 3) == 0) {
1122 opt += 3;
1123 current_par.montype = 3;
1124 } else if (strncmp(opt, "svga", 4) == 0) {
1125 opt += 4;
1126 current_par.montype = 4;
1127 } else if (strncmp(opt, "auto", 4) == 0) {
1128 opt += 4;
1129 current_par.montype = -1;
1130 } else if (isdigit(*opt))
1131 current_par.montype = simple_strtoul(opt, &opt, 0);
1132
1133 if (current_par.montype == -2 ||
1134 current_par.montype > NR_MONTYPES) {
1135 printk(KERN_ERR "acornfb: unknown monitor type: %s\n",
1136 opt);
1137 current_par.montype = -1;
1138 } else
1139 if (opt && *opt) {
1140 if (strcmp(opt, ",dpms") == 0)
1141 current_par.dpms = 1;
1142 else
1143 printk(KERN_ERR
1144 "acornfb: unknown monitor option: %s\n",
1145 opt);
1146 }
1147}
1148
1149static void __init
1150acornfb_parse_dram(char *opt)
1151{
1152 unsigned int size;
1153
1154 size = simple_strtoul(opt, &opt, 0);
1155
1156 if (opt) {
1157 switch (*opt) {
1158 case 'M':
1159 case 'm':
1160 size *= 1024;
1161 case 'K':
1162 case 'k':
1163 size *= 1024;
1164 default:
1165 break;
1166 }
1167 }
1168
1169 current_par.dram_size = size;
1170}
1171
1172static struct options {
1173 char *name;
1174 void (*parse)(char *opt);
1175} opt_table[] __initdata = {
1176 { "mon", acornfb_parse_mon },
1177 { "montype", acornfb_parse_montype },
1178 { "dram", acornfb_parse_dram },
1179 { NULL, NULL }
1180};
1181
1182int __init
1183acornfb_setup(char *options)
1184{
1185 struct options *optp;
1186 char *opt;
1187
1188 if (!options || !*options)
1189 return 0;
1190
1191 acornfb_init_fbinfo();
1192
1193 while ((opt = strsep(&options, ",")) != NULL) {
1194 if (!*opt)
1195 continue;
1196
1197 for (optp = opt_table; optp->name; optp++) {
1198 int optlen;
1199
1200 optlen = strlen(optp->name);
1201
1202 if (strncmp(opt, optp->name, optlen) == 0 &&
1203 opt[optlen] == ':') {
1204 optp->parse(opt + optlen + 1);
1205 break;
1206 }
1207 }
1208
1209 if (!optp->name)
1210 printk(KERN_ERR "acornfb: unknown parameter: %s\n",
1211 opt);
1212 }
1213 return 0;
1214}
1215
1216
1217
1218
1219
1220static int __init
1221acornfb_detect_monitortype(void)
1222{
1223 return 4;
1224}
1225
1226
1227
1228
1229
1230
1231static inline void
1232free_unused_pages(unsigned int virtual_start, unsigned int virtual_end)
1233{
1234 int mb_freed = 0;
1235
1236
1237
1238
1239 virtual_start = PAGE_ALIGN(virtual_start);
1240 virtual_end = PAGE_ALIGN(virtual_end);
1241
1242 while (virtual_start < virtual_end) {
1243 struct page *page;
1244
1245
1246
1247
1248
1249
1250 page = virt_to_page(virtual_start);
1251 ClearPageReserved(page);
1252 init_page_count(page);
1253 free_page(virtual_start);
1254
1255 virtual_start += PAGE_SIZE;
1256 mb_freed += PAGE_SIZE / 1024;
1257 }
1258
1259 printk("acornfb: freed %dK memory\n", mb_freed);
1260}
1261
1262static int __init acornfb_probe(struct platform_device *dev)
1263{
1264 unsigned long size;
1265 u_int h_sync, v_sync;
1266 int rc, i;
1267 char *option = NULL;
1268
1269 if (fb_get_options("acornfb", &option))
1270 return -ENODEV;
1271 acornfb_setup(option);
1272
1273 acornfb_init_fbinfo();
1274
1275 current_par.dev = &dev->dev;
1276
1277 if (current_par.montype == -1)
1278 current_par.montype = acornfb_detect_monitortype();
1279
1280 if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
1281 current_par.montype = 4;
1282
1283 if (current_par.montype >= 0) {
1284 fb_info.monspecs = monspecs[current_par.montype];
1285 fb_info.monspecs.dpms = current_par.dpms;
1286 }
1287
1288
1289
1290
1291 for (i = 0; i < ARRAY_SIZE(modedb); i++) {
1292 unsigned long hs;
1293
1294 hs = modedb[i].refresh *
1295 (modedb[i].yres + modedb[i].upper_margin +
1296 modedb[i].lower_margin + modedb[i].vsync_len);
1297 if (modedb[i].xres == DEFAULT_XRES &&
1298 modedb[i].yres == DEFAULT_YRES &&
1299 modedb[i].refresh >= fb_info.monspecs.vfmin &&
1300 modedb[i].refresh <= fb_info.monspecs.vfmax &&
1301 hs >= fb_info.monspecs.hfmin &&
1302 hs <= fb_info.monspecs.hfmax) {
1303 acornfb_default_mode = modedb[i];
1304 break;
1305 }
1306 }
1307
1308 fb_info.screen_base = (char *)SCREEN_BASE;
1309 fb_info.fix.smem_start = SCREEN_START;
1310 current_par.using_vram = 0;
1311
1312
1313
1314
1315
1316
1317 if (vram_size && !current_par.dram_size) {
1318 size = vram_size;
1319 current_par.vram_half_sam = vram_size / 1024;
1320 current_par.using_vram = 1;
1321 } else if (current_par.dram_size)
1322 size = current_par.dram_size;
1323 else
1324 size = MAX_SIZE;
1325
1326
1327
1328
1329 if (size > MAX_SIZE)
1330 size = MAX_SIZE;
1331
1332 size = PAGE_ALIGN(size);
1333
1334#if defined(HAS_VIDC20)
1335 if (!current_par.using_vram) {
1336 dma_addr_t handle;
1337 void *base;
1338
1339
1340
1341
1342
1343
1344 base = dma_alloc_writecombine(current_par.dev, size, &handle,
1345 GFP_KERNEL);
1346 if (base == NULL) {
1347 printk(KERN_ERR "acornfb: unable to allocate screen "
1348 "memory\n");
1349 return -ENOMEM;
1350 }
1351
1352 fb_info.screen_base = base;
1353 fb_info.fix.smem_start = handle;
1354 }
1355#endif
1356#if defined(HAS_VIDC)
1357
1358
1359
1360
1361 free_unused_pages(PAGE_OFFSET + size, PAGE_OFFSET + MAX_SIZE);
1362#endif
1363
1364 fb_info.fix.smem_len = size;
1365 current_par.palette_size = VIDC_PALETTE_SIZE;
1366
1367
1368
1369
1370
1371
1372 do {
1373 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1374 ARRAY_SIZE(modedb),
1375 &acornfb_default_mode, DEFAULT_BPP);
1376
1377
1378
1379 if (rc == 1)
1380 break;
1381
1382 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1383 &acornfb_default_mode, DEFAULT_BPP);
1384
1385
1386
1387 if (rc == 1)
1388 break;
1389
1390 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1391 ARRAY_SIZE(modedb),
1392 &acornfb_default_mode, DEFAULT_BPP);
1393 if (rc)
1394 break;
1395
1396 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1397 &acornfb_default_mode, DEFAULT_BPP);
1398 } while (0);
1399
1400
1401
1402
1403
1404 if (rc == 0) {
1405 printk("Acornfb: no valid mode found\n");
1406 return -EINVAL;
1407 }
1408
1409 h_sync = 1953125000 / fb_info.var.pixclock;
1410 h_sync = h_sync * 512 / (fb_info.var.xres + fb_info.var.left_margin +
1411 fb_info.var.right_margin + fb_info.var.hsync_len);
1412 v_sync = h_sync / (fb_info.var.yres + fb_info.var.upper_margin +
1413 fb_info.var.lower_margin + fb_info.var.vsync_len);
1414
1415 printk(KERN_INFO "Acornfb: %dkB %cRAM, %s, using %dx%d, "
1416 "%d.%03dkHz, %dHz\n",
1417 fb_info.fix.smem_len / 1024,
1418 current_par.using_vram ? 'V' : 'D',
1419 VIDC_NAME, fb_info.var.xres, fb_info.var.yres,
1420 h_sync / 1000, h_sync % 1000, v_sync);
1421
1422 printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
1423 fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
1424 fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
1425 fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
1426 fb_info.monspecs.dpms ? ", DPMS" : "");
1427
1428 if (fb_set_var(&fb_info, &fb_info.var))
1429 printk(KERN_ERR "Acornfb: unable to set display parameters\n");
1430
1431 if (register_framebuffer(&fb_info) < 0)
1432 return -EINVAL;
1433 return 0;
1434}
1435
1436static struct platform_driver acornfb_driver = {
1437 .probe = acornfb_probe,
1438 .driver = {
1439 .name = "acornfb",
1440 },
1441};
1442
1443static int __init acornfb_init(void)
1444{
1445 return platform_driver_register(&acornfb_driver);
1446}
1447
1448module_init(acornfb_init);
1449
1450MODULE_AUTHOR("Russell King");
1451MODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver");
1452MODULE_LICENSE("GPL");
1453