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