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