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#include <linux/io.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#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
862static struct fb_ops acornfb_ops = {
863 .owner = THIS_MODULE,
864 .fb_check_var = acornfb_check_var,
865 .fb_set_par = acornfb_set_par,
866 .fb_setcolreg = acornfb_setcolreg,
867 .fb_pan_display = acornfb_pan_display,
868 .fb_fillrect = cfb_fillrect,
869 .fb_copyarea = cfb_copyarea,
870 .fb_imageblit = cfb_imageblit,
871};
872
873
874
875
876static struct fb_videomode modedb[] __initdata = {
877 {
878 NULL, 50, 320, 256, 125000, 92, 62, 35, 19, 38, 2,
879 FB_SYNC_COMP_HIGH_ACT,
880 FB_VMODE_NONINTERLACED
881 }, {
882 NULL, 50, 640, 250, 62500, 185, 123, 38, 21, 76, 3,
883 0,
884 FB_VMODE_NONINTERLACED
885 }, {
886 NULL, 50, 640, 256, 62500, 185, 123, 35, 18, 76, 3,
887 0,
888 FB_VMODE_NONINTERLACED
889 }, {
890 NULL, 50, 640, 512, 41667, 113, 87, 18, 1, 56, 3,
891 0,
892 FB_VMODE_NONINTERLACED
893 }, {
894 NULL, 70, 640, 250, 39722, 48, 16, 109, 88, 96, 2,
895 0,
896 FB_VMODE_NONINTERLACED
897 }, {
898 NULL, 70, 640, 256, 39722, 48, 16, 106, 85, 96, 2,
899 0,
900 FB_VMODE_NONINTERLACED
901 }, {
902 NULL, 70, 640, 352, 39722, 48, 16, 58, 37, 96, 2,
903 0,
904 FB_VMODE_NONINTERLACED
905 }, {
906 NULL, 60, 640, 480, 39722, 48, 16, 32, 11, 96, 2,
907 0,
908 FB_VMODE_NONINTERLACED
909 }, {
910 NULL, 56, 800, 600, 27778, 101, 23, 22, 1, 100, 2,
911 0,
912 FB_VMODE_NONINTERLACED
913 }, {
914 NULL, 60, 896, 352, 41667, 59, 27, 9, 0, 118, 3,
915 0,
916 FB_VMODE_NONINTERLACED
917 }, {
918 NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
919 0,
920 FB_VMODE_NONINTERLACED
921 }, {
922 NULL, 60, 1280, 1024, 9090, 186, 96, 38, 1, 160, 3,
923 0,
924 FB_VMODE_NONINTERLACED
925 }
926};
927
928static struct fb_videomode __initdata
929acornfb_default_mode = {
930 .name = NULL,
931 .refresh = 60,
932 .xres = 640,
933 .yres = 480,
934 .pixclock = 39722,
935 .left_margin = 56,
936 .right_margin = 16,
937 .upper_margin = 34,
938 .lower_margin = 9,
939 .hsync_len = 88,
940 .vsync_len = 2,
941 .sync = 0,
942 .vmode = FB_VMODE_NONINTERLACED
943};
944
945static void __init acornfb_init_fbinfo(void)
946{
947 static int first = 1;
948
949 if (!first)
950 return;
951 first = 0;
952
953 fb_info.fbops = &acornfb_ops;
954 fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
955 fb_info.pseudo_palette = current_par.pseudo_palette;
956
957 strcpy(fb_info.fix.id, "Acorn");
958 fb_info.fix.type = FB_TYPE_PACKED_PIXELS;
959 fb_info.fix.type_aux = 0;
960 fb_info.fix.xpanstep = 0;
961 fb_info.fix.ypanstep = 1;
962 fb_info.fix.ywrapstep = 1;
963 fb_info.fix.line_length = 0;
964 fb_info.fix.accel = FB_ACCEL_NONE;
965
966
967
968
969 memset(&fb_info.var, 0, sizeof(fb_info.var));
970
971#if defined(HAS_VIDC20)
972 fb_info.var.red.length = 8;
973 fb_info.var.transp.length = 4;
974#elif defined(HAS_VIDC)
975 fb_info.var.red.length = 4;
976 fb_info.var.transp.length = 1;
977#endif
978 fb_info.var.green = fb_info.var.red;
979 fb_info.var.blue = fb_info.var.red;
980 fb_info.var.nonstd = 0;
981 fb_info.var.activate = FB_ACTIVATE_NOW;
982 fb_info.var.height = -1;
983 fb_info.var.width = -1;
984 fb_info.var.vmode = FB_VMODE_NONINTERLACED;
985 fb_info.var.accel_flags = FB_ACCELF_TEXT;
986
987 current_par.dram_size = 0;
988 current_par.montype = -1;
989 current_par.dpms = 0;
990}
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021static void __init
1022acornfb_parse_mon(char *opt)
1023{
1024 char *p = opt;
1025
1026 current_par.montype = -2;
1027
1028 fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
1029 if (*p == '-')
1030 fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
1031 else
1032 fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
1033
1034 if (*p != ':')
1035 goto bad;
1036
1037 fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
1038 if (*p == '-')
1039 fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
1040 else
1041 fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
1042
1043 if (*p != ':')
1044 goto check_values;
1045
1046 fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
1047
1048 if (*p != ':')
1049 goto check_values;
1050
1051 fb_info.var.width = simple_strtoul(p + 1, &p, 0);
1052
1053 if (*p != ':')
1054 goto check_values;
1055
1056 fb_info.var.height = simple_strtoul(p + 1, NULL, 0);
1057
1058check_values:
1059 if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
1060 fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
1061 goto bad;
1062 return;
1063
1064bad:
1065 printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt);
1066 current_par.montype = -1;
1067}
1068
1069static void __init
1070acornfb_parse_montype(char *opt)
1071{
1072 current_par.montype = -2;
1073
1074 if (strncmp(opt, "tv", 2) == 0) {
1075 opt += 2;
1076 current_par.montype = 0;
1077 } else if (strncmp(opt, "multi", 5) == 0) {
1078 opt += 5;
1079 current_par.montype = 1;
1080 } else if (strncmp(opt, "hires", 5) == 0) {
1081 opt += 5;
1082 current_par.montype = 2;
1083 } else if (strncmp(opt, "vga", 3) == 0) {
1084 opt += 3;
1085 current_par.montype = 3;
1086 } else if (strncmp(opt, "svga", 4) == 0) {
1087 opt += 4;
1088 current_par.montype = 4;
1089 } else if (strncmp(opt, "auto", 4) == 0) {
1090 opt += 4;
1091 current_par.montype = -1;
1092 } else if (isdigit(*opt))
1093 current_par.montype = simple_strtoul(opt, &opt, 0);
1094
1095 if (current_par.montype == -2 ||
1096 current_par.montype > NR_MONTYPES) {
1097 printk(KERN_ERR "acornfb: unknown monitor type: %s\n",
1098 opt);
1099 current_par.montype = -1;
1100 } else
1101 if (opt && *opt) {
1102 if (strcmp(opt, ",dpms") == 0)
1103 current_par.dpms = 1;
1104 else
1105 printk(KERN_ERR
1106 "acornfb: unknown monitor option: %s\n",
1107 opt);
1108 }
1109}
1110
1111static void __init
1112acornfb_parse_dram(char *opt)
1113{
1114 unsigned int size;
1115
1116 size = simple_strtoul(opt, &opt, 0);
1117
1118 if (opt) {
1119 switch (*opt) {
1120 case 'M':
1121 case 'm':
1122 size *= 1024;
1123 case 'K':
1124 case 'k':
1125 size *= 1024;
1126 default:
1127 break;
1128 }
1129 }
1130
1131 current_par.dram_size = size;
1132}
1133
1134static struct options {
1135 char *name;
1136 void (*parse)(char *opt);
1137} opt_table[] __initdata = {
1138 { "mon", acornfb_parse_mon },
1139 { "montype", acornfb_parse_montype },
1140 { "dram", acornfb_parse_dram },
1141 { NULL, NULL }
1142};
1143
1144int __init
1145acornfb_setup(char *options)
1146{
1147 struct options *optp;
1148 char *opt;
1149
1150 if (!options || !*options)
1151 return 0;
1152
1153 acornfb_init_fbinfo();
1154
1155 while ((opt = strsep(&options, ",")) != NULL) {
1156 if (!*opt)
1157 continue;
1158
1159 for (optp = opt_table; optp->name; optp++) {
1160 int optlen;
1161
1162 optlen = strlen(optp->name);
1163
1164 if (strncmp(opt, optp->name, optlen) == 0 &&
1165 opt[optlen] == ':') {
1166 optp->parse(opt + optlen + 1);
1167 break;
1168 }
1169 }
1170
1171 if (!optp->name)
1172 printk(KERN_ERR "acornfb: unknown parameter: %s\n",
1173 opt);
1174 }
1175 return 0;
1176}
1177
1178
1179
1180
1181
1182static int __init
1183acornfb_detect_monitortype(void)
1184{
1185 return 4;
1186}
1187
1188
1189
1190
1191
1192
1193static inline void
1194free_unused_pages(unsigned int virtual_start, unsigned int virtual_end)
1195{
1196 int mb_freed = 0;
1197
1198
1199
1200
1201 virtual_start = PAGE_ALIGN(virtual_start);
1202 virtual_end = PAGE_ALIGN(virtual_end);
1203
1204 while (virtual_start < virtual_end) {
1205 struct page *page;
1206
1207
1208
1209
1210
1211
1212 page = virt_to_page(virtual_start);
1213 ClearPageReserved(page);
1214 init_page_count(page);
1215 free_page(virtual_start);
1216
1217 virtual_start += PAGE_SIZE;
1218 mb_freed += PAGE_SIZE / 1024;
1219 }
1220
1221 printk("acornfb: freed %dK memory\n", mb_freed);
1222}
1223
1224static int __init acornfb_probe(struct platform_device *dev)
1225{
1226 unsigned long size;
1227 u_int h_sync, v_sync;
1228 int rc, i;
1229 char *option = NULL;
1230
1231 if (fb_get_options("acornfb", &option))
1232 return -ENODEV;
1233 acornfb_setup(option);
1234
1235 acornfb_init_fbinfo();
1236
1237 current_par.dev = &dev->dev;
1238
1239 if (current_par.montype == -1)
1240 current_par.montype = acornfb_detect_monitortype();
1241
1242 if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
1243 current_par.montype = 4;
1244
1245 if (current_par.montype >= 0) {
1246 fb_info.monspecs = monspecs[current_par.montype];
1247 fb_info.monspecs.dpms = current_par.dpms;
1248 }
1249
1250
1251
1252
1253 for (i = 0; i < ARRAY_SIZE(modedb); i++) {
1254 unsigned long hs;
1255
1256 hs = modedb[i].refresh *
1257 (modedb[i].yres + modedb[i].upper_margin +
1258 modedb[i].lower_margin + modedb[i].vsync_len);
1259 if (modedb[i].xres == DEFAULT_XRES &&
1260 modedb[i].yres == DEFAULT_YRES &&
1261 modedb[i].refresh >= fb_info.monspecs.vfmin &&
1262 modedb[i].refresh <= fb_info.monspecs.vfmax &&
1263 hs >= fb_info.monspecs.hfmin &&
1264 hs <= fb_info.monspecs.hfmax) {
1265 acornfb_default_mode = modedb[i];
1266 break;
1267 }
1268 }
1269
1270 fb_info.screen_base = (char *)SCREEN_BASE;
1271 fb_info.fix.smem_start = SCREEN_START;
1272 current_par.using_vram = 0;
1273
1274
1275
1276
1277
1278
1279 if (vram_size && !current_par.dram_size) {
1280 size = vram_size;
1281 current_par.vram_half_sam = vram_size / 1024;
1282 current_par.using_vram = 1;
1283 } else if (current_par.dram_size)
1284 size = current_par.dram_size;
1285 else
1286 size = MAX_SIZE;
1287
1288
1289
1290
1291 if (size > MAX_SIZE)
1292 size = MAX_SIZE;
1293
1294 size = PAGE_ALIGN(size);
1295
1296#if defined(HAS_VIDC20)
1297 if (!current_par.using_vram) {
1298 dma_addr_t handle;
1299 void *base;
1300
1301
1302
1303
1304
1305
1306 base = dma_alloc_writecombine(current_par.dev, size, &handle,
1307 GFP_KERNEL);
1308 if (base == NULL) {
1309 printk(KERN_ERR "acornfb: unable to allocate screen "
1310 "memory\n");
1311 return -ENOMEM;
1312 }
1313
1314 fb_info.screen_base = base;
1315 fb_info.fix.smem_start = handle;
1316 }
1317#endif
1318#if defined(HAS_VIDC)
1319
1320
1321
1322
1323 free_unused_pages(PAGE_OFFSET + size, PAGE_OFFSET + MAX_SIZE);
1324#endif
1325
1326 fb_info.fix.smem_len = size;
1327 current_par.palette_size = VIDC_PALETTE_SIZE;
1328
1329
1330
1331
1332
1333
1334 do {
1335 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1336 ARRAY_SIZE(modedb),
1337 &acornfb_default_mode, DEFAULT_BPP);
1338
1339
1340
1341 if (rc == 1)
1342 break;
1343
1344 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1345 &acornfb_default_mode, DEFAULT_BPP);
1346
1347
1348
1349 if (rc == 1)
1350 break;
1351
1352 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1353 ARRAY_SIZE(modedb),
1354 &acornfb_default_mode, DEFAULT_BPP);
1355 if (rc)
1356 break;
1357
1358 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1359 &acornfb_default_mode, DEFAULT_BPP);
1360 } while (0);
1361
1362
1363
1364
1365
1366 if (rc == 0) {
1367 printk("Acornfb: no valid mode found\n");
1368 return -EINVAL;
1369 }
1370
1371 h_sync = 1953125000 / fb_info.var.pixclock;
1372 h_sync = h_sync * 512 / (fb_info.var.xres + fb_info.var.left_margin +
1373 fb_info.var.right_margin + fb_info.var.hsync_len);
1374 v_sync = h_sync / (fb_info.var.yres + fb_info.var.upper_margin +
1375 fb_info.var.lower_margin + fb_info.var.vsync_len);
1376
1377 printk(KERN_INFO "Acornfb: %dkB %cRAM, %s, using %dx%d, "
1378 "%d.%03dkHz, %dHz\n",
1379 fb_info.fix.smem_len / 1024,
1380 current_par.using_vram ? 'V' : 'D',
1381 VIDC_NAME, fb_info.var.xres, fb_info.var.yres,
1382 h_sync / 1000, h_sync % 1000, v_sync);
1383
1384 printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
1385 fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
1386 fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
1387 fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
1388 fb_info.monspecs.dpms ? ", DPMS" : "");
1389
1390 if (fb_set_var(&fb_info, &fb_info.var))
1391 printk(KERN_ERR "Acornfb: unable to set display parameters\n");
1392
1393 if (register_framebuffer(&fb_info) < 0)
1394 return -EINVAL;
1395 return 0;
1396}
1397
1398static struct platform_driver acornfb_driver = {
1399 .probe = acornfb_probe,
1400 .driver = {
1401 .name = "acornfb",
1402 },
1403};
1404
1405static int __init acornfb_init(void)
1406{
1407 return platform_driver_register(&acornfb_driver);
1408}
1409
1410module_init(acornfb_init);
1411
1412MODULE_AUTHOR("Russell King");
1413MODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver");
1414MODULE_LICENSE("GPL");
1415