[PATCH] update copyright and licensing
[linux-2.6/history.git] / drivers / video / acornfb.c
blob4ae0cbfbc4e81cd1c8fa499fdd3f0460067451f7
1 /*
2 * linux/drivers/video/acornfb.c
4 * Copyright (C) 1998-2001 Russell King
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * Frame buffer code for Acorn platforms
12 * NOTE: Most of the modes with X!=640 will disappear shortly.
13 * NOTE: Startup setting of HS & VS polarity not supported.
14 * (do we need to support it if we're coming up in 640x480?)
16 * FIXME: (things broken by the "new improved" FBCON API)
17 * - Blanking 8bpp displays with VIDC
20 #include <linux/config.h>
21 #include <linux/module.h>
22 #include <linux/kernel.h>
23 #include <linux/sched.h>
24 #include <linux/errno.h>
25 #include <linux/string.h>
26 #include <linux/ctype.h>
27 #include <linux/mm.h>
28 #include <linux/tty.h>
29 #include <linux/slab.h>
30 #include <linux/init.h>
31 #include <linux/fb.h>
33 #include <asm/hardware.h>
34 #include <asm/io.h>
35 #include <asm/irq.h>
36 #include <asm/mach-types.h>
37 #include <asm/uaccess.h>
39 #include "acornfb.h"
42 * VIDC machines can't do 16 or 32BPP modes.
44 #ifdef HAS_VIDC
45 #undef FBCON_HAS_CFB16
46 #undef FBCON_HAS_CFB32
47 #endif
50 * Default resolution.
51 * NOTE that it has to be supported in the table towards
52 * the end of this file.
54 #define DEFAULT_XRES 640
55 #define DEFAULT_YRES 480
56 #define DEFAULT_BPP 4
59 * define this to debug the video mode selection
61 #undef DEBUG_MODE_SELECTION
64 * Translation from RISC OS monitor types to actual
65 * HSYNC and VSYNC frequency ranges. These are
66 * probably not right, but they're the best info I
67 * have. Allow 1% either way on the nominal for TVs.
69 #define NR_MONTYPES 6
70 static struct fb_monspecs monspecs[NR_MONTYPES] __initdata = {
71 { 15469, 15781, 49, 51, 0 }, /* TV */
72 { 0, 99999, 0, 99, 0 }, /* Multi Freq */
73 { 58608, 58608, 64, 64, 0 }, /* Hi-res mono */
74 { 30000, 70000, 60, 60, 0 }, /* VGA */
75 { 30000, 70000, 56, 75, 0 }, /* SVGA */
76 { 30000, 70000, 60, 60, 0 }
79 static struct fb_info fb_info;
80 static struct acornfb_par current_par;
81 static struct vidc_timing current_vidc;
83 extern unsigned int vram_size; /* set by setup.c */
85 #ifdef HAS_VIDC
87 #define MAX_SIZE 480*1024
89 /* CTL VIDC Actual
90 * 24.000 0 8.000
91 * 25.175 0 8.392
92 * 36.000 0 12.000
93 * 24.000 1 12.000
94 * 25.175 1 12.588
95 * 24.000 2 16.000
96 * 25.175 2 16.783
97 * 36.000 1 18.000
98 * 24.000 3 24.000
99 * 36.000 2 24.000
100 * 25.175 3 25.175
101 * 36.000 3 36.000
103 struct pixclock {
104 u_long min_clock;
105 u_long max_clock;
106 u_int vidc_ctl;
107 u_int vid_ctl;
110 static struct pixclock arc_clocks[] = {
111 /* we allow +/-1% on these */
112 { 123750, 126250, VIDC_CTRL_DIV3, VID_CTL_24MHz }, /* 8.000MHz */
113 { 82500, 84167, VIDC_CTRL_DIV2, VID_CTL_24MHz }, /* 12.000MHz */
114 { 61875, 63125, VIDC_CTRL_DIV1_5, VID_CTL_24MHz }, /* 16.000MHz */
115 { 41250, 42083, VIDC_CTRL_DIV1, VID_CTL_24MHz }, /* 24.000MHz */
118 #ifdef CONFIG_ARCH_A5K
119 static struct pixclock a5k_clocks[] = {
120 { 117974, 120357, VIDC_CTRL_DIV3, VID_CTL_25MHz }, /* 8.392MHz */
121 { 78649, 80238, VIDC_CTRL_DIV2, VID_CTL_25MHz }, /* 12.588MHz */
122 { 58987, 60178, VIDC_CTRL_DIV1_5, VID_CTL_25MHz }, /* 16.588MHz */
123 { 55000, 56111, VIDC_CTRL_DIV2, VID_CTL_36MHz }, /* 18.000MHz */
124 { 39325, 40119, VIDC_CTRL_DIV1, VID_CTL_25MHz }, /* 25.175MHz */
125 { 27500, 28055, VIDC_CTRL_DIV1, VID_CTL_36MHz }, /* 36.000MHz */
127 #endif
129 static struct pixclock *
130 acornfb_valid_pixrate(u_long pixclock)
132 u_int i;
134 for (i = 0; i < ARRAY_SIZE(arc_clocks); i++)
135 if (pixclock > arc_clocks[i].min_clock &&
136 pixclock < arc_clocks[i].max_clock)
137 return arc_clocks + i;
139 #ifdef CONFIG_ARCH_A5K
140 if (machine_is_a5k()) {
141 for (i = 0; i < ARRAY_SIZE(a5k_clocks); i++)
142 if (pixclock > a5k_clocks[i].min_clock &&
143 pixclock < a5k_clocks[i].max_clock)
144 return a5k_clocks + i;
146 #endif
148 return NULL;
151 /* VIDC Rules:
152 * hcr : must be even (interlace, hcr/2 must be even)
153 * hswr : must be even
154 * hdsr : must be odd
155 * hder : must be odd
157 * vcr : must be odd
158 * vswr : >= 1
159 * vdsr : >= 1
160 * vder : >= vdsr
161 * if interlaced, then hcr/2 must be even
163 static void
164 acornfb_set_timing(struct fb_var_screeninfo *var)
166 struct pixclock *pclk;
167 struct vidc_timing vidc;
168 u_int horiz_correction;
169 u_int sync_len, display_start, display_end, cycle;
170 u_int is_interlaced;
171 u_int vid_ctl, vidc_ctl;
172 u_int bandwidth;
174 memset(&vidc, 0, sizeof(vidc));
176 pclk = acornfb_valid_pixrate(var->pixclock);
177 vidc_ctl = pclk->vidc_ctl;
178 vid_ctl = pclk->vid_ctl;
180 bandwidth = var->pixclock * 8 / var->bits_per_pixel;
181 /* 25.175, 4bpp = 79.444ns per byte, 317.776ns per word: fifo = 2,6 */
182 if (bandwidth > 143500)
183 vidc_ctl |= VIDC_CTRL_FIFO_3_7;
184 else if (bandwidth > 71750)
185 vidc_ctl |= VIDC_CTRL_FIFO_2_6;
186 else if (bandwidth > 35875)
187 vidc_ctl |= VIDC_CTRL_FIFO_1_5;
188 else
189 vidc_ctl |= VIDC_CTRL_FIFO_0_4;
191 switch (var->bits_per_pixel) {
192 case 1:
193 horiz_correction = 19;
194 vidc_ctl |= VIDC_CTRL_1BPP;
195 break;
197 case 2:
198 horiz_correction = 11;
199 vidc_ctl |= VIDC_CTRL_2BPP;
200 break;
202 case 4:
203 horiz_correction = 7;
204 vidc_ctl |= VIDC_CTRL_4BPP;
205 break;
207 default:
208 case 8:
209 horiz_correction = 5;
210 vidc_ctl |= VIDC_CTRL_8BPP;
211 break;
214 if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */
215 vidc_ctl |= VIDC_CTRL_CSYNC;
216 else {
217 if (!(var->sync & FB_SYNC_HOR_HIGH_ACT))
218 vid_ctl |= VID_CTL_HS_NHSYNC;
220 if (!(var->sync & FB_SYNC_VERT_HIGH_ACT))
221 vid_ctl |= VID_CTL_VS_NVSYNC;
224 sync_len = var->hsync_len;
225 display_start = sync_len + var->left_margin;
226 display_end = display_start + var->xres;
227 cycle = display_end + var->right_margin;
229 /* if interlaced, then hcr/2 must be even */
230 is_interlaced = (var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED;
232 if (is_interlaced) {
233 vidc_ctl |= VIDC_CTRL_INTERLACE;
234 if (cycle & 2) {
235 cycle += 2;
236 var->right_margin += 2;
240 vidc.h_cycle = (cycle - 2) / 2;
241 vidc.h_sync_width = (sync_len - 2) / 2;
242 vidc.h_border_start = (display_start - 1) / 2;
243 vidc.h_display_start = (display_start - horiz_correction) / 2;
244 vidc.h_display_end = (display_end - horiz_correction) / 2;
245 vidc.h_border_end = (display_end - 1) / 2;
246 vidc.h_interlace = (vidc.h_cycle + 1) / 2;
248 sync_len = var->vsync_len;
249 display_start = sync_len + var->upper_margin;
250 display_end = display_start + var->yres;
251 cycle = display_end + var->lower_margin;
253 if (is_interlaced)
254 cycle = (cycle - 3) / 2;
255 else
256 cycle = cycle - 1;
258 vidc.v_cycle = cycle;
259 vidc.v_sync_width = sync_len - 1;
260 vidc.v_border_start = display_start - 1;
261 vidc.v_display_start = vidc.v_border_start;
262 vidc.v_display_end = display_end - 1;
263 vidc.v_border_end = vidc.v_display_end;
265 if (machine_is_a5k())
266 __raw_writeb(vid_ctl, IOEB_VID_CTL);
268 if (memcmp(&current_vidc, &vidc, sizeof(vidc))) {
269 current_vidc = vidc;
271 vidc_writel(0xe0000000 | vidc_ctl);
272 vidc_writel(0x80000000 | (vidc.h_cycle << 14));
273 vidc_writel(0x84000000 | (vidc.h_sync_width << 14));
274 vidc_writel(0x88000000 | (vidc.h_border_start << 14));
275 vidc_writel(0x8c000000 | (vidc.h_display_start << 14));
276 vidc_writel(0x90000000 | (vidc.h_display_end << 14));
277 vidc_writel(0x94000000 | (vidc.h_border_end << 14));
278 vidc_writel(0x98000000);
279 vidc_writel(0x9c000000 | (vidc.h_interlace << 14));
280 vidc_writel(0xa0000000 | (vidc.v_cycle << 14));
281 vidc_writel(0xa4000000 | (vidc.v_sync_width << 14));
282 vidc_writel(0xa8000000 | (vidc.v_border_start << 14));
283 vidc_writel(0xac000000 | (vidc.v_display_start << 14));
284 vidc_writel(0xb0000000 | (vidc.v_display_end << 14));
285 vidc_writel(0xb4000000 | (vidc.v_border_end << 14));
286 vidc_writel(0xb8000000);
287 vidc_writel(0xbc000000);
289 #ifdef DEBUG_MODE_SELECTION
290 printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,
291 var->yres, var->bits_per_pixel);
292 printk(KERN_DEBUG " H-cycle : %d\n", vidc.h_cycle);
293 printk(KERN_DEBUG " H-sync-width : %d\n", vidc.h_sync_width);
294 printk(KERN_DEBUG " H-border-start : %d\n", vidc.h_border_start);
295 printk(KERN_DEBUG " H-display-start : %d\n", vidc.h_display_start);
296 printk(KERN_DEBUG " H-display-end : %d\n", vidc.h_display_end);
297 printk(KERN_DEBUG " H-border-end : %d\n", vidc.h_border_end);
298 printk(KERN_DEBUG " H-interlace : %d\n", vidc.h_interlace);
299 printk(KERN_DEBUG " V-cycle : %d\n", vidc.v_cycle);
300 printk(KERN_DEBUG " V-sync-width : %d\n", vidc.v_sync_width);
301 printk(KERN_DEBUG " V-border-start : %d\n", vidc.v_border_start);
302 printk(KERN_DEBUG " V-display-start : %d\n", vidc.v_display_start);
303 printk(KERN_DEBUG " V-display-end : %d\n", vidc.v_display_end);
304 printk(KERN_DEBUG " V-border-end : %d\n", vidc.v_border_end);
305 printk(KERN_DEBUG " VIDC Ctrl (E) : 0x%08X\n", vidc_ctl);
306 printk(KERN_DEBUG " IOEB Ctrl : 0x%08X\n", vid_ctl);
307 #endif
310 static int
311 acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
312 u_int trans, struct fb_info *info)
314 union palette pal;
316 if (regno >= current_par.palette_size)
317 return 1;
319 pal.p = 0;
320 pal.vidc.reg = regno;
321 pal.vidc.red = red >> 12;
322 pal.vidc.green = green >> 12;
323 pal.vidc.blue = blue >> 12;
325 current_par.palette[regno] = pal;
327 vidc_writel(pal.p);
329 return 0;
331 #endif
333 #ifdef HAS_VIDC20
334 #include <asm/arch/acornfb.h>
336 #define MAX_SIZE 2*1024*1024
338 /* VIDC20 has a different set of rules from the VIDC:
339 * hcr : must be multiple of 4
340 * hswr : must be even
341 * hdsr : must be even
342 * hder : must be even
343 * vcr : >= 2, (interlace, must be odd)
344 * vswr : >= 1
345 * vdsr : >= 1
346 * vder : >= vdsr
348 static void
349 acornfb_set_timing(struct fb_info *info, struct fb_var_screeninfo *var)
351 struct vidc_timing vidc;
352 u_int vcr, fsize;
353 u_int ext_ctl, dat_ctl;
354 u_int words_per_line;
356 memset(&vidc, 0, sizeof(vidc));
358 vidc.h_sync_width = var->hsync_len - 8;
359 vidc.h_border_start = vidc.h_sync_width + var->left_margin + 8 - 12;
360 vidc.h_display_start = vidc.h_border_start + 12 - 18;
361 vidc.h_display_end = vidc.h_display_start + var->xres;
362 vidc.h_border_end = vidc.h_display_end + 18 - 12;
363 vidc.h_cycle = vidc.h_border_end + var->right_margin + 12 - 8;
364 vidc.h_interlace = vidc.h_cycle / 2;
365 vidc.v_sync_width = var->vsync_len - 1;
366 vidc.v_border_start = vidc.v_sync_width + var->upper_margin;
367 vidc.v_display_start = vidc.v_border_start;
368 vidc.v_display_end = vidc.v_display_start + var->yres;
369 vidc.v_border_end = vidc.v_display_end;
370 vidc.control = acornfb_default_control();
372 vcr = var->vsync_len + var->upper_margin + var->yres +
373 var->lower_margin;
375 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
376 vidc.v_cycle = (vcr - 3) / 2;
377 vidc.control |= VIDC20_CTRL_INT;
378 } else
379 vidc.v_cycle = vcr - 2;
381 switch (var->bits_per_pixel) {
382 case 1: vidc.control |= VIDC20_CTRL_1BPP; break;
383 case 2: vidc.control |= VIDC20_CTRL_2BPP; break;
384 case 4: vidc.control |= VIDC20_CTRL_4BPP; break;
385 default:
386 case 8: vidc.control |= VIDC20_CTRL_8BPP; break;
387 case 16: vidc.control |= VIDC20_CTRL_16BPP; break;
388 case 32: vidc.control |= VIDC20_CTRL_32BPP; break;
391 acornfb_vidc20_find_rates(&vidc, var);
392 fsize = var->vsync_len + var->upper_margin + var->lower_margin - 1;
394 if (memcmp(&current_vidc, &vidc, sizeof(vidc))) {
395 current_vidc = vidc;
397 vidc_writel(VIDC20_CTRL| vidc.control);
398 vidc_writel(0xd0000000 | vidc.pll_ctl);
399 vidc_writel(0x80000000 | vidc.h_cycle);
400 vidc_writel(0x81000000 | vidc.h_sync_width);
401 vidc_writel(0x82000000 | vidc.h_border_start);
402 vidc_writel(0x83000000 | vidc.h_display_start);
403 vidc_writel(0x84000000 | vidc.h_display_end);
404 vidc_writel(0x85000000 | vidc.h_border_end);
405 vidc_writel(0x86000000);
406 vidc_writel(0x87000000 | vidc.h_interlace);
407 vidc_writel(0x90000000 | vidc.v_cycle);
408 vidc_writel(0x91000000 | vidc.v_sync_width);
409 vidc_writel(0x92000000 | vidc.v_border_start);
410 vidc_writel(0x93000000 | vidc.v_display_start);
411 vidc_writel(0x94000000 | vidc.v_display_end);
412 vidc_writel(0x95000000 | vidc.v_border_end);
413 vidc_writel(0x96000000);
414 vidc_writel(0x97000000);
417 iomd_writel(fsize, IOMD_FSIZE);
419 ext_ctl = acornfb_default_econtrol();
421 if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */
422 ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC;
423 else {
424 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
425 ext_ctl |= VIDC20_ECTL_HS_HSYNC;
426 else
427 ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
429 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
430 ext_ctl |= VIDC20_ECTL_VS_VSYNC;
431 else
432 ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
435 vidc_writel(VIDC20_ECTL | ext_ctl);
437 words_per_line = var->xres * var->bits_per_pixel / 32;
439 if (current_par.using_vram && info->fix.smem_len == 2048*1024)
440 words_per_line /= 2;
442 /* RiscPC doesn't use the VIDC's VRAM control. */
443 dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line;
445 /* The data bus width is dependent on both the type
446 * and amount of video memory.
447 * DRAM 32bit low
448 * 1MB VRAM 32bit
449 * 2MB VRAM 64bit
451 if (current_par.using_vram && current_par.vram_half_sam == 2048) {
452 dat_ctl |= VIDC20_DCTL_BUS_D63_0;
453 } else
454 dat_ctl |= VIDC20_DCTL_BUS_D31_0;
456 vidc_writel(VIDC20_DCTL | dat_ctl);
458 #ifdef DEBUG_MODE_SELECTION
459 printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,
460 var->yres, var->bits_per_pixel);
461 printk(KERN_DEBUG " H-cycle : %d\n", vidc.h_cycle);
462 printk(KERN_DEBUG " H-sync-width : %d\n", vidc.h_sync_width);
463 printk(KERN_DEBUG " H-border-start : %d\n", vidc.h_border_start);
464 printk(KERN_DEBUG " H-display-start : %d\n", vidc.h_display_start);
465 printk(KERN_DEBUG " H-display-end : %d\n", vidc.h_display_end);
466 printk(KERN_DEBUG " H-border-end : %d\n", vidc.h_border_end);
467 printk(KERN_DEBUG " H-interlace : %d\n", vidc.h_interlace);
468 printk(KERN_DEBUG " V-cycle : %d\n", vidc.v_cycle);
469 printk(KERN_DEBUG " V-sync-width : %d\n", vidc.v_sync_width);
470 printk(KERN_DEBUG " V-border-start : %d\n", vidc.v_border_start);
471 printk(KERN_DEBUG " V-display-start : %d\n", vidc.v_display_start);
472 printk(KERN_DEBUG " V-display-end : %d\n", vidc.v_display_end);
473 printk(KERN_DEBUG " V-border-end : %d\n", vidc.v_border_end);
474 printk(KERN_DEBUG " Ext Ctrl (C) : 0x%08X\n", ext_ctl);
475 printk(KERN_DEBUG " PLL Ctrl (D) : 0x%08X\n", vidc.pll_ctl);
476 printk(KERN_DEBUG " Ctrl (E) : 0x%08X\n", vidc.control);
477 printk(KERN_DEBUG " Data Ctrl (F) : 0x%08X\n", dat_ctl);
478 printk(KERN_DEBUG " Fsize : 0x%08X\n", fsize);
479 #endif
483 * We have to take note of the VIDC20's 16-bit palette here.
484 * The VIDC20 looks up a 16 bit pixel as follows:
486 * bits 111111
487 * 5432109876543210
488 * red ++++++++ (8 bits, 7 to 0)
489 * green ++++++++ (8 bits, 11 to 4)
490 * blue ++++++++ (8 bits, 15 to 8)
492 * We use a pixel which looks like:
494 * bits 111111
495 * 5432109876543210
496 * red +++++ (5 bits, 4 to 0)
497 * green +++++ (5 bits, 9 to 5)
498 * blue +++++ (5 bits, 14 to 10)
500 static int
501 acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
502 u_int trans, struct fb_info *info)
504 union palette pal;
505 int bpp = info->var.bits_per_pixel;
507 if (regno >= current_par.palette_size)
508 return 1;
510 pal.p = 0;
511 pal.vidc20.red = red >> 8;
512 pal.vidc20.green = green >> 8;
513 pal.vidc20.blue = blue >> 8;
515 current_par.palette[regno] = pal;
517 if (bpp == 32 && regno < 16) {
518 current_par.cmap.cfb32[regno] =
519 regno | regno << 8 | regno << 16;
521 if (bpp == 16 && regno < 16) {
522 int i;
524 current_par.cmap.cfb16[regno] =
525 regno | regno << 5 | regno << 10;
527 pal.p = 0;
528 vidc_writel(0x10000000);
529 for (i = 0; i < 256; i += 1) {
530 pal.vidc20.red = current_par.palette[ i & 31].vidc20.red;
531 pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
532 pal.vidc20.blue = current_par.palette[(i >> 2) & 31].vidc20.blue;
533 vidc_writel(pal.p);
534 /* Palette register pointer auto-increments */
536 } else {
537 vidc_writel(0x10000000 | regno);
538 vidc_writel(pal.p);
541 return 0;
543 #endif
546 * Before selecting the timing parameters, adjust
547 * the resolution to fit the rules.
549 static int
550 acornfb_adjust_timing(struct fb_info *info, struct fb_var_screeninfo *var, u_int fontht)
552 u_int font_line_len, sam_size, min_size, size, nr_y;
554 /* xres must be even */
555 var->xres = (var->xres + 1) & ~1;
558 * We don't allow xres_virtual to differ from xres
560 var->xres_virtual = var->xres;
561 var->xoffset = 0;
563 if (current_par.using_vram)
564 sam_size = current_par.vram_half_sam * 2;
565 else
566 sam_size = 16;
569 * Now, find a value for yres_virtual which allows
570 * us to do ywrap scrolling. The value of
571 * yres_virtual must be such that the end of the
572 * displayable frame buffer must be aligned with
573 * the start of a font line.
575 font_line_len = var->xres * var->bits_per_pixel * fontht / 8;
576 min_size = var->xres * var->yres * var->bits_per_pixel / 8;
579 * If minimum screen size is greater than that we have
580 * available, reject it.
582 if (min_size > info->fix.smem_len)
583 return -EINVAL;
585 /* Find int 'y', such that y * fll == s * sam < maxsize
586 * y = s * sam / fll; s = maxsize / sam
588 for (size = info->fix.smem_len;
589 nr_y = size / font_line_len, min_size <= size;
590 size -= sam_size) {
591 if (nr_y * font_line_len == size)
592 break;
594 nr_y *= fontht;
596 if (var->accel_flags & FB_ACCELF_TEXT) {
597 if (min_size > size) {
599 * failed, use ypan
601 size = info->fix.smem_len;
602 var->yres_virtual = size / (font_line_len / fontht);
603 } else
604 var->yres_virtual = nr_y;
605 } else if (var->yres_virtual > nr_y)
606 var->yres_virtual = nr_y;
608 current_par.screen_end = info->fix.smem_start + size;
611 * Fix yres & yoffset if needed.
613 if (var->yres > var->yres_virtual)
614 var->yres = var->yres_virtual;
616 if (var->vmode & FB_VMODE_YWRAP) {
617 if (var->yoffset > var->yres_virtual)
618 var->yoffset = var->yres_virtual;
619 } else {
620 if (var->yoffset + var->yres > var->yres_virtual)
621 var->yoffset = var->yres_virtual - var->yres;
624 /* hsync_len must be even */
625 var->hsync_len = (var->hsync_len + 1) & ~1;
627 #ifdef HAS_VIDC
628 /* left_margin must be odd */
629 if ((var->left_margin & 1) == 0) {
630 var->left_margin -= 1;
631 var->right_margin += 1;
634 /* right_margin must be odd */
635 var->right_margin |= 1;
636 #elif defined(HAS_VIDC20)
637 /* left_margin must be even */
638 if (var->left_margin & 1) {
639 var->left_margin += 1;
640 var->right_margin -= 1;
643 /* right_margin must be even */
644 if (var->right_margin & 1)
645 var->right_margin += 1;
646 #endif
648 if (var->vsync_len < 1)
649 var->vsync_len = 1;
651 return 0;
654 static int
655 acornfb_validate_timing(struct fb_var_screeninfo *var,
656 struct fb_monspecs *monspecs)
658 unsigned long hs, vs;
661 * hs(Hz) = 10^12 / (pixclock * xtotal)
662 * vs(Hz) = hs(Hz) / ytotal
664 * No need to do long long divisions or anything
665 * like that if you factor it correctly
667 hs = 1953125000 / var->pixclock;
668 hs = hs * 512 /
669 (var->xres + var->left_margin + var->right_margin + var->hsync_len);
670 vs = hs /
671 (var->yres + var->upper_margin + var->lower_margin + var->vsync_len);
673 return (vs >= monspecs->vfmin && vs <= monspecs->vfmax &&
674 hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL;
677 static inline void
678 acornfb_update_dma(struct fb_info *info, struct fb_var_screeninfo *var)
680 u_int off = (var->yoffset * var->xres_virtual *
681 var->bits_per_pixel) >> 3;
683 #if defined(HAS_MEMC)
684 memc_write(VDMA_INIT, off >> 2);
685 #elif defined(HAS_IOMD)
686 iomd_writel(info->fix.smem_start + off, IOMD_VIDINIT);
687 #endif
690 static int
691 acornfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
693 u_int fontht;
694 int err;
697 * FIXME: Find the font height
699 fontht = 8;
701 switch (var->bits_per_pixel) {
702 case 1: case 2: case 4: case 8:
703 var->red.offset = 0;
704 var->red.length = var->bits_per_pixel;
705 var->green = var->red;
706 var->blue = var->red;
707 var->transp.offset = 0;
708 var->transp.length = 0;
709 break;
711 #ifdef HAS_VIDC20
712 case 16:
713 var->red.offset = 0;
714 var->red.length = 5;
715 var->green.offset = 5;
716 var->green.length = 5;
717 var->blue.offset = 10;
718 var->blue.length = 5;
719 var->transp.offset = 15;
720 var->transp.length = 1;
721 break;
723 case 32:
724 var->red.offset = 0;
725 var->red.length = 8;
726 var->green.offset = 8;
727 var->green.length = 8;
728 var->blue.offset = 16;
729 var->blue.length = 8;
730 var->transp.offset = 24;
731 var->transp.length = 4;
732 break;
733 #endif
734 default:
735 return -EINVAL;
739 * Check to see if the pixel rate is valid.
741 if (!var->pixclock || !acornfb_valid_pixrate(var->pixclock))
742 return -EINVAL;
745 * Validate and adjust the resolution to
746 * match the video generator hardware.
748 err = acornfb_adjust_timing(info, var, fontht);
749 if (err)
750 return err;
753 * Validate the timing against the
754 * monitor hardware.
756 return acornfb_validate_timing(var, &info->monspecs);
759 static int acornfb_set_par(struct fb_info *info)
761 switch (info->var.bits_per_pixel) {
762 case 1:
763 current_par.palette_size = 2;
764 info->fix.visual = FB_VISUAL_MONO10;
765 break;
766 case 2:
767 current_par.palette_size = 4;
768 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
769 break;
770 case 4:
771 current_par.palette_size = 16;
772 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
773 break;
774 case 8:
775 current_par.palette_size = VIDC_PALETTE_SIZE;
776 #ifdef HAS_VIDC
777 info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
778 #else
779 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
780 #endif
781 break;
782 #ifdef HAS_VIDC20
783 case 16:
784 current_par.palette_size = 32;
785 info->pseudo_palette = current_par.cmap.cfb16;
786 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
787 break;
788 case 32:
789 current_par.palette_size = VIDC_PALETTE_SIZE;
790 info->pseudo_palette = current_par.cmap.cfb32;
791 info->fix.visual = FB_VISUAL_TRUECOLOR;
792 break;
793 #endif
794 default:
795 BUG();
798 info->fix.line_length = (info->var.xres * info->var.bits_per_pixel) / 8;
800 #if defined(HAS_MEMC)
802 unsigned long size = info->fix.smem_len - VDMA_XFERSIZE;
804 memc_write(VDMA_START, 0);
805 memc_write(VDMA_END, size >> 2);
807 #elif defined(HAS_IOMD)
809 unsigned long start, size;
810 u_int control;
812 start = info->fix.smem_start;
813 size = current_par.screen_end;
815 if (current_par.using_vram) {
816 size -= current_par.vram_half_sam;
817 control = DMA_CR_E | (current_par.vram_half_sam / 256);
818 } else {
819 size -= 16;
820 control = DMA_CR_E | DMA_CR_D | 16;
823 iomd_writel(start, IOMD_VIDSTART);
824 iomd_writel(size, IOMD_VIDEND);
825 iomd_writel(control, IOMD_VIDCR);
827 #endif
829 acornfb_update_dma(info, &info->var);
830 acornfb_set_timing(info, &info->var);
832 return 0;
835 static int
836 acornfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
838 u_int y_bottom = var->yoffset;
840 if (!(var->vmode & FB_VMODE_YWRAP))
841 y_bottom += var->yres;
843 BUG_ON(y_bottom > var->yres_virtual);
845 acornfb_update_dma(info, var);
847 return 0;
851 * Note that we are entered with the kernel locked.
853 static int
854 acornfb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
856 unsigned long off, start;
857 u32 len;
859 off = vma->vm_pgoff << PAGE_SHIFT;
861 start = info->fix.smem_start;
862 len = PAGE_ALIGN(start & ~PAGE_MASK) + info->fix.smem_len;
863 start &= PAGE_MASK;
864 if ((vma->vm_end - vma->vm_start + off) > len)
865 return -EINVAL;
866 off += start;
867 vma->vm_pgoff = off >> PAGE_SHIFT;
869 /* This is an IO map - tell maydump to skip this VMA */
870 vma->vm_flags |= VM_IO;
872 #ifdef CONFIG_CPU_32
873 pgprot_val(vma->vm_page_prot) &= ~L_PTE_CACHEABLE;
874 #endif
877 * Don't alter the page protection flags; we want to keep the area
878 * cached for better performance. This does mean that we may miss
879 * some updates to the screen occasionally, but process switches
880 * should cause the caches and buffers to be flushed often enough.
882 if (io_remap_page_range(vma, vma->vm_start, off,
883 vma->vm_end - vma->vm_start,
884 vma->vm_page_prot))
885 return -EAGAIN;
886 return 0;
889 static struct fb_ops acornfb_ops = {
890 .owner = THIS_MODULE,
891 .fb_check_var = acornfb_check_var,
892 .fb_set_par = acornfb_set_par,
893 .fb_setcolreg = acornfb_setcolreg,
894 .fb_pan_display = acornfb_pan_display,
895 .fb_fillrect = cfb_fillrect,
896 .fb_copyarea = cfb_copyarea,
897 .fb_imageblit = cfb_imageblit,
898 .fb_mmap = acornfb_mmap,
899 .fb_cursor = soft_cursor,
903 * Everything after here is initialisation!!!
905 static struct fb_videomode modedb[] __initdata = {
906 { /* 320x256 @ 50Hz */
907 NULL, 50, 320, 256, 125000, 92, 62, 35, 19, 38, 2,
908 FB_SYNC_COMP_HIGH_ACT,
909 FB_VMODE_NONINTERLACED
910 }, { /* 640x250 @ 50Hz, 15.6 kHz hsync */
911 NULL, 50, 640, 250, 62500, 185, 123, 38, 21, 76, 3,
913 FB_VMODE_NONINTERLACED
914 }, { /* 640x256 @ 50Hz, 15.6 kHz hsync */
915 NULL, 50, 640, 256, 62500, 185, 123, 35, 18, 76, 3,
917 FB_VMODE_NONINTERLACED
918 }, { /* 640x512 @ 50Hz, 26.8 kHz hsync */
919 NULL, 50, 640, 512, 41667, 113, 87, 18, 1, 56, 3,
921 FB_VMODE_NONINTERLACED
922 }, { /* 640x250 @ 70Hz, 31.5 kHz hsync */
923 NULL, 70, 640, 250, 39722, 48, 16, 109, 88, 96, 2,
925 FB_VMODE_NONINTERLACED
926 }, { /* 640x256 @ 70Hz, 31.5 kHz hsync */
927 NULL, 70, 640, 256, 39722, 48, 16, 106, 85, 96, 2,
929 FB_VMODE_NONINTERLACED
930 }, { /* 640x352 @ 70Hz, 31.5 kHz hsync */
931 NULL, 70, 640, 352, 39722, 48, 16, 58, 37, 96, 2,
933 FB_VMODE_NONINTERLACED
934 }, { /* 640x480 @ 60Hz, 31.5 kHz hsync */
935 NULL, 60, 640, 480, 39722, 48, 16, 32, 11, 96, 2,
937 FB_VMODE_NONINTERLACED
938 }, { /* 800x600 @ 56Hz, 35.2 kHz hsync */
939 NULL, 56, 800, 600, 27778, 101, 23, 22, 1, 100, 2,
941 FB_VMODE_NONINTERLACED
942 }, { /* 896x352 @ 60Hz, 21.8 kHz hsync */
943 NULL, 60, 896, 352, 41667, 59, 27, 9, 0, 118, 3,
945 FB_VMODE_NONINTERLACED
946 }, { /* 1024x 768 @ 60Hz, 48.4 kHz hsync */
947 NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
949 FB_VMODE_NONINTERLACED
950 }, { /* 1280x1024 @ 60Hz, 63.8 kHz hsync */
951 NULL, 60, 1280, 1024, 9090, 186, 96, 38, 1, 160, 3,
953 FB_VMODE_NONINTERLACED
957 static struct fb_videomode __initdata
958 acornfb_default_mode = {
959 .name = NULL,
960 .refresh = 60,
961 .xres = 640,
962 .yres = 480,
963 .pixclock = 39722,
964 .left_margin = 56,
965 .right_margin = 16,
966 .upper_margin = 34,
967 .lower_margin = 9,
968 .hsync_len = 88,
969 .vsync_len = 2,
970 .sync = 0,
971 .vmode = FB_VMODE_NONINTERLACED
974 static void __init acornfb_init_fbinfo(void)
976 static int first = 1;
978 if (!first)
979 return;
980 first = 0;
982 fb_info.fbops = &acornfb_ops;
983 fb_info.flags = FBINFO_FLAG_DEFAULT;
985 strcpy(fb_info.fix.id, "Acorn");
986 fb_info.fix.type = FB_TYPE_PACKED_PIXELS;
987 fb_info.fix.type_aux = 0;
988 fb_info.fix.xpanstep = 0;
989 fb_info.fix.ypanstep = 1;
990 fb_info.fix.ywrapstep = 1;
991 fb_info.fix.line_length = 0;
992 fb_info.fix.accel = FB_ACCEL_NONE;
995 * setup initial parameters
997 memset(&fb_info.var, 0, sizeof(fb_info.var));
999 #if defined(HAS_VIDC20)
1000 fb_info.var.red.length = 8;
1001 fb_info.var.transp.length = 4;
1002 #elif defined(HAS_VIDC)
1003 fb_info.var.red.length = 4;
1004 fb_info.var.transp.length = 1;
1005 #endif
1006 fb_info.var.green = fb_info.var.red;
1007 fb_info.var.blue = fb_info.var.red;
1008 fb_info.var.nonstd = 0;
1009 fb_info.var.activate = FB_ACTIVATE_NOW;
1010 fb_info.var.height = -1;
1011 fb_info.var.width = -1;
1012 fb_info.var.vmode = FB_VMODE_NONINTERLACED;
1013 fb_info.var.accel_flags = FB_ACCELF_TEXT;
1015 current_par.dram_size = 0;
1016 current_par.montype = -1;
1017 current_par.dpms = 0;
1021 * setup acornfb options:
1023 * mon:hmin-hmax:vmin-vmax:dpms:width:height
1024 * Set monitor parameters:
1025 * hmin = horizontal minimum frequency (Hz)
1026 * hmax = horizontal maximum frequency (Hz) (optional)
1027 * vmin = vertical minimum frequency (Hz)
1028 * vmax = vertical maximum frequency (Hz) (optional)
1029 * dpms = DPMS supported? (optional)
1030 * width = width of picture in mm. (optional)
1031 * height = height of picture in mm. (optional)
1033 * montype:type
1034 * Set RISC-OS style monitor type:
1035 * 0 (or tv) - TV frequency
1036 * 1 (or multi) - Multi frequency
1037 * 2 (or hires) - Hi-res monochrome
1038 * 3 (or vga) - VGA
1039 * 4 (or svga) - SVGA
1040 * auto, or option missing
1041 * - try hardware detect
1043 * dram:size
1044 * Set the amount of DRAM to use for the frame buffer
1045 * (even if you have VRAM).
1046 * size can optionally be followed by 'M' or 'K' for
1047 * MB or KB respectively.
1049 static void __init
1050 acornfb_parse_mon(char *opt)
1052 char *p = opt;
1054 current_par.montype = -2;
1056 fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
1057 if (*p == '-')
1058 fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
1059 else
1060 fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
1062 if (*p != ':')
1063 goto bad;
1065 fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
1066 if (*p == '-')
1067 fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
1068 else
1069 fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
1071 if (*p != ':')
1072 goto check_values;
1074 fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
1076 if (*p != ':')
1077 goto check_values;
1079 fb_info.var.width = simple_strtoul(p + 1, &p, 0);
1081 if (*p != ':')
1082 goto check_values;
1084 fb_info.var.height = simple_strtoul(p + 1, NULL, 0);
1086 check_values:
1087 if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
1088 fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
1089 goto bad;
1090 return;
1092 bad:
1093 printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt);
1094 current_par.montype = -1;
1097 static void __init
1098 acornfb_parse_montype(char *opt)
1100 current_par.montype = -2;
1102 if (strncmp(opt, "tv", 2) == 0) {
1103 opt += 2;
1104 current_par.montype = 0;
1105 } else if (strncmp(opt, "multi", 5) == 0) {
1106 opt += 5;
1107 current_par.montype = 1;
1108 } else if (strncmp(opt, "hires", 5) == 0) {
1109 opt += 5;
1110 current_par.montype = 2;
1111 } else if (strncmp(opt, "vga", 3) == 0) {
1112 opt += 3;
1113 current_par.montype = 3;
1114 } else if (strncmp(opt, "svga", 4) == 0) {
1115 opt += 4;
1116 current_par.montype = 4;
1117 } else if (strncmp(opt, "auto", 4) == 0) {
1118 opt += 4;
1119 current_par.montype = -1;
1120 } else if (isdigit(*opt))
1121 current_par.montype = simple_strtoul(opt, &opt, 0);
1123 if (current_par.montype == -2 ||
1124 current_par.montype > NR_MONTYPES) {
1125 printk(KERN_ERR "acornfb: unknown monitor type: %s\n",
1126 opt);
1127 current_par.montype = -1;
1128 } else
1129 if (opt && *opt) {
1130 if (strcmp(opt, ",dpms") == 0)
1131 current_par.dpms = 1;
1132 else
1133 printk(KERN_ERR
1134 "acornfb: unknown monitor option: %s\n",
1135 opt);
1139 static void __init
1140 acornfb_parse_dram(char *opt)
1142 unsigned int size;
1144 size = simple_strtoul(opt, &opt, 0);
1146 if (opt) {
1147 switch (*opt) {
1148 case 'M':
1149 case 'm':
1150 size *= 1024;
1151 case 'K':
1152 case 'k':
1153 size *= 1024;
1154 default:
1155 break;
1159 current_par.dram_size = size;
1162 static struct options {
1163 char *name;
1164 void (*parse)(char *opt);
1165 } opt_table[] __initdata = {
1166 { "mon", acornfb_parse_mon },
1167 { "montype", acornfb_parse_montype },
1168 { "dram", acornfb_parse_dram },
1169 { NULL, NULL }
1172 int __init
1173 acornfb_setup(char *options)
1175 struct options *optp;
1176 char *opt;
1178 if (!options || !*options)
1179 return 0;
1181 acornfb_init_fbinfo();
1183 while ((opt = strsep(&options, ",")) != NULL) {
1184 if (!*opt)
1185 continue;
1187 for (optp = opt_table; optp->name; optp++) {
1188 int optlen;
1190 optlen = strlen(optp->name);
1192 if (strncmp(opt, optp->name, optlen) == 0 &&
1193 opt[optlen] == ':') {
1194 optp->parse(opt + optlen + 1);
1195 break;
1199 if (!optp->name)
1200 printk(KERN_ERR "acornfb: unknown parameter: %s\n",
1201 opt);
1203 return 0;
1207 * Detect type of monitor connected
1208 * For now, we just assume SVGA
1210 static int __init
1211 acornfb_detect_monitortype(void)
1213 return 4;
1217 * This enables the unused memory to be freed on older Acorn machines.
1219 static inline void
1220 free_unused_pages(unsigned int virtual_start, unsigned int virtual_end)
1222 int mb_freed = 0;
1225 * Align addresses
1227 virtual_start = PAGE_ALIGN(virtual_start);
1228 virtual_end = PAGE_ALIGN(virtual_end);
1230 while (virtual_start < virtual_end) {
1231 struct page *page;
1234 * Clear page reserved bit,
1235 * set count to 1, and free
1236 * the page.
1238 page = virt_to_page(virtual_start);
1239 ClearPageReserved(page);
1240 atomic_set(&page->count, 1);
1241 free_page(virtual_start);
1243 virtual_start += PAGE_SIZE;
1244 mb_freed += PAGE_SIZE / 1024;
1247 printk("acornfb: freed %dK memory\n", mb_freed);
1250 int __init
1251 acornfb_init(void)
1253 unsigned long size;
1254 u_int h_sync, v_sync;
1255 int rc, i;
1257 acornfb_init_fbinfo();
1259 if (current_par.montype == -1)
1260 current_par.montype = acornfb_detect_monitortype();
1262 if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
1263 current_par.montype = 4;
1265 if (current_par.montype >= 0) {
1266 fb_info.monspecs = monspecs[current_par.montype];
1267 fb_info.monspecs.dpms = current_par.dpms;
1271 * Try to select a suitable default mode
1273 for (i = 0; i < sizeof(modedb) / sizeof(*modedb); i++) {
1274 unsigned long hs;
1276 hs = modedb[i].refresh *
1277 (modedb[i].yres + modedb[i].upper_margin +
1278 modedb[i].lower_margin + modedb[i].vsync_len);
1279 if (modedb[i].xres == DEFAULT_XRES &&
1280 modedb[i].yres == DEFAULT_YRES &&
1281 modedb[i].refresh >= fb_info.monspecs.vfmin &&
1282 modedb[i].refresh <= fb_info.monspecs.vfmax &&
1283 hs >= fb_info.monspecs.hfmin &&
1284 hs <= fb_info.monspecs.hfmax) {
1285 acornfb_default_mode = modedb[i];
1286 break;
1290 fb_info.currcon = -1;
1291 fb_info.screen_base = (char *)SCREEN_BASE;
1292 fb_info.fix.smem_start = SCREEN_START;
1293 current_par.using_vram = 0;
1296 * If vram_size is set, we are using VRAM in
1297 * a Risc PC. However, if the user has specified
1298 * an amount of DRAM then use that instead.
1300 if (vram_size && !current_par.dram_size) {
1301 size = vram_size;
1302 current_par.vram_half_sam = vram_size / 1024;
1303 current_par.using_vram = 1;
1304 } else if (current_par.dram_size)
1305 size = current_par.dram_size;
1306 else
1307 size = MAX_SIZE;
1310 * Limit maximum screen size.
1312 if (size > MAX_SIZE)
1313 size = MAX_SIZE;
1315 size = PAGE_ALIGN(size);
1317 #if defined(HAS_VIDC20)
1318 if (!current_par.using_vram) {
1320 * RiscPC needs to allocate the DRAM memory
1321 * for the framebuffer if we are not using
1322 * VRAM. Archimedes/A5000 machines use a
1323 * fixed address for their framebuffers.
1325 unsigned long page, top, base;
1326 int order = get_order(size);
1328 base = __get_free_pages(GFP_KERNEL, order);
1329 if (base == 0) {
1330 printk(KERN_ERR "acornfb: unable to allocate screen "
1331 "memory\n");
1332 return -ENOMEM;
1334 top = base + (PAGE_SIZE << order);
1336 /* Mark the framebuffer pages as reserved so mmap will work. */
1337 for (page = base; page < PAGE_ALIGN(base + size); page += PAGE_SIZE)
1338 SetPageReserved(virt_to_page(page));
1339 /* Hand back any excess pages that we allocated. */
1340 for (page = base + size; page < top; page += PAGE_SIZE)
1341 free_page(page);
1343 fb_info.screen_base = (char *)base;
1344 fb_info.fix.smem_start = virt_to_phys(fb_info.screen_base);
1346 #endif
1347 #if defined(HAS_VIDC)
1349 * Free unused pages
1351 free_unused_pages(PAGE_OFFSET + size, PAGE_OFFSET + MAX_SIZE);
1352 #endif
1354 fb_info.fix.smem_len = size;
1355 current_par.palette_size = VIDC_PALETTE_SIZE;
1358 * Lookup the timing for this resolution. If we can't
1359 * find it, then we can't restore it if we change
1360 * the resolution, so we disable this feature.
1362 do {
1363 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1364 sizeof(modedb) / sizeof(*modedb),
1365 &acornfb_default_mode, DEFAULT_BPP);
1367 * If we found an exact match, all ok.
1369 if (rc == 1)
1370 break;
1372 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1373 &acornfb_default_mode, DEFAULT_BPP);
1375 * If we found an exact match, all ok.
1377 if (rc == 1)
1378 break;
1380 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1381 sizeof(modedb) / sizeof(*modedb),
1382 &acornfb_default_mode, DEFAULT_BPP);
1383 if (rc)
1384 break;
1386 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1387 &acornfb_default_mode, DEFAULT_BPP);
1388 } while (0);
1391 * If we didn't find an exact match, try the
1392 * generic database.
1394 if (rc == 0) {
1395 printk("Acornfb: no valid mode found\n");
1396 return -EINVAL;
1399 h_sync = 1953125000 / fb_info.var.pixclock;
1400 h_sync = h_sync * 512 / (fb_info.var.xres + fb_info.var.left_margin +
1401 fb_info.var.right_margin + fb_info.var.hsync_len);
1402 v_sync = h_sync / (fb_info.var.yres + fb_info.var.upper_margin +
1403 fb_info.var.lower_margin + fb_info.var.vsync_len);
1405 printk(KERN_INFO "Acornfb: %dkB %cRAM, %s, using %dx%d, "
1406 "%d.%03dkHz, %dHz\n",
1407 fb_info.fix.smem_len / 1024,
1408 current_par.using_vram ? 'V' : 'D',
1409 VIDC_NAME, fb_info.var.xres, fb_info.var.yres,
1410 h_sync / 1000, h_sync % 1000, v_sync);
1412 printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
1413 fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
1414 fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
1415 fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
1416 fb_info.monspecs.dpms ? ", DPMS" : "");
1418 if (fb_set_var(&fb_info.var, &fb_info))
1419 printk(KERN_ERR "Acornfb: unable to set display parameters\n");
1421 if (register_framebuffer(&fb_info) < 0)
1422 return -EINVAL;
1423 return 0;
1426 MODULE_AUTHOR("Russell King");
1427 MODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver");
1428 MODULE_LICENSE("GPL");