diff options
Diffstat (limited to 'drivers/video/fbdev/acornfb.c')
-rw-r--r-- | drivers/video/fbdev/acornfb.c | 1143 |
1 files changed, 1143 insertions, 0 deletions
diff --git a/drivers/video/fbdev/acornfb.c b/drivers/video/fbdev/acornfb.c new file mode 100644 index 000000000000..a305caea58ee --- /dev/null +++ b/drivers/video/fbdev/acornfb.c | |||
@@ -0,0 +1,1143 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/acornfb.c | ||
3 | * | ||
4 | * Copyright (C) 1998-2001 Russell King | ||
5 | * | ||
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. | ||
9 | * | ||
10 | * Frame buffer code for Acorn platforms | ||
11 | * | ||
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?) | ||
15 | * | ||
16 | * FIXME: (things broken by the "new improved" FBCON API) | ||
17 | * - Blanking 8bpp displays with VIDC | ||
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 | * Default resolution. | ||
42 | * NOTE that it has to be supported in the table towards | ||
43 | * the end of this file. | ||
44 | */ | ||
45 | #define DEFAULT_XRES 640 | ||
46 | #define DEFAULT_YRES 480 | ||
47 | #define DEFAULT_BPP 4 | ||
48 | |||
49 | /* | ||
50 | * define this to debug the video mode selection | ||
51 | */ | ||
52 | #undef DEBUG_MODE_SELECTION | ||
53 | |||
54 | /* | ||
55 | * Translation from RISC OS monitor types to actual | ||
56 | * HSYNC and VSYNC frequency ranges. These are | ||
57 | * probably not right, but they're the best info I | ||
58 | * have. Allow 1% either way on the nominal for TVs. | ||
59 | */ | ||
60 | #define NR_MONTYPES 6 | ||
61 | static struct fb_monspecs monspecs[NR_MONTYPES] = { | ||
62 | { /* TV */ | ||
63 | .hfmin = 15469, | ||
64 | .hfmax = 15781, | ||
65 | .vfmin = 49, | ||
66 | .vfmax = 51, | ||
67 | }, { /* Multi Freq */ | ||
68 | .hfmin = 0, | ||
69 | .hfmax = 99999, | ||
70 | .vfmin = 0, | ||
71 | .vfmax = 199, | ||
72 | }, { /* Hi-res mono */ | ||
73 | .hfmin = 58608, | ||
74 | .hfmax = 58608, | ||
75 | .vfmin = 64, | ||
76 | .vfmax = 64, | ||
77 | }, { /* VGA */ | ||
78 | .hfmin = 30000, | ||
79 | .hfmax = 70000, | ||
80 | .vfmin = 60, | ||
81 | .vfmax = 60, | ||
82 | }, { /* SVGA */ | ||
83 | .hfmin = 30000, | ||
84 | .hfmax = 70000, | ||
85 | .vfmin = 56, | ||
86 | .vfmax = 75, | ||
87 | }, { | ||
88 | .hfmin = 30000, | ||
89 | .hfmax = 70000, | ||
90 | .vfmin = 60, | ||
91 | .vfmax = 60, | ||
92 | } | ||
93 | }; | ||
94 | |||
95 | static struct fb_info fb_info; | ||
96 | static struct acornfb_par current_par; | ||
97 | static struct vidc_timing current_vidc; | ||
98 | |||
99 | extern unsigned int vram_size; /* set by setup.c */ | ||
100 | |||
101 | #ifdef HAS_VIDC20 | ||
102 | #include <mach/acornfb.h> | ||
103 | |||
104 | #define MAX_SIZE 2*1024*1024 | ||
105 | |||
106 | /* VIDC20 has a different set of rules from the VIDC: | ||
107 | * hcr : must be multiple of 4 | ||
108 | * hswr : must be even | ||
109 | * hdsr : must be even | ||
110 | * hder : must be even | ||
111 | * vcr : >= 2, (interlace, must be odd) | ||
112 | * vswr : >= 1 | ||
113 | * vdsr : >= 1 | ||
114 | * vder : >= vdsr | ||
115 | */ | ||
116 | static void acornfb_set_timing(struct fb_info *info) | ||
117 | { | ||
118 | struct fb_var_screeninfo *var = &info->var; | ||
119 | struct vidc_timing vidc; | ||
120 | u_int vcr, fsize; | ||
121 | u_int ext_ctl, dat_ctl; | ||
122 | u_int words_per_line; | ||
123 | |||
124 | memset(&vidc, 0, sizeof(vidc)); | ||
125 | |||
126 | vidc.h_sync_width = var->hsync_len - 8; | ||
127 | vidc.h_border_start = vidc.h_sync_width + var->left_margin + 8 - 12; | ||
128 | vidc.h_display_start = vidc.h_border_start + 12 - 18; | ||
129 | vidc.h_display_end = vidc.h_display_start + var->xres; | ||
130 | vidc.h_border_end = vidc.h_display_end + 18 - 12; | ||
131 | vidc.h_cycle = vidc.h_border_end + var->right_margin + 12 - 8; | ||
132 | vidc.h_interlace = vidc.h_cycle / 2; | ||
133 | vidc.v_sync_width = var->vsync_len - 1; | ||
134 | vidc.v_border_start = vidc.v_sync_width + var->upper_margin; | ||
135 | vidc.v_display_start = vidc.v_border_start; | ||
136 | vidc.v_display_end = vidc.v_display_start + var->yres; | ||
137 | vidc.v_border_end = vidc.v_display_end; | ||
138 | vidc.control = acornfb_default_control(); | ||
139 | |||
140 | vcr = var->vsync_len + var->upper_margin + var->yres + | ||
141 | var->lower_margin; | ||
142 | |||
143 | if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { | ||
144 | vidc.v_cycle = (vcr - 3) / 2; | ||
145 | vidc.control |= VIDC20_CTRL_INT; | ||
146 | } else | ||
147 | vidc.v_cycle = vcr - 2; | ||
148 | |||
149 | switch (var->bits_per_pixel) { | ||
150 | case 1: vidc.control |= VIDC20_CTRL_1BPP; break; | ||
151 | case 2: vidc.control |= VIDC20_CTRL_2BPP; break; | ||
152 | case 4: vidc.control |= VIDC20_CTRL_4BPP; break; | ||
153 | default: | ||
154 | case 8: vidc.control |= VIDC20_CTRL_8BPP; break; | ||
155 | case 16: vidc.control |= VIDC20_CTRL_16BPP; break; | ||
156 | case 32: vidc.control |= VIDC20_CTRL_32BPP; break; | ||
157 | } | ||
158 | |||
159 | acornfb_vidc20_find_rates(&vidc, var); | ||
160 | fsize = var->vsync_len + var->upper_margin + var->lower_margin - 1; | ||
161 | |||
162 | if (memcmp(¤t_vidc, &vidc, sizeof(vidc))) { | ||
163 | current_vidc = vidc; | ||
164 | |||
165 | vidc_writel(VIDC20_CTRL| vidc.control); | ||
166 | vidc_writel(0xd0000000 | vidc.pll_ctl); | ||
167 | vidc_writel(0x80000000 | vidc.h_cycle); | ||
168 | vidc_writel(0x81000000 | vidc.h_sync_width); | ||
169 | vidc_writel(0x82000000 | vidc.h_border_start); | ||
170 | vidc_writel(0x83000000 | vidc.h_display_start); | ||
171 | vidc_writel(0x84000000 | vidc.h_display_end); | ||
172 | vidc_writel(0x85000000 | vidc.h_border_end); | ||
173 | vidc_writel(0x86000000); | ||
174 | vidc_writel(0x87000000 | vidc.h_interlace); | ||
175 | vidc_writel(0x90000000 | vidc.v_cycle); | ||
176 | vidc_writel(0x91000000 | vidc.v_sync_width); | ||
177 | vidc_writel(0x92000000 | vidc.v_border_start); | ||
178 | vidc_writel(0x93000000 | vidc.v_display_start); | ||
179 | vidc_writel(0x94000000 | vidc.v_display_end); | ||
180 | vidc_writel(0x95000000 | vidc.v_border_end); | ||
181 | vidc_writel(0x96000000); | ||
182 | vidc_writel(0x97000000); | ||
183 | } | ||
184 | |||
185 | iomd_writel(fsize, IOMD_FSIZE); | ||
186 | |||
187 | ext_ctl = acornfb_default_econtrol(); | ||
188 | |||
189 | if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */ | ||
190 | ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC; | ||
191 | else { | ||
192 | if (var->sync & FB_SYNC_HOR_HIGH_ACT) | ||
193 | ext_ctl |= VIDC20_ECTL_HS_HSYNC; | ||
194 | else | ||
195 | ext_ctl |= VIDC20_ECTL_HS_NHSYNC; | ||
196 | |||
197 | if (var->sync & FB_SYNC_VERT_HIGH_ACT) | ||
198 | ext_ctl |= VIDC20_ECTL_VS_VSYNC; | ||
199 | else | ||
200 | ext_ctl |= VIDC20_ECTL_VS_NVSYNC; | ||
201 | } | ||
202 | |||
203 | vidc_writel(VIDC20_ECTL | ext_ctl); | ||
204 | |||
205 | words_per_line = var->xres * var->bits_per_pixel / 32; | ||
206 | |||
207 | if (current_par.using_vram && info->fix.smem_len == 2048*1024) | ||
208 | words_per_line /= 2; | ||
209 | |||
210 | /* RiscPC doesn't use the VIDC's VRAM control. */ | ||
211 | dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line; | ||
212 | |||
213 | /* The data bus width is dependent on both the type | ||
214 | * and amount of video memory. | ||
215 | * DRAM 32bit low | ||
216 | * 1MB VRAM 32bit | ||
217 | * 2MB VRAM 64bit | ||
218 | */ | ||
219 | if (current_par.using_vram && current_par.vram_half_sam == 2048) | ||
220 | dat_ctl |= VIDC20_DCTL_BUS_D63_0; | ||
221 | else | ||
222 | dat_ctl |= VIDC20_DCTL_BUS_D31_0; | ||
223 | |||
224 | vidc_writel(VIDC20_DCTL | dat_ctl); | ||
225 | |||
226 | #ifdef DEBUG_MODE_SELECTION | ||
227 | printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres, | ||
228 | var->yres, var->bits_per_pixel); | ||
229 | printk(KERN_DEBUG " H-cycle : %d\n", vidc.h_cycle); | ||
230 | printk(KERN_DEBUG " H-sync-width : %d\n", vidc.h_sync_width); | ||
231 | printk(KERN_DEBUG " H-border-start : %d\n", vidc.h_border_start); | ||
232 | printk(KERN_DEBUG " H-display-start : %d\n", vidc.h_display_start); | ||
233 | printk(KERN_DEBUG " H-display-end : %d\n", vidc.h_display_end); | ||
234 | printk(KERN_DEBUG " H-border-end : %d\n", vidc.h_border_end); | ||
235 | printk(KERN_DEBUG " H-interlace : %d\n", vidc.h_interlace); | ||
236 | printk(KERN_DEBUG " V-cycle : %d\n", vidc.v_cycle); | ||
237 | printk(KERN_DEBUG " V-sync-width : %d\n", vidc.v_sync_width); | ||
238 | printk(KERN_DEBUG " V-border-start : %d\n", vidc.v_border_start); | ||
239 | printk(KERN_DEBUG " V-display-start : %d\n", vidc.v_display_start); | ||
240 | printk(KERN_DEBUG " V-display-end : %d\n", vidc.v_display_end); | ||
241 | printk(KERN_DEBUG " V-border-end : %d\n", vidc.v_border_end); | ||
242 | printk(KERN_DEBUG " Ext Ctrl (C) : 0x%08X\n", ext_ctl); | ||
243 | printk(KERN_DEBUG " PLL Ctrl (D) : 0x%08X\n", vidc.pll_ctl); | ||
244 | printk(KERN_DEBUG " Ctrl (E) : 0x%08X\n", vidc.control); | ||
245 | printk(KERN_DEBUG " Data Ctrl (F) : 0x%08X\n", dat_ctl); | ||
246 | printk(KERN_DEBUG " Fsize : 0x%08X\n", fsize); | ||
247 | #endif | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * We have to take note of the VIDC20's 16-bit palette here. | ||
252 | * The VIDC20 looks up a 16 bit pixel as follows: | ||
253 | * | ||
254 | * bits 111111 | ||
255 | * 5432109876543210 | ||
256 | * red ++++++++ (8 bits, 7 to 0) | ||
257 | * green ++++++++ (8 bits, 11 to 4) | ||
258 | * blue ++++++++ (8 bits, 15 to 8) | ||
259 | * | ||
260 | * We use a pixel which looks like: | ||
261 | * | ||
262 | * bits 111111 | ||
263 | * 5432109876543210 | ||
264 | * red +++++ (5 bits, 4 to 0) | ||
265 | * green +++++ (5 bits, 9 to 5) | ||
266 | * blue +++++ (5 bits, 14 to 10) | ||
267 | */ | ||
268 | static int | ||
269 | acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | ||
270 | u_int trans, struct fb_info *info) | ||
271 | { | ||
272 | union palette pal; | ||
273 | |||
274 | if (regno >= current_par.palette_size) | ||
275 | return 1; | ||
276 | |||
277 | if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) { | ||
278 | u32 pseudo_val; | ||
279 | |||
280 | pseudo_val = regno << info->var.red.offset; | ||
281 | pseudo_val |= regno << info->var.green.offset; | ||
282 | pseudo_val |= regno << info->var.blue.offset; | ||
283 | |||
284 | ((u32 *)info->pseudo_palette)[regno] = pseudo_val; | ||
285 | } | ||
286 | |||
287 | pal.p = 0; | ||
288 | pal.vidc20.red = red >> 8; | ||
289 | pal.vidc20.green = green >> 8; | ||
290 | pal.vidc20.blue = blue >> 8; | ||
291 | |||
292 | current_par.palette[regno] = pal; | ||
293 | |||
294 | if (info->var.bits_per_pixel == 16) { | ||
295 | int i; | ||
296 | |||
297 | pal.p = 0; | ||
298 | vidc_writel(0x10000000); | ||
299 | for (i = 0; i < 256; i += 1) { | ||
300 | pal.vidc20.red = current_par.palette[ i & 31].vidc20.red; | ||
301 | pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green; | ||
302 | pal.vidc20.blue = current_par.palette[(i >> 2) & 31].vidc20.blue; | ||
303 | vidc_writel(pal.p); | ||
304 | /* Palette register pointer auto-increments */ | ||
305 | } | ||
306 | } else { | ||
307 | vidc_writel(0x10000000 | regno); | ||
308 | vidc_writel(pal.p); | ||
309 | } | ||
310 | |||
311 | return 0; | ||
312 | } | ||
313 | #endif | ||
314 | |||
315 | /* | ||
316 | * Before selecting the timing parameters, adjust | ||
317 | * the resolution to fit the rules. | ||
318 | */ | ||
319 | static int | ||
320 | acornfb_adjust_timing(struct fb_info *info, struct fb_var_screeninfo *var, u_int fontht) | ||
321 | { | ||
322 | u_int font_line_len, sam_size, min_size, size, nr_y; | ||
323 | |||
324 | /* xres must be even */ | ||
325 | var->xres = (var->xres + 1) & ~1; | ||
326 | |||
327 | /* | ||
328 | * We don't allow xres_virtual to differ from xres | ||
329 | */ | ||
330 | var->xres_virtual = var->xres; | ||
331 | var->xoffset = 0; | ||
332 | |||
333 | if (current_par.using_vram) | ||
334 | sam_size = current_par.vram_half_sam * 2; | ||
335 | else | ||
336 | sam_size = 16; | ||
337 | |||
338 | /* | ||
339 | * Now, find a value for yres_virtual which allows | ||
340 | * us to do ywrap scrolling. The value of | ||
341 | * yres_virtual must be such that the end of the | ||
342 | * displayable frame buffer must be aligned with | ||
343 | * the start of a font line. | ||
344 | */ | ||
345 | font_line_len = var->xres * var->bits_per_pixel * fontht / 8; | ||
346 | min_size = var->xres * var->yres * var->bits_per_pixel / 8; | ||
347 | |||
348 | /* | ||
349 | * If minimum screen size is greater than that we have | ||
350 | * available, reject it. | ||
351 | */ | ||
352 | if (min_size > info->fix.smem_len) | ||
353 | return -EINVAL; | ||
354 | |||
355 | /* Find int 'y', such that y * fll == s * sam < maxsize | ||
356 | * y = s * sam / fll; s = maxsize / sam | ||
357 | */ | ||
358 | for (size = info->fix.smem_len; | ||
359 | nr_y = size / font_line_len, min_size <= size; | ||
360 | size -= sam_size) { | ||
361 | if (nr_y * font_line_len == size) | ||
362 | break; | ||
363 | } | ||
364 | nr_y *= fontht; | ||
365 | |||
366 | if (var->accel_flags & FB_ACCELF_TEXT) { | ||
367 | if (min_size > size) { | ||
368 | /* | ||
369 | * failed, use ypan | ||
370 | */ | ||
371 | size = info->fix.smem_len; | ||
372 | var->yres_virtual = size / (font_line_len / fontht); | ||
373 | } else | ||
374 | var->yres_virtual = nr_y; | ||
375 | } else if (var->yres_virtual > nr_y) | ||
376 | var->yres_virtual = nr_y; | ||
377 | |||
378 | current_par.screen_end = info->fix.smem_start + size; | ||
379 | |||
380 | /* | ||
381 | * Fix yres & yoffset if needed. | ||
382 | */ | ||
383 | if (var->yres > var->yres_virtual) | ||
384 | var->yres = var->yres_virtual; | ||
385 | |||
386 | if (var->vmode & FB_VMODE_YWRAP) { | ||
387 | if (var->yoffset > var->yres_virtual) | ||
388 | var->yoffset = var->yres_virtual; | ||
389 | } else { | ||
390 | if (var->yoffset + var->yres > var->yres_virtual) | ||
391 | var->yoffset = var->yres_virtual - var->yres; | ||
392 | } | ||
393 | |||
394 | /* hsync_len must be even */ | ||
395 | var->hsync_len = (var->hsync_len + 1) & ~1; | ||
396 | |||
397 | #if defined(HAS_VIDC20) | ||
398 | /* left_margin must be even */ | ||
399 | if (var->left_margin & 1) { | ||
400 | var->left_margin += 1; | ||
401 | var->right_margin -= 1; | ||
402 | } | ||
403 | |||
404 | /* right_margin must be even */ | ||
405 | if (var->right_margin & 1) | ||
406 | var->right_margin += 1; | ||
407 | #endif | ||
408 | |||
409 | if (var->vsync_len < 1) | ||
410 | var->vsync_len = 1; | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static int | ||
416 | acornfb_validate_timing(struct fb_var_screeninfo *var, | ||
417 | struct fb_monspecs *monspecs) | ||
418 | { | ||
419 | unsigned long hs, vs; | ||
420 | |||
421 | /* | ||
422 | * hs(Hz) = 10^12 / (pixclock * xtotal) | ||
423 | * vs(Hz) = hs(Hz) / ytotal | ||
424 | * | ||
425 | * No need to do long long divisions or anything | ||
426 | * like that if you factor it correctly | ||
427 | */ | ||
428 | hs = 1953125000 / var->pixclock; | ||
429 | hs = hs * 512 / | ||
430 | (var->xres + var->left_margin + var->right_margin + var->hsync_len); | ||
431 | vs = hs / | ||
432 | (var->yres + var->upper_margin + var->lower_margin + var->vsync_len); | ||
433 | |||
434 | return (vs >= monspecs->vfmin && vs <= monspecs->vfmax && | ||
435 | hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL; | ||
436 | } | ||
437 | |||
438 | static inline void | ||
439 | acornfb_update_dma(struct fb_info *info, struct fb_var_screeninfo *var) | ||
440 | { | ||
441 | u_int off = var->yoffset * info->fix.line_length; | ||
442 | |||
443 | #if defined(HAS_MEMC) | ||
444 | memc_write(VDMA_INIT, off >> 2); | ||
445 | #elif defined(HAS_IOMD) | ||
446 | iomd_writel(info->fix.smem_start + off, IOMD_VIDINIT); | ||
447 | #endif | ||
448 | } | ||
449 | |||
450 | static int | ||
451 | acornfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
452 | { | ||
453 | u_int fontht; | ||
454 | int err; | ||
455 | |||
456 | /* | ||
457 | * FIXME: Find the font height | ||
458 | */ | ||
459 | fontht = 8; | ||
460 | |||
461 | var->red.msb_right = 0; | ||
462 | var->green.msb_right = 0; | ||
463 | var->blue.msb_right = 0; | ||
464 | var->transp.msb_right = 0; | ||
465 | |||
466 | switch (var->bits_per_pixel) { | ||
467 | case 1: case 2: case 4: case 8: | ||
468 | var->red.offset = 0; | ||
469 | var->red.length = var->bits_per_pixel; | ||
470 | var->green = var->red; | ||
471 | var->blue = var->red; | ||
472 | var->transp.offset = 0; | ||
473 | var->transp.length = 0; | ||
474 | break; | ||
475 | |||
476 | #ifdef HAS_VIDC20 | ||
477 | case 16: | ||
478 | var->red.offset = 0; | ||
479 | var->red.length = 5; | ||
480 | var->green.offset = 5; | ||
481 | var->green.length = 5; | ||
482 | var->blue.offset = 10; | ||
483 | var->blue.length = 5; | ||
484 | var->transp.offset = 15; | ||
485 | var->transp.length = 1; | ||
486 | break; | ||
487 | |||
488 | case 32: | ||
489 | var->red.offset = 0; | ||
490 | var->red.length = 8; | ||
491 | var->green.offset = 8; | ||
492 | var->green.length = 8; | ||
493 | var->blue.offset = 16; | ||
494 | var->blue.length = 8; | ||
495 | var->transp.offset = 24; | ||
496 | var->transp.length = 4; | ||
497 | break; | ||
498 | #endif | ||
499 | default: | ||
500 | return -EINVAL; | ||
501 | } | ||
502 | |||
503 | /* | ||
504 | * Check to see if the pixel rate is valid. | ||
505 | */ | ||
506 | if (!acornfb_valid_pixrate(var)) | ||
507 | return -EINVAL; | ||
508 | |||
509 | /* | ||
510 | * Validate and adjust the resolution to | ||
511 | * match the video generator hardware. | ||
512 | */ | ||
513 | err = acornfb_adjust_timing(info, var, fontht); | ||
514 | if (err) | ||
515 | return err; | ||
516 | |||
517 | /* | ||
518 | * Validate the timing against the | ||
519 | * monitor hardware. | ||
520 | */ | ||
521 | return acornfb_validate_timing(var, &info->monspecs); | ||
522 | } | ||
523 | |||
524 | static int acornfb_set_par(struct fb_info *info) | ||
525 | { | ||
526 | switch (info->var.bits_per_pixel) { | ||
527 | case 1: | ||
528 | current_par.palette_size = 2; | ||
529 | info->fix.visual = FB_VISUAL_MONO10; | ||
530 | break; | ||
531 | case 2: | ||
532 | current_par.palette_size = 4; | ||
533 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
534 | break; | ||
535 | case 4: | ||
536 | current_par.palette_size = 16; | ||
537 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
538 | break; | ||
539 | case 8: | ||
540 | current_par.palette_size = VIDC_PALETTE_SIZE; | ||
541 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
542 | break; | ||
543 | #ifdef HAS_VIDC20 | ||
544 | case 16: | ||
545 | current_par.palette_size = 32; | ||
546 | info->fix.visual = FB_VISUAL_DIRECTCOLOR; | ||
547 | break; | ||
548 | case 32: | ||
549 | current_par.palette_size = VIDC_PALETTE_SIZE; | ||
550 | info->fix.visual = FB_VISUAL_DIRECTCOLOR; | ||
551 | break; | ||
552 | #endif | ||
553 | default: | ||
554 | BUG(); | ||
555 | } | ||
556 | |||
557 | info->fix.line_length = (info->var.xres * info->var.bits_per_pixel) / 8; | ||
558 | |||
559 | #if defined(HAS_MEMC) | ||
560 | { | ||
561 | unsigned long size = info->fix.smem_len - VDMA_XFERSIZE; | ||
562 | |||
563 | memc_write(VDMA_START, 0); | ||
564 | memc_write(VDMA_END, size >> 2); | ||
565 | } | ||
566 | #elif defined(HAS_IOMD) | ||
567 | { | ||
568 | unsigned long start, size; | ||
569 | u_int control; | ||
570 | |||
571 | start = info->fix.smem_start; | ||
572 | size = current_par.screen_end; | ||
573 | |||
574 | if (current_par.using_vram) { | ||
575 | size -= current_par.vram_half_sam; | ||
576 | control = DMA_CR_E | (current_par.vram_half_sam / 256); | ||
577 | } else { | ||
578 | size -= 16; | ||
579 | control = DMA_CR_E | DMA_CR_D | 16; | ||
580 | } | ||
581 | |||
582 | iomd_writel(start, IOMD_VIDSTART); | ||
583 | iomd_writel(size, IOMD_VIDEND); | ||
584 | iomd_writel(control, IOMD_VIDCR); | ||
585 | } | ||
586 | #endif | ||
587 | |||
588 | acornfb_update_dma(info, &info->var); | ||
589 | acornfb_set_timing(info); | ||
590 | |||
591 | return 0; | ||
592 | } | ||
593 | |||
594 | static int | ||
595 | acornfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) | ||
596 | { | ||
597 | u_int y_bottom = var->yoffset; | ||
598 | |||
599 | if (!(var->vmode & FB_VMODE_YWRAP)) | ||
600 | y_bottom += info->var.yres; | ||
601 | |||
602 | if (y_bottom > info->var.yres_virtual) | ||
603 | return -EINVAL; | ||
604 | |||
605 | acornfb_update_dma(info, var); | ||
606 | |||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | static struct fb_ops acornfb_ops = { | ||
611 | .owner = THIS_MODULE, | ||
612 | .fb_check_var = acornfb_check_var, | ||
613 | .fb_set_par = acornfb_set_par, | ||
614 | .fb_setcolreg = acornfb_setcolreg, | ||
615 | .fb_pan_display = acornfb_pan_display, | ||
616 | .fb_fillrect = cfb_fillrect, | ||
617 | .fb_copyarea = cfb_copyarea, | ||
618 | .fb_imageblit = cfb_imageblit, | ||
619 | }; | ||
620 | |||
621 | /* | ||
622 | * Everything after here is initialisation!!! | ||
623 | */ | ||
624 | static struct fb_videomode modedb[] = { | ||
625 | { /* 320x256 @ 50Hz */ | ||
626 | NULL, 50, 320, 256, 125000, 92, 62, 35, 19, 38, 2, | ||
627 | FB_SYNC_COMP_HIGH_ACT, | ||
628 | FB_VMODE_NONINTERLACED | ||
629 | }, { /* 640x250 @ 50Hz, 15.6 kHz hsync */ | ||
630 | NULL, 50, 640, 250, 62500, 185, 123, 38, 21, 76, 3, | ||
631 | 0, | ||
632 | FB_VMODE_NONINTERLACED | ||
633 | }, { /* 640x256 @ 50Hz, 15.6 kHz hsync */ | ||
634 | NULL, 50, 640, 256, 62500, 185, 123, 35, 18, 76, 3, | ||
635 | 0, | ||
636 | FB_VMODE_NONINTERLACED | ||
637 | }, { /* 640x512 @ 50Hz, 26.8 kHz hsync */ | ||
638 | NULL, 50, 640, 512, 41667, 113, 87, 18, 1, 56, 3, | ||
639 | 0, | ||
640 | FB_VMODE_NONINTERLACED | ||
641 | }, { /* 640x250 @ 70Hz, 31.5 kHz hsync */ | ||
642 | NULL, 70, 640, 250, 39722, 48, 16, 109, 88, 96, 2, | ||
643 | 0, | ||
644 | FB_VMODE_NONINTERLACED | ||
645 | }, { /* 640x256 @ 70Hz, 31.5 kHz hsync */ | ||
646 | NULL, 70, 640, 256, 39722, 48, 16, 106, 85, 96, 2, | ||
647 | 0, | ||
648 | FB_VMODE_NONINTERLACED | ||
649 | }, { /* 640x352 @ 70Hz, 31.5 kHz hsync */ | ||
650 | NULL, 70, 640, 352, 39722, 48, 16, 58, 37, 96, 2, | ||
651 | 0, | ||
652 | FB_VMODE_NONINTERLACED | ||
653 | }, { /* 640x480 @ 60Hz, 31.5 kHz hsync */ | ||
654 | NULL, 60, 640, 480, 39722, 48, 16, 32, 11, 96, 2, | ||
655 | 0, | ||
656 | FB_VMODE_NONINTERLACED | ||
657 | }, { /* 800x600 @ 56Hz, 35.2 kHz hsync */ | ||
658 | NULL, 56, 800, 600, 27778, 101, 23, 22, 1, 100, 2, | ||
659 | 0, | ||
660 | FB_VMODE_NONINTERLACED | ||
661 | }, { /* 896x352 @ 60Hz, 21.8 kHz hsync */ | ||
662 | NULL, 60, 896, 352, 41667, 59, 27, 9, 0, 118, 3, | ||
663 | 0, | ||
664 | FB_VMODE_NONINTERLACED | ||
665 | }, { /* 1024x 768 @ 60Hz, 48.4 kHz hsync */ | ||
666 | NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6, | ||
667 | 0, | ||
668 | FB_VMODE_NONINTERLACED | ||
669 | }, { /* 1280x1024 @ 60Hz, 63.8 kHz hsync */ | ||
670 | NULL, 60, 1280, 1024, 9090, 186, 96, 38, 1, 160, 3, | ||
671 | 0, | ||
672 | FB_VMODE_NONINTERLACED | ||
673 | } | ||
674 | }; | ||
675 | |||
676 | static struct fb_videomode acornfb_default_mode = { | ||
677 | .name = NULL, | ||
678 | .refresh = 60, | ||
679 | .xres = 640, | ||
680 | .yres = 480, | ||
681 | .pixclock = 39722, | ||
682 | .left_margin = 56, | ||
683 | .right_margin = 16, | ||
684 | .upper_margin = 34, | ||
685 | .lower_margin = 9, | ||
686 | .hsync_len = 88, | ||
687 | .vsync_len = 2, | ||
688 | .sync = 0, | ||
689 | .vmode = FB_VMODE_NONINTERLACED | ||
690 | }; | ||
691 | |||
692 | static void acornfb_init_fbinfo(void) | ||
693 | { | ||
694 | static int first = 1; | ||
695 | |||
696 | if (!first) | ||
697 | return; | ||
698 | first = 0; | ||
699 | |||
700 | fb_info.fbops = &acornfb_ops; | ||
701 | fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; | ||
702 | fb_info.pseudo_palette = current_par.pseudo_palette; | ||
703 | |||
704 | strcpy(fb_info.fix.id, "Acorn"); | ||
705 | fb_info.fix.type = FB_TYPE_PACKED_PIXELS; | ||
706 | fb_info.fix.type_aux = 0; | ||
707 | fb_info.fix.xpanstep = 0; | ||
708 | fb_info.fix.ypanstep = 1; | ||
709 | fb_info.fix.ywrapstep = 1; | ||
710 | fb_info.fix.line_length = 0; | ||
711 | fb_info.fix.accel = FB_ACCEL_NONE; | ||
712 | |||
713 | /* | ||
714 | * setup initial parameters | ||
715 | */ | ||
716 | memset(&fb_info.var, 0, sizeof(fb_info.var)); | ||
717 | |||
718 | #if defined(HAS_VIDC20) | ||
719 | fb_info.var.red.length = 8; | ||
720 | fb_info.var.transp.length = 4; | ||
721 | #endif | ||
722 | fb_info.var.green = fb_info.var.red; | ||
723 | fb_info.var.blue = fb_info.var.red; | ||
724 | fb_info.var.nonstd = 0; | ||
725 | fb_info.var.activate = FB_ACTIVATE_NOW; | ||
726 | fb_info.var.height = -1; | ||
727 | fb_info.var.width = -1; | ||
728 | fb_info.var.vmode = FB_VMODE_NONINTERLACED; | ||
729 | fb_info.var.accel_flags = FB_ACCELF_TEXT; | ||
730 | |||
731 | current_par.dram_size = 0; | ||
732 | current_par.montype = -1; | ||
733 | current_par.dpms = 0; | ||
734 | } | ||
735 | |||
736 | /* | ||
737 | * setup acornfb options: | ||
738 | * | ||
739 | * mon:hmin-hmax:vmin-vmax:dpms:width:height | ||
740 | * Set monitor parameters: | ||
741 | * hmin = horizontal minimum frequency (Hz) | ||
742 | * hmax = horizontal maximum frequency (Hz) (optional) | ||
743 | * vmin = vertical minimum frequency (Hz) | ||
744 | * vmax = vertical maximum frequency (Hz) (optional) | ||
745 | * dpms = DPMS supported? (optional) | ||
746 | * width = width of picture in mm. (optional) | ||
747 | * height = height of picture in mm. (optional) | ||
748 | * | ||
749 | * montype:type | ||
750 | * Set RISC-OS style monitor type: | ||
751 | * 0 (or tv) - TV frequency | ||
752 | * 1 (or multi) - Multi frequency | ||
753 | * 2 (or hires) - Hi-res monochrome | ||
754 | * 3 (or vga) - VGA | ||
755 | * 4 (or svga) - SVGA | ||
756 | * auto, or option missing | ||
757 | * - try hardware detect | ||
758 | * | ||
759 | * dram:size | ||
760 | * Set the amount of DRAM to use for the frame buffer | ||
761 | * (even if you have VRAM). | ||
762 | * size can optionally be followed by 'M' or 'K' for | ||
763 | * MB or KB respectively. | ||
764 | */ | ||
765 | static void acornfb_parse_mon(char *opt) | ||
766 | { | ||
767 | char *p = opt; | ||
768 | |||
769 | current_par.montype = -2; | ||
770 | |||
771 | fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0); | ||
772 | if (*p == '-') | ||
773 | fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0); | ||
774 | else | ||
775 | fb_info.monspecs.hfmax = fb_info.monspecs.hfmin; | ||
776 | |||
777 | if (*p != ':') | ||
778 | goto bad; | ||
779 | |||
780 | fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0); | ||
781 | if (*p == '-') | ||
782 | fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0); | ||
783 | else | ||
784 | fb_info.monspecs.vfmax = fb_info.monspecs.vfmin; | ||
785 | |||
786 | if (*p != ':') | ||
787 | goto check_values; | ||
788 | |||
789 | fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0); | ||
790 | |||
791 | if (*p != ':') | ||
792 | goto check_values; | ||
793 | |||
794 | fb_info.var.width = simple_strtoul(p + 1, &p, 0); | ||
795 | |||
796 | if (*p != ':') | ||
797 | goto check_values; | ||
798 | |||
799 | fb_info.var.height = simple_strtoul(p + 1, NULL, 0); | ||
800 | |||
801 | check_values: | ||
802 | if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin || | ||
803 | fb_info.monspecs.vfmax < fb_info.monspecs.vfmin) | ||
804 | goto bad; | ||
805 | return; | ||
806 | |||
807 | bad: | ||
808 | printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt); | ||
809 | current_par.montype = -1; | ||
810 | } | ||
811 | |||
812 | static void acornfb_parse_montype(char *opt) | ||
813 | { | ||
814 | current_par.montype = -2; | ||
815 | |||
816 | if (strncmp(opt, "tv", 2) == 0) { | ||
817 | opt += 2; | ||
818 | current_par.montype = 0; | ||
819 | } else if (strncmp(opt, "multi", 5) == 0) { | ||
820 | opt += 5; | ||
821 | current_par.montype = 1; | ||
822 | } else if (strncmp(opt, "hires", 5) == 0) { | ||
823 | opt += 5; | ||
824 | current_par.montype = 2; | ||
825 | } else if (strncmp(opt, "vga", 3) == 0) { | ||
826 | opt += 3; | ||
827 | current_par.montype = 3; | ||
828 | } else if (strncmp(opt, "svga", 4) == 0) { | ||
829 | opt += 4; | ||
830 | current_par.montype = 4; | ||
831 | } else if (strncmp(opt, "auto", 4) == 0) { | ||
832 | opt += 4; | ||
833 | current_par.montype = -1; | ||
834 | } else if (isdigit(*opt)) | ||
835 | current_par.montype = simple_strtoul(opt, &opt, 0); | ||
836 | |||
837 | if (current_par.montype == -2 || | ||
838 | current_par.montype > NR_MONTYPES) { | ||
839 | printk(KERN_ERR "acornfb: unknown monitor type: %s\n", | ||
840 | opt); | ||
841 | current_par.montype = -1; | ||
842 | } else | ||
843 | if (opt && *opt) { | ||
844 | if (strcmp(opt, ",dpms") == 0) | ||
845 | current_par.dpms = 1; | ||
846 | else | ||
847 | printk(KERN_ERR | ||
848 | "acornfb: unknown monitor option: %s\n", | ||
849 | opt); | ||
850 | } | ||
851 | } | ||
852 | |||
853 | static void acornfb_parse_dram(char *opt) | ||
854 | { | ||
855 | unsigned int size; | ||
856 | |||
857 | size = simple_strtoul(opt, &opt, 0); | ||
858 | |||
859 | if (opt) { | ||
860 | switch (*opt) { | ||
861 | case 'M': | ||
862 | case 'm': | ||
863 | size *= 1024; | ||
864 | case 'K': | ||
865 | case 'k': | ||
866 | size *= 1024; | ||
867 | default: | ||
868 | break; | ||
869 | } | ||
870 | } | ||
871 | |||
872 | current_par.dram_size = size; | ||
873 | } | ||
874 | |||
875 | static struct options { | ||
876 | char *name; | ||
877 | void (*parse)(char *opt); | ||
878 | } opt_table[] = { | ||
879 | { "mon", acornfb_parse_mon }, | ||
880 | { "montype", acornfb_parse_montype }, | ||
881 | { "dram", acornfb_parse_dram }, | ||
882 | { NULL, NULL } | ||
883 | }; | ||
884 | |||
885 | static int acornfb_setup(char *options) | ||
886 | { | ||
887 | struct options *optp; | ||
888 | char *opt; | ||
889 | |||
890 | if (!options || !*options) | ||
891 | return 0; | ||
892 | |||
893 | acornfb_init_fbinfo(); | ||
894 | |||
895 | while ((opt = strsep(&options, ",")) != NULL) { | ||
896 | if (!*opt) | ||
897 | continue; | ||
898 | |||
899 | for (optp = opt_table; optp->name; optp++) { | ||
900 | int optlen; | ||
901 | |||
902 | optlen = strlen(optp->name); | ||
903 | |||
904 | if (strncmp(opt, optp->name, optlen) == 0 && | ||
905 | opt[optlen] == ':') { | ||
906 | optp->parse(opt + optlen + 1); | ||
907 | break; | ||
908 | } | ||
909 | } | ||
910 | |||
911 | if (!optp->name) | ||
912 | printk(KERN_ERR "acornfb: unknown parameter: %s\n", | ||
913 | opt); | ||
914 | } | ||
915 | return 0; | ||
916 | } | ||
917 | |||
918 | /* | ||
919 | * Detect type of monitor connected | ||
920 | * For now, we just assume SVGA | ||
921 | */ | ||
922 | static int acornfb_detect_monitortype(void) | ||
923 | { | ||
924 | return 4; | ||
925 | } | ||
926 | |||
927 | /* | ||
928 | * This enables the unused memory to be freed on older Acorn machines. | ||
929 | * We are freeing memory on behalf of the architecture initialisation | ||
930 | * code here. | ||
931 | */ | ||
932 | static inline void | ||
933 | free_unused_pages(unsigned int virtual_start, unsigned int virtual_end) | ||
934 | { | ||
935 | int mb_freed = 0; | ||
936 | |||
937 | /* | ||
938 | * Align addresses | ||
939 | */ | ||
940 | virtual_start = PAGE_ALIGN(virtual_start); | ||
941 | virtual_end = PAGE_ALIGN(virtual_end); | ||
942 | |||
943 | while (virtual_start < virtual_end) { | ||
944 | struct page *page; | ||
945 | |||
946 | /* | ||
947 | * Clear page reserved bit, | ||
948 | * set count to 1, and free | ||
949 | * the page. | ||
950 | */ | ||
951 | page = virt_to_page(virtual_start); | ||
952 | __free_reserved_page(page); | ||
953 | |||
954 | virtual_start += PAGE_SIZE; | ||
955 | mb_freed += PAGE_SIZE / 1024; | ||
956 | } | ||
957 | |||
958 | printk("acornfb: freed %dK memory\n", mb_freed); | ||
959 | } | ||
960 | |||
961 | static int acornfb_probe(struct platform_device *dev) | ||
962 | { | ||
963 | unsigned long size; | ||
964 | u_int h_sync, v_sync; | ||
965 | int rc, i; | ||
966 | char *option = NULL; | ||
967 | |||
968 | if (fb_get_options("acornfb", &option)) | ||
969 | return -ENODEV; | ||
970 | acornfb_setup(option); | ||
971 | |||
972 | acornfb_init_fbinfo(); | ||
973 | |||
974 | current_par.dev = &dev->dev; | ||
975 | |||
976 | if (current_par.montype == -1) | ||
977 | current_par.montype = acornfb_detect_monitortype(); | ||
978 | |||
979 | if (current_par.montype == -1 || current_par.montype > NR_MONTYPES) | ||
980 | current_par.montype = 4; | ||
981 | |||
982 | if (current_par.montype >= 0) { | ||
983 | fb_info.monspecs = monspecs[current_par.montype]; | ||
984 | fb_info.monspecs.dpms = current_par.dpms; | ||
985 | } | ||
986 | |||
987 | /* | ||
988 | * Try to select a suitable default mode | ||
989 | */ | ||
990 | for (i = 0; i < ARRAY_SIZE(modedb); i++) { | ||
991 | unsigned long hs; | ||
992 | |||
993 | hs = modedb[i].refresh * | ||
994 | (modedb[i].yres + modedb[i].upper_margin + | ||
995 | modedb[i].lower_margin + modedb[i].vsync_len); | ||
996 | if (modedb[i].xres == DEFAULT_XRES && | ||
997 | modedb[i].yres == DEFAULT_YRES && | ||
998 | modedb[i].refresh >= fb_info.monspecs.vfmin && | ||
999 | modedb[i].refresh <= fb_info.monspecs.vfmax && | ||
1000 | hs >= fb_info.monspecs.hfmin && | ||
1001 | hs <= fb_info.monspecs.hfmax) { | ||
1002 | acornfb_default_mode = modedb[i]; | ||
1003 | break; | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | fb_info.screen_base = (char *)SCREEN_BASE; | ||
1008 | fb_info.fix.smem_start = SCREEN_START; | ||
1009 | current_par.using_vram = 0; | ||
1010 | |||
1011 | /* | ||
1012 | * If vram_size is set, we are using VRAM in | ||
1013 | * a Risc PC. However, if the user has specified | ||
1014 | * an amount of DRAM then use that instead. | ||
1015 | */ | ||
1016 | if (vram_size && !current_par.dram_size) { | ||
1017 | size = vram_size; | ||
1018 | current_par.vram_half_sam = vram_size / 1024; | ||
1019 | current_par.using_vram = 1; | ||
1020 | } else if (current_par.dram_size) | ||
1021 | size = current_par.dram_size; | ||
1022 | else | ||
1023 | size = MAX_SIZE; | ||
1024 | |||
1025 | /* | ||
1026 | * Limit maximum screen size. | ||
1027 | */ | ||
1028 | if (size > MAX_SIZE) | ||
1029 | size = MAX_SIZE; | ||
1030 | |||
1031 | size = PAGE_ALIGN(size); | ||
1032 | |||
1033 | #if defined(HAS_VIDC20) | ||
1034 | if (!current_par.using_vram) { | ||
1035 | dma_addr_t handle; | ||
1036 | void *base; | ||
1037 | |||
1038 | /* | ||
1039 | * RiscPC needs to allocate the DRAM memory | ||
1040 | * for the framebuffer if we are not using | ||
1041 | * VRAM. | ||
1042 | */ | ||
1043 | base = dma_alloc_writecombine(current_par.dev, size, &handle, | ||
1044 | GFP_KERNEL); | ||
1045 | if (base == NULL) { | ||
1046 | printk(KERN_ERR "acornfb: unable to allocate screen " | ||
1047 | "memory\n"); | ||
1048 | return -ENOMEM; | ||
1049 | } | ||
1050 | |||
1051 | fb_info.screen_base = base; | ||
1052 | fb_info.fix.smem_start = handle; | ||
1053 | } | ||
1054 | #endif | ||
1055 | fb_info.fix.smem_len = size; | ||
1056 | current_par.palette_size = VIDC_PALETTE_SIZE; | ||
1057 | |||
1058 | /* | ||
1059 | * Lookup the timing for this resolution. If we can't | ||
1060 | * find it, then we can't restore it if we change | ||
1061 | * the resolution, so we disable this feature. | ||
1062 | */ | ||
1063 | do { | ||
1064 | rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb, | ||
1065 | ARRAY_SIZE(modedb), | ||
1066 | &acornfb_default_mode, DEFAULT_BPP); | ||
1067 | /* | ||
1068 | * If we found an exact match, all ok. | ||
1069 | */ | ||
1070 | if (rc == 1) | ||
1071 | break; | ||
1072 | |||
1073 | rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0, | ||
1074 | &acornfb_default_mode, DEFAULT_BPP); | ||
1075 | /* | ||
1076 | * If we found an exact match, all ok. | ||
1077 | */ | ||
1078 | if (rc == 1) | ||
1079 | break; | ||
1080 | |||
1081 | rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb, | ||
1082 | ARRAY_SIZE(modedb), | ||
1083 | &acornfb_default_mode, DEFAULT_BPP); | ||
1084 | if (rc) | ||
1085 | break; | ||
1086 | |||
1087 | rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0, | ||
1088 | &acornfb_default_mode, DEFAULT_BPP); | ||
1089 | } while (0); | ||
1090 | |||
1091 | /* | ||
1092 | * If we didn't find an exact match, try the | ||
1093 | * generic database. | ||
1094 | */ | ||
1095 | if (rc == 0) { | ||
1096 | printk("Acornfb: no valid mode found\n"); | ||
1097 | return -EINVAL; | ||
1098 | } | ||
1099 | |||
1100 | h_sync = 1953125000 / fb_info.var.pixclock; | ||
1101 | h_sync = h_sync * 512 / (fb_info.var.xres + fb_info.var.left_margin + | ||
1102 | fb_info.var.right_margin + fb_info.var.hsync_len); | ||
1103 | v_sync = h_sync / (fb_info.var.yres + fb_info.var.upper_margin + | ||
1104 | fb_info.var.lower_margin + fb_info.var.vsync_len); | ||
1105 | |||
1106 | printk(KERN_INFO "Acornfb: %dkB %cRAM, %s, using %dx%d, " | ||
1107 | "%d.%03dkHz, %dHz\n", | ||
1108 | fb_info.fix.smem_len / 1024, | ||
1109 | current_par.using_vram ? 'V' : 'D', | ||
1110 | VIDC_NAME, fb_info.var.xres, fb_info.var.yres, | ||
1111 | h_sync / 1000, h_sync % 1000, v_sync); | ||
1112 | |||
1113 | printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n", | ||
1114 | fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000, | ||
1115 | fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000, | ||
1116 | fb_info.monspecs.vfmin, fb_info.monspecs.vfmax, | ||
1117 | fb_info.monspecs.dpms ? ", DPMS" : ""); | ||
1118 | |||
1119 | if (fb_set_var(&fb_info, &fb_info.var)) | ||
1120 | printk(KERN_ERR "Acornfb: unable to set display parameters\n"); | ||
1121 | |||
1122 | if (register_framebuffer(&fb_info) < 0) | ||
1123 | return -EINVAL; | ||
1124 | return 0; | ||
1125 | } | ||
1126 | |||
1127 | static struct platform_driver acornfb_driver = { | ||
1128 | .probe = acornfb_probe, | ||
1129 | .driver = { | ||
1130 | .name = "acornfb", | ||
1131 | }, | ||
1132 | }; | ||
1133 | |||
1134 | static int __init acornfb_init(void) | ||
1135 | { | ||
1136 | return platform_driver_register(&acornfb_driver); | ||
1137 | } | ||
1138 | |||
1139 | module_init(acornfb_init); | ||
1140 | |||
1141 | MODULE_AUTHOR("Russell King"); | ||
1142 | MODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver"); | ||
1143 | MODULE_LICENSE("GPL"); | ||