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/fbmon.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/fbmon.c')
-rw-r--r-- | drivers/video/fbmon.c | 1258 |
1 files changed, 1258 insertions, 0 deletions
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c new file mode 100644 index 000000000000..978def013587 --- /dev/null +++ b/drivers/video/fbmon.c | |||
@@ -0,0 +1,1258 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/fbmon.c | ||
3 | * | ||
4 | * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net> | ||
5 | * | ||
6 | * Credits: | ||
7 | * | ||
8 | * The EDID Parser is a conglomeration from the following sources: | ||
9 | * | ||
10 | * 1. SciTech SNAP Graphics Architecture | ||
11 | * Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved. | ||
12 | * | ||
13 | * 2. XFree86 4.3.0, interpret_edid.c | ||
14 | * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE> | ||
15 | * | ||
16 | * 3. John Fremlin <vii@users.sourceforge.net> and | ||
17 | * Ani Joshi <ajoshi@unixbox.com> | ||
18 | * | ||
19 | * Generalized Timing Formula is derived from: | ||
20 | * | ||
21 | * GTF Spreadsheet by Andy Morrish (1/5/97) | ||
22 | * available at http://www.vesa.org | ||
23 | * | ||
24 | * This file is subject to the terms and conditions of the GNU General Public | ||
25 | * License. See the file COPYING in the main directory of this archive | ||
26 | * for more details. | ||
27 | * | ||
28 | */ | ||
29 | #include <linux/tty.h> | ||
30 | #include <linux/fb.h> | ||
31 | #include <linux/module.h> | ||
32 | #ifdef CONFIG_PPC_OF | ||
33 | #include <linux/pci.h> | ||
34 | #include <asm/prom.h> | ||
35 | #include <asm/pci-bridge.h> | ||
36 | #endif | ||
37 | #include <video/edid.h> | ||
38 | #include "edid.h" | ||
39 | |||
40 | /* | ||
41 | * EDID parser | ||
42 | */ | ||
43 | |||
44 | #undef DEBUG /* define this for verbose EDID parsing output */ | ||
45 | |||
46 | #ifdef DEBUG | ||
47 | #define DPRINTK(fmt, args...) printk(fmt,## args) | ||
48 | #else | ||
49 | #define DPRINTK(fmt, args...) | ||
50 | #endif | ||
51 | |||
52 | #define FBMON_FIX_HEADER 1 | ||
53 | #define FBMON_FIX_INPUT 2 | ||
54 | |||
55 | #ifdef CONFIG_FB_MODE_HELPERS | ||
56 | struct broken_edid { | ||
57 | u8 manufacturer[4]; | ||
58 | u32 model; | ||
59 | u32 fix; | ||
60 | }; | ||
61 | |||
62 | static struct broken_edid brokendb[] = { | ||
63 | /* DEC FR-PCXAV-YZ */ | ||
64 | { | ||
65 | .manufacturer = "DEC", | ||
66 | .model = 0x073a, | ||
67 | .fix = FBMON_FIX_HEADER, | ||
68 | }, | ||
69 | /* ViewSonic PF775a */ | ||
70 | { | ||
71 | .manufacturer = "VSC", | ||
72 | .model = 0x5a44, | ||
73 | .fix = FBMON_FIX_INPUT, | ||
74 | }, | ||
75 | }; | ||
76 | |||
77 | static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff, | ||
78 | 0xff, 0xff, 0xff, 0x00 | ||
79 | }; | ||
80 | |||
81 | static void copy_string(unsigned char *c, unsigned char *s) | ||
82 | { | ||
83 | int i; | ||
84 | c = c + 5; | ||
85 | for (i = 0; (i < 13 && *c != 0x0A); i++) | ||
86 | *(s++) = *(c++); | ||
87 | *s = 0; | ||
88 | while (i-- && (*--s == 0x20)) *s = 0; | ||
89 | } | ||
90 | |||
91 | static int check_edid(unsigned char *edid) | ||
92 | { | ||
93 | unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4]; | ||
94 | unsigned char *b; | ||
95 | u32 model; | ||
96 | int i, fix = 0, ret = 0; | ||
97 | |||
98 | manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@'; | ||
99 | manufacturer[1] = ((block[0] & 0x03) << 3) + | ||
100 | ((block[1] & 0xe0) >> 5) + '@'; | ||
101 | manufacturer[2] = (block[1] & 0x1f) + '@'; | ||
102 | manufacturer[3] = 0; | ||
103 | model = block[2] + (block[3] << 8); | ||
104 | |||
105 | for (i = 0; i < ARRAY_SIZE(brokendb); i++) { | ||
106 | if (!strncmp(manufacturer, brokendb[i].manufacturer, 4) && | ||
107 | brokendb[i].model == model) { | ||
108 | printk("fbmon: The EDID Block of " | ||
109 | "Manufacturer: %s Model: 0x%x is known to " | ||
110 | "be broken,\n", manufacturer, model); | ||
111 | fix = brokendb[i].fix; | ||
112 | break; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | switch (fix) { | ||
117 | case FBMON_FIX_HEADER: | ||
118 | for (i = 0; i < 8; i++) { | ||
119 | if (edid[i] != edid_v1_header[i]) | ||
120 | ret = fix; | ||
121 | } | ||
122 | break; | ||
123 | case FBMON_FIX_INPUT: | ||
124 | b = edid + EDID_STRUCT_DISPLAY; | ||
125 | /* Only if display is GTF capable will | ||
126 | the input type be reset to analog */ | ||
127 | if (b[4] & 0x01 && b[0] & 0x80) | ||
128 | ret = fix; | ||
129 | break; | ||
130 | } | ||
131 | |||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | static void fix_edid(unsigned char *edid, int fix) | ||
136 | { | ||
137 | unsigned char *b; | ||
138 | |||
139 | switch (fix) { | ||
140 | case FBMON_FIX_HEADER: | ||
141 | printk("fbmon: trying a header reconstruct\n"); | ||
142 | memcpy(edid, edid_v1_header, 8); | ||
143 | break; | ||
144 | case FBMON_FIX_INPUT: | ||
145 | printk("fbmon: trying to fix input type\n"); | ||
146 | b = edid + EDID_STRUCT_DISPLAY; | ||
147 | b[0] &= ~0x80; | ||
148 | edid[127] += 0x80; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | static int edid_checksum(unsigned char *edid) | ||
153 | { | ||
154 | unsigned char i, csum = 0, all_null = 0; | ||
155 | int err = 0, fix = check_edid(edid); | ||
156 | |||
157 | if (fix) | ||
158 | fix_edid(edid, fix); | ||
159 | |||
160 | for (i = 0; i < EDID_LENGTH; i++) { | ||
161 | csum += edid[i]; | ||
162 | all_null |= edid[i]; | ||
163 | } | ||
164 | |||
165 | if (csum == 0x00 && all_null) { | ||
166 | /* checksum passed, everything's good */ | ||
167 | err = 1; | ||
168 | } | ||
169 | |||
170 | return err; | ||
171 | } | ||
172 | |||
173 | static int edid_check_header(unsigned char *edid) | ||
174 | { | ||
175 | int i, err = 1, fix = check_edid(edid); | ||
176 | |||
177 | if (fix) | ||
178 | fix_edid(edid, fix); | ||
179 | |||
180 | for (i = 0; i < 8; i++) { | ||
181 | if (edid[i] != edid_v1_header[i]) | ||
182 | err = 0; | ||
183 | } | ||
184 | |||
185 | return err; | ||
186 | } | ||
187 | |||
188 | static void parse_vendor_block(unsigned char *block, struct fb_monspecs *specs) | ||
189 | { | ||
190 | specs->manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@'; | ||
191 | specs->manufacturer[1] = ((block[0] & 0x03) << 3) + | ||
192 | ((block[1] & 0xe0) >> 5) + '@'; | ||
193 | specs->manufacturer[2] = (block[1] & 0x1f) + '@'; | ||
194 | specs->manufacturer[3] = 0; | ||
195 | specs->model = block[2] + (block[3] << 8); | ||
196 | specs->serial = block[4] + (block[5] << 8) + | ||
197 | (block[6] << 16) + (block[7] << 24); | ||
198 | specs->year = block[9] + 1990; | ||
199 | specs->week = block[8]; | ||
200 | DPRINTK(" Manufacturer: %s\n", specs->manufacturer); | ||
201 | DPRINTK(" Model: %x\n", specs->model); | ||
202 | DPRINTK(" Serial#: %u\n", specs->serial); | ||
203 | DPRINTK(" Year: %u Week %u\n", specs->year, specs->week); | ||
204 | } | ||
205 | |||
206 | static void get_dpms_capabilities(unsigned char flags, | ||
207 | struct fb_monspecs *specs) | ||
208 | { | ||
209 | specs->dpms = 0; | ||
210 | if (flags & DPMS_ACTIVE_OFF) | ||
211 | specs->dpms |= FB_DPMS_ACTIVE_OFF; | ||
212 | if (flags & DPMS_SUSPEND) | ||
213 | specs->dpms |= FB_DPMS_SUSPEND; | ||
214 | if (flags & DPMS_STANDBY) | ||
215 | specs->dpms |= FB_DPMS_STANDBY; | ||
216 | DPRINTK(" DPMS: Active %s, Suspend %s, Standby %s\n", | ||
217 | (flags & DPMS_ACTIVE_OFF) ? "yes" : "no", | ||
218 | (flags & DPMS_SUSPEND) ? "yes" : "no", | ||
219 | (flags & DPMS_STANDBY) ? "yes" : "no"); | ||
220 | } | ||
221 | |||
222 | static void get_chroma(unsigned char *block, struct fb_monspecs *specs) | ||
223 | { | ||
224 | int tmp; | ||
225 | |||
226 | DPRINTK(" Chroma\n"); | ||
227 | /* Chromaticity data */ | ||
228 | tmp = ((block[5] & (3 << 6)) >> 6) | (block[0x7] << 2); | ||
229 | tmp *= 1000; | ||
230 | tmp += 512; | ||
231 | specs->chroma.redx = tmp/1024; | ||
232 | DPRINTK(" RedX: 0.%03d ", specs->chroma.redx); | ||
233 | |||
234 | tmp = ((block[5] & (3 << 4)) >> 4) | (block[0x8] << 2); | ||
235 | tmp *= 1000; | ||
236 | tmp += 512; | ||
237 | specs->chroma.redy = tmp/1024; | ||
238 | DPRINTK("RedY: 0.%03d\n", specs->chroma.redy); | ||
239 | |||
240 | tmp = ((block[5] & (3 << 2)) >> 2) | (block[0x9] << 2); | ||
241 | tmp *= 1000; | ||
242 | tmp += 512; | ||
243 | specs->chroma.greenx = tmp/1024; | ||
244 | DPRINTK(" GreenX: 0.%03d ", specs->chroma.greenx); | ||
245 | |||
246 | tmp = (block[5] & 3) | (block[0xa] << 2); | ||
247 | tmp *= 1000; | ||
248 | tmp += 512; | ||
249 | specs->chroma.greeny = tmp/1024; | ||
250 | DPRINTK("GreenY: 0.%03d\n", specs->chroma.greeny); | ||
251 | |||
252 | tmp = ((block[6] & (3 << 6)) >> 6) | (block[0xb] << 2); | ||
253 | tmp *= 1000; | ||
254 | tmp += 512; | ||
255 | specs->chroma.bluex = tmp/1024; | ||
256 | DPRINTK(" BlueX: 0.%03d ", specs->chroma.bluex); | ||
257 | |||
258 | tmp = ((block[6] & (3 << 4)) >> 4) | (block[0xc] << 2); | ||
259 | tmp *= 1000; | ||
260 | tmp += 512; | ||
261 | specs->chroma.bluey = tmp/1024; | ||
262 | DPRINTK("BlueY: 0.%03d\n", specs->chroma.bluey); | ||
263 | |||
264 | tmp = ((block[6] & (3 << 2)) >> 2) | (block[0xd] << 2); | ||
265 | tmp *= 1000; | ||
266 | tmp += 512; | ||
267 | specs->chroma.whitex = tmp/1024; | ||
268 | DPRINTK(" WhiteX: 0.%03d ", specs->chroma.whitex); | ||
269 | |||
270 | tmp = (block[6] & 3) | (block[0xe] << 2); | ||
271 | tmp *= 1000; | ||
272 | tmp += 512; | ||
273 | specs->chroma.whitey = tmp/1024; | ||
274 | DPRINTK("WhiteY: 0.%03d\n", specs->chroma.whitey); | ||
275 | } | ||
276 | |||
277 | static int edid_is_serial_block(unsigned char *block) | ||
278 | { | ||
279 | if ((block[0] == 0x00) && (block[1] == 0x00) && | ||
280 | (block[2] == 0x00) && (block[3] == 0xff) && | ||
281 | (block[4] == 0x00)) | ||
282 | return 1; | ||
283 | else | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | static int edid_is_ascii_block(unsigned char *block) | ||
288 | { | ||
289 | if ((block[0] == 0x00) && (block[1] == 0x00) && | ||
290 | (block[2] == 0x00) && (block[3] == 0xfe) && | ||
291 | (block[4] == 0x00)) | ||
292 | return 1; | ||
293 | else | ||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static int edid_is_limits_block(unsigned char *block) | ||
298 | { | ||
299 | if ((block[0] == 0x00) && (block[1] == 0x00) && | ||
300 | (block[2] == 0x00) && (block[3] == 0xfd) && | ||
301 | (block[4] == 0x00)) | ||
302 | return 1; | ||
303 | else | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | static int edid_is_monitor_block(unsigned char *block) | ||
308 | { | ||
309 | if ((block[0] == 0x00) && (block[1] == 0x00) && | ||
310 | (block[2] == 0x00) && (block[3] == 0xfc) && | ||
311 | (block[4] == 0x00)) | ||
312 | return 1; | ||
313 | else | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | static void calc_mode_timings(int xres, int yres, int refresh, struct fb_videomode *mode) | ||
318 | { | ||
319 | struct fb_var_screeninfo var; | ||
320 | struct fb_info info; | ||
321 | |||
322 | var.xres = xres; | ||
323 | var.yres = yres; | ||
324 | fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, | ||
325 | refresh, &var, &info); | ||
326 | mode->xres = xres; | ||
327 | mode->yres = yres; | ||
328 | mode->pixclock = var.pixclock; | ||
329 | mode->refresh = refresh; | ||
330 | mode->left_margin = var.left_margin; | ||
331 | mode->right_margin = var.right_margin; | ||
332 | mode->upper_margin = var.upper_margin; | ||
333 | mode->lower_margin = var.lower_margin; | ||
334 | mode->hsync_len = var.hsync_len; | ||
335 | mode->vsync_len = var.vsync_len; | ||
336 | mode->vmode = 0; | ||
337 | mode->sync = 0; | ||
338 | } | ||
339 | |||
340 | static int get_est_timing(unsigned char *block, struct fb_videomode *mode) | ||
341 | { | ||
342 | int num = 0; | ||
343 | unsigned char c; | ||
344 | |||
345 | c = block[0]; | ||
346 | if (c&0x80) { | ||
347 | calc_mode_timings(720, 400, 70, &mode[num]); | ||
348 | mode[num++].flag = FB_MODE_IS_CALCULATED; | ||
349 | DPRINTK(" 720x400@70Hz\n"); | ||
350 | } | ||
351 | if (c&0x40) { | ||
352 | calc_mode_timings(720, 400, 88, &mode[num]); | ||
353 | mode[num++].flag = FB_MODE_IS_CALCULATED; | ||
354 | DPRINTK(" 720x400@88Hz\n"); | ||
355 | } | ||
356 | if (c&0x20) { | ||
357 | mode[num++] = vesa_modes[3]; | ||
358 | DPRINTK(" 640x480@60Hz\n"); | ||
359 | } | ||
360 | if (c&0x10) { | ||
361 | calc_mode_timings(640, 480, 67, &mode[num]); | ||
362 | mode[num++].flag = FB_MODE_IS_CALCULATED; | ||
363 | DPRINTK(" 640x480@67Hz\n"); | ||
364 | } | ||
365 | if (c&0x08) { | ||
366 | mode[num++] = vesa_modes[4]; | ||
367 | DPRINTK(" 640x480@72Hz\n"); | ||
368 | } | ||
369 | if (c&0x04) { | ||
370 | mode[num++] = vesa_modes[5]; | ||
371 | DPRINTK(" 640x480@75Hz\n"); | ||
372 | } | ||
373 | if (c&0x02) { | ||
374 | mode[num++] = vesa_modes[7]; | ||
375 | DPRINTK(" 800x600@56Hz\n"); | ||
376 | } | ||
377 | if (c&0x01) { | ||
378 | mode[num++] = vesa_modes[8]; | ||
379 | DPRINTK(" 800x600@60Hz\n"); | ||
380 | } | ||
381 | |||
382 | c = block[1]; | ||
383 | if (c&0x80) { | ||
384 | mode[num++] = vesa_modes[9]; | ||
385 | DPRINTK(" 800x600@72Hz\n"); | ||
386 | } | ||
387 | if (c&0x40) { | ||
388 | mode[num++] = vesa_modes[10]; | ||
389 | DPRINTK(" 800x600@75Hz\n"); | ||
390 | } | ||
391 | if (c&0x20) { | ||
392 | calc_mode_timings(832, 624, 75, &mode[num]); | ||
393 | mode[num++].flag = FB_MODE_IS_CALCULATED; | ||
394 | DPRINTK(" 832x624@75Hz\n"); | ||
395 | } | ||
396 | if (c&0x10) { | ||
397 | mode[num++] = vesa_modes[12]; | ||
398 | DPRINTK(" 1024x768@87Hz Interlaced\n"); | ||
399 | } | ||
400 | if (c&0x08) { | ||
401 | mode[num++] = vesa_modes[13]; | ||
402 | DPRINTK(" 1024x768@60Hz\n"); | ||
403 | } | ||
404 | if (c&0x04) { | ||
405 | mode[num++] = vesa_modes[14]; | ||
406 | DPRINTK(" 1024x768@70Hz\n"); | ||
407 | } | ||
408 | if (c&0x02) { | ||
409 | mode[num++] = vesa_modes[15]; | ||
410 | DPRINTK(" 1024x768@75Hz\n"); | ||
411 | } | ||
412 | if (c&0x01) { | ||
413 | mode[num++] = vesa_modes[21]; | ||
414 | DPRINTK(" 1280x1024@75Hz\n"); | ||
415 | } | ||
416 | c = block[2]; | ||
417 | if (c&0x80) { | ||
418 | mode[num++] = vesa_modes[17]; | ||
419 | DPRINTK(" 1152x870@75Hz\n"); | ||
420 | } | ||
421 | DPRINTK(" Manufacturer's mask: %x\n",c&0x7F); | ||
422 | return num; | ||
423 | } | ||
424 | |||
425 | static int get_std_timing(unsigned char *block, struct fb_videomode *mode) | ||
426 | { | ||
427 | int xres, yres = 0, refresh, ratio, i; | ||
428 | |||
429 | xres = (block[0] + 31) * 8; | ||
430 | if (xres <= 256) | ||
431 | return 0; | ||
432 | |||
433 | ratio = (block[1] & 0xc0) >> 6; | ||
434 | switch (ratio) { | ||
435 | case 0: | ||
436 | yres = xres; | ||
437 | break; | ||
438 | case 1: | ||
439 | yres = (xres * 3)/4; | ||
440 | break; | ||
441 | case 2: | ||
442 | yres = (xres * 4)/5; | ||
443 | break; | ||
444 | case 3: | ||
445 | yres = (xres * 9)/16; | ||
446 | break; | ||
447 | } | ||
448 | refresh = (block[1] & 0x3f) + 60; | ||
449 | |||
450 | DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh); | ||
451 | for (i = 0; i < VESA_MODEDB_SIZE; i++) { | ||
452 | if (vesa_modes[i].xres == xres && | ||
453 | vesa_modes[i].yres == yres && | ||
454 | vesa_modes[i].refresh == refresh) { | ||
455 | *mode = vesa_modes[i]; | ||
456 | mode->flag |= FB_MODE_IS_STANDARD; | ||
457 | return 1; | ||
458 | } | ||
459 | } | ||
460 | calc_mode_timings(xres, yres, refresh, mode); | ||
461 | return 1; | ||
462 | } | ||
463 | |||
464 | static int get_dst_timing(unsigned char *block, | ||
465 | struct fb_videomode *mode) | ||
466 | { | ||
467 | int j, num = 0; | ||
468 | |||
469 | for (j = 0; j < 6; j++, block+= STD_TIMING_DESCRIPTION_SIZE) | ||
470 | num += get_std_timing(block, &mode[num]); | ||
471 | |||
472 | return num; | ||
473 | } | ||
474 | |||
475 | static void get_detailed_timing(unsigned char *block, | ||
476 | struct fb_videomode *mode) | ||
477 | { | ||
478 | mode->xres = H_ACTIVE; | ||
479 | mode->yres = V_ACTIVE; | ||
480 | mode->pixclock = PIXEL_CLOCK; | ||
481 | mode->pixclock /= 1000; | ||
482 | mode->pixclock = KHZ2PICOS(mode->pixclock); | ||
483 | mode->right_margin = H_SYNC_OFFSET; | ||
484 | mode->left_margin = (H_ACTIVE + H_BLANKING) - | ||
485 | (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); | ||
486 | mode->upper_margin = V_BLANKING - V_SYNC_OFFSET - | ||
487 | V_SYNC_WIDTH; | ||
488 | mode->lower_margin = V_SYNC_OFFSET; | ||
489 | mode->hsync_len = H_SYNC_WIDTH; | ||
490 | mode->vsync_len = V_SYNC_WIDTH; | ||
491 | if (HSYNC_POSITIVE) | ||
492 | mode->sync |= FB_SYNC_HOR_HIGH_ACT; | ||
493 | if (VSYNC_POSITIVE) | ||
494 | mode->sync |= FB_SYNC_VERT_HIGH_ACT; | ||
495 | mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) * | ||
496 | (V_ACTIVE + V_BLANKING)); | ||
497 | mode->vmode = 0; | ||
498 | mode->flag = FB_MODE_IS_DETAILED; | ||
499 | |||
500 | DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000); | ||
501 | DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET, | ||
502 | H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING); | ||
503 | DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET, | ||
504 | V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING); | ||
505 | DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-", | ||
506 | (VSYNC_POSITIVE) ? "+" : "-"); | ||
507 | } | ||
508 | |||
509 | /** | ||
510 | * fb_create_modedb - create video mode database | ||
511 | * @edid: EDID data | ||
512 | * @dbsize: database size | ||
513 | * | ||
514 | * RETURNS: struct fb_videomode, @dbsize contains length of database | ||
515 | * | ||
516 | * DESCRIPTION: | ||
517 | * This function builds a mode database using the contents of the EDID | ||
518 | * data | ||
519 | */ | ||
520 | static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) | ||
521 | { | ||
522 | struct fb_videomode *mode, *m; | ||
523 | unsigned char *block; | ||
524 | int num = 0, i; | ||
525 | |||
526 | mode = kmalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL); | ||
527 | if (mode == NULL) | ||
528 | return NULL; | ||
529 | memset(mode, 0, 50 * sizeof(struct fb_videomode)); | ||
530 | |||
531 | if (edid == NULL || !edid_checksum(edid) || | ||
532 | !edid_check_header(edid)) { | ||
533 | kfree(mode); | ||
534 | return NULL; | ||
535 | } | ||
536 | |||
537 | *dbsize = 0; | ||
538 | |||
539 | DPRINTK(" Supported VESA Modes\n"); | ||
540 | block = edid + ESTABLISHED_TIMING_1; | ||
541 | num += get_est_timing(block, &mode[num]); | ||
542 | |||
543 | DPRINTK(" Standard Timings\n"); | ||
544 | block = edid + STD_TIMING_DESCRIPTIONS_START; | ||
545 | for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE) | ||
546 | num += get_std_timing(block, &mode[num]); | ||
547 | |||
548 | DPRINTK(" Detailed Timings\n"); | ||
549 | block = edid + DETAILED_TIMING_DESCRIPTIONS_START; | ||
550 | for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) { | ||
551 | int first = 1; | ||
552 | |||
553 | if (block[0] == 0x00 && block[1] == 0x00) { | ||
554 | if (block[3] == 0xfa) { | ||
555 | num += get_dst_timing(block + 5, &mode[num]); | ||
556 | } | ||
557 | } else { | ||
558 | get_detailed_timing(block, &mode[num]); | ||
559 | if (first) { | ||
560 | mode[num].flag |= FB_MODE_IS_FIRST; | ||
561 | first = 0; | ||
562 | } | ||
563 | num++; | ||
564 | } | ||
565 | } | ||
566 | |||
567 | /* Yikes, EDID data is totally useless */ | ||
568 | if (!num) { | ||
569 | kfree(mode); | ||
570 | return NULL; | ||
571 | } | ||
572 | |||
573 | *dbsize = num; | ||
574 | m = kmalloc(num * sizeof(struct fb_videomode), GFP_KERNEL); | ||
575 | if (!m) | ||
576 | return mode; | ||
577 | memmove(m, mode, num * sizeof(struct fb_videomode)); | ||
578 | kfree(mode); | ||
579 | return m; | ||
580 | } | ||
581 | |||
582 | /** | ||
583 | * fb_destroy_modedb - destroys mode database | ||
584 | * @modedb: mode database to destroy | ||
585 | * | ||
586 | * DESCRIPTION: | ||
587 | * Destroy mode database created by fb_create_modedb | ||
588 | */ | ||
589 | void fb_destroy_modedb(struct fb_videomode *modedb) | ||
590 | { | ||
591 | kfree(modedb); | ||
592 | } | ||
593 | |||
594 | static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) | ||
595 | { | ||
596 | int i, retval = 1; | ||
597 | unsigned char *block; | ||
598 | |||
599 | block = edid + DETAILED_TIMING_DESCRIPTIONS_START; | ||
600 | |||
601 | DPRINTK(" Monitor Operating Limits: "); | ||
602 | for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { | ||
603 | if (edid_is_limits_block(block)) { | ||
604 | specs->hfmin = H_MIN_RATE * 1000; | ||
605 | specs->hfmax = H_MAX_RATE * 1000; | ||
606 | specs->vfmin = V_MIN_RATE; | ||
607 | specs->vfmax = V_MAX_RATE; | ||
608 | specs->dclkmax = MAX_PIXEL_CLOCK * 1000000; | ||
609 | specs->gtf = (GTF_SUPPORT) ? 1 : 0; | ||
610 | retval = 0; | ||
611 | DPRINTK("From EDID\n"); | ||
612 | break; | ||
613 | } | ||
614 | } | ||
615 | |||
616 | /* estimate monitor limits based on modes supported */ | ||
617 | if (retval) { | ||
618 | struct fb_videomode *modes; | ||
619 | int num_modes, i, hz, hscan, pixclock; | ||
620 | |||
621 | modes = fb_create_modedb(edid, &num_modes); | ||
622 | if (!modes) { | ||
623 | DPRINTK("None Available\n"); | ||
624 | return 1; | ||
625 | } | ||
626 | |||
627 | retval = 0; | ||
628 | for (i = 0; i < num_modes; i++) { | ||
629 | hz = modes[i].refresh; | ||
630 | pixclock = PICOS2KHZ(modes[i].pixclock) * 1000; | ||
631 | hscan = (modes[i].yres * 105 * hz + 5000)/100; | ||
632 | |||
633 | if (specs->dclkmax == 0 || specs->dclkmax < pixclock) | ||
634 | specs->dclkmax = pixclock; | ||
635 | if (specs->dclkmin == 0 || specs->dclkmin > pixclock) | ||
636 | specs->dclkmin = pixclock; | ||
637 | if (specs->hfmax == 0 || specs->hfmax < hscan) | ||
638 | specs->hfmax = hscan; | ||
639 | if (specs->hfmin == 0 || specs->hfmin > hscan) | ||
640 | specs->hfmin = hscan; | ||
641 | if (specs->vfmax == 0 || specs->vfmax < hz) | ||
642 | specs->vfmax = hz; | ||
643 | if (specs->vfmin == 0 || specs->vfmin > hz) | ||
644 | specs->vfmin = hz; | ||
645 | } | ||
646 | DPRINTK("Extrapolated\n"); | ||
647 | fb_destroy_modedb(modes); | ||
648 | } | ||
649 | DPRINTK(" H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n", | ||
650 | specs->hfmin/1000, specs->hfmax/1000, specs->vfmin, | ||
651 | specs->vfmax, specs->dclkmax/1000000); | ||
652 | return retval; | ||
653 | } | ||
654 | |||
655 | static void get_monspecs(unsigned char *edid, struct fb_monspecs *specs) | ||
656 | { | ||
657 | unsigned char c, *block; | ||
658 | |||
659 | block = edid + EDID_STRUCT_DISPLAY; | ||
660 | |||
661 | fb_get_monitor_limits(edid, specs); | ||
662 | |||
663 | c = block[0] & 0x80; | ||
664 | specs->input = 0; | ||
665 | if (c) { | ||
666 | specs->input |= FB_DISP_DDI; | ||
667 | DPRINTK(" Digital Display Input"); | ||
668 | } else { | ||
669 | DPRINTK(" Analog Display Input: Input Voltage - "); | ||
670 | switch ((block[0] & 0x60) >> 5) { | ||
671 | case 0: | ||
672 | DPRINTK("0.700V/0.300V"); | ||
673 | specs->input |= FB_DISP_ANA_700_300; | ||
674 | break; | ||
675 | case 1: | ||
676 | DPRINTK("0.714V/0.286V"); | ||
677 | specs->input |= FB_DISP_ANA_714_286; | ||
678 | break; | ||
679 | case 2: | ||
680 | DPRINTK("1.000V/0.400V"); | ||
681 | specs->input |= FB_DISP_ANA_1000_400; | ||
682 | break; | ||
683 | case 3: | ||
684 | DPRINTK("0.700V/0.000V"); | ||
685 | specs->input |= FB_DISP_ANA_700_000; | ||
686 | break; | ||
687 | } | ||
688 | } | ||
689 | DPRINTK("\n Sync: "); | ||
690 | c = block[0] & 0x10; | ||
691 | if (c) | ||
692 | DPRINTK(" Configurable signal level\n"); | ||
693 | c = block[0] & 0x0f; | ||
694 | specs->signal = 0; | ||
695 | if (c & 0x10) { | ||
696 | DPRINTK("Blank to Blank "); | ||
697 | specs->signal |= FB_SIGNAL_BLANK_BLANK; | ||
698 | } | ||
699 | if (c & 0x08) { | ||
700 | DPRINTK("Separate "); | ||
701 | specs->signal |= FB_SIGNAL_SEPARATE; | ||
702 | } | ||
703 | if (c & 0x04) { | ||
704 | DPRINTK("Composite "); | ||
705 | specs->signal |= FB_SIGNAL_COMPOSITE; | ||
706 | } | ||
707 | if (c & 0x02) { | ||
708 | DPRINTK("Sync on Green "); | ||
709 | specs->signal |= FB_SIGNAL_SYNC_ON_GREEN; | ||
710 | } | ||
711 | if (c & 0x01) { | ||
712 | DPRINTK("Serration on "); | ||
713 | specs->signal |= FB_SIGNAL_SERRATION_ON; | ||
714 | } | ||
715 | DPRINTK("\n"); | ||
716 | specs->max_x = block[1]; | ||
717 | specs->max_y = block[2]; | ||
718 | DPRINTK(" Max H-size in cm: "); | ||
719 | if (specs->max_x) | ||
720 | DPRINTK("%d\n", specs->max_x); | ||
721 | else | ||
722 | DPRINTK("variable\n"); | ||
723 | DPRINTK(" Max V-size in cm: "); | ||
724 | if (specs->max_y) | ||
725 | DPRINTK("%d\n", specs->max_y); | ||
726 | else | ||
727 | DPRINTK("variable\n"); | ||
728 | |||
729 | c = block[3]; | ||
730 | specs->gamma = c+100; | ||
731 | DPRINTK(" Gamma: "); | ||
732 | DPRINTK("%d.%d\n", specs->gamma/100, specs->gamma % 100); | ||
733 | |||
734 | get_dpms_capabilities(block[4], specs); | ||
735 | |||
736 | switch ((block[4] & 0x18) >> 3) { | ||
737 | case 0: | ||
738 | DPRINTK(" Monochrome/Grayscale\n"); | ||
739 | specs->input |= FB_DISP_MONO; | ||
740 | break; | ||
741 | case 1: | ||
742 | DPRINTK(" RGB Color Display\n"); | ||
743 | specs->input |= FB_DISP_RGB; | ||
744 | break; | ||
745 | case 2: | ||
746 | DPRINTK(" Non-RGB Multicolor Display\n"); | ||
747 | specs->input |= FB_DISP_MULTI; | ||
748 | break; | ||
749 | default: | ||
750 | DPRINTK(" Unknown\n"); | ||
751 | specs->input |= FB_DISP_UNKNOWN; | ||
752 | break; | ||
753 | } | ||
754 | |||
755 | get_chroma(block, specs); | ||
756 | |||
757 | specs->misc = 0; | ||
758 | c = block[4] & 0x7; | ||
759 | if (c & 0x04) { | ||
760 | DPRINTK(" Default color format is primary\n"); | ||
761 | specs->misc |= FB_MISC_PRIM_COLOR; | ||
762 | } | ||
763 | if (c & 0x02) { | ||
764 | DPRINTK(" First DETAILED Timing is preferred\n"); | ||
765 | specs->misc |= FB_MISC_1ST_DETAIL; | ||
766 | } | ||
767 | if (c & 0x01) { | ||
768 | printk(" Display is GTF capable\n"); | ||
769 | specs->gtf = 1; | ||
770 | } | ||
771 | } | ||
772 | |||
773 | static int edid_is_timing_block(unsigned char *block) | ||
774 | { | ||
775 | if ((block[0] != 0x00) || (block[1] != 0x00) || | ||
776 | (block[2] != 0x00) || (block[4] != 0x00)) | ||
777 | return 1; | ||
778 | else | ||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) | ||
783 | { | ||
784 | int i; | ||
785 | unsigned char *block; | ||
786 | |||
787 | if (edid == NULL || var == NULL) | ||
788 | return 1; | ||
789 | |||
790 | if (!(edid_checksum(edid))) | ||
791 | return 1; | ||
792 | |||
793 | if (!(edid_check_header(edid))) | ||
794 | return 1; | ||
795 | |||
796 | block = edid + DETAILED_TIMING_DESCRIPTIONS_START; | ||
797 | |||
798 | for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { | ||
799 | if (edid_is_timing_block(block)) { | ||
800 | var->xres = var->xres_virtual = H_ACTIVE; | ||
801 | var->yres = var->yres_virtual = V_ACTIVE; | ||
802 | var->height = var->width = -1; | ||
803 | var->right_margin = H_SYNC_OFFSET; | ||
804 | var->left_margin = (H_ACTIVE + H_BLANKING) - | ||
805 | (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); | ||
806 | var->upper_margin = V_BLANKING - V_SYNC_OFFSET - | ||
807 | V_SYNC_WIDTH; | ||
808 | var->lower_margin = V_SYNC_OFFSET; | ||
809 | var->hsync_len = H_SYNC_WIDTH; | ||
810 | var->vsync_len = V_SYNC_WIDTH; | ||
811 | var->pixclock = PIXEL_CLOCK; | ||
812 | var->pixclock /= 1000; | ||
813 | var->pixclock = KHZ2PICOS(var->pixclock); | ||
814 | |||
815 | if (HSYNC_POSITIVE) | ||
816 | var->sync |= FB_SYNC_HOR_HIGH_ACT; | ||
817 | if (VSYNC_POSITIVE) | ||
818 | var->sync |= FB_SYNC_VERT_HIGH_ACT; | ||
819 | return 0; | ||
820 | } | ||
821 | } | ||
822 | return 1; | ||
823 | } | ||
824 | |||
825 | void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) | ||
826 | { | ||
827 | unsigned char *block; | ||
828 | int i; | ||
829 | |||
830 | if (edid == NULL) | ||
831 | return; | ||
832 | |||
833 | if (!(edid_checksum(edid))) | ||
834 | return; | ||
835 | |||
836 | if (!(edid_check_header(edid))) | ||
837 | return; | ||
838 | |||
839 | memset(specs, 0, sizeof(struct fb_monspecs)); | ||
840 | |||
841 | specs->version = edid[EDID_STRUCT_VERSION]; | ||
842 | specs->revision = edid[EDID_STRUCT_REVISION]; | ||
843 | |||
844 | DPRINTK("========================================\n"); | ||
845 | DPRINTK("Display Information (EDID)\n"); | ||
846 | DPRINTK("========================================\n"); | ||
847 | DPRINTK(" EDID Version %d.%d\n", (int) specs->version, | ||
848 | (int) specs->revision); | ||
849 | |||
850 | parse_vendor_block(edid + ID_MANUFACTURER_NAME, specs); | ||
851 | |||
852 | block = edid + DETAILED_TIMING_DESCRIPTIONS_START; | ||
853 | for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { | ||
854 | if (edid_is_serial_block(block)) { | ||
855 | copy_string(block, specs->serial_no); | ||
856 | DPRINTK(" Serial Number: %s\n", specs->serial_no); | ||
857 | } else if (edid_is_ascii_block(block)) { | ||
858 | copy_string(block, specs->ascii); | ||
859 | DPRINTK(" ASCII Block: %s\n", specs->ascii); | ||
860 | } else if (edid_is_monitor_block(block)) { | ||
861 | copy_string(block, specs->monitor); | ||
862 | DPRINTK(" Monitor Name: %s\n", specs->monitor); | ||
863 | } | ||
864 | } | ||
865 | |||
866 | DPRINTK(" Display Characteristics:\n"); | ||
867 | get_monspecs(edid, specs); | ||
868 | |||
869 | specs->modedb = fb_create_modedb(edid, &specs->modedb_len); | ||
870 | DPRINTK("========================================\n"); | ||
871 | } | ||
872 | |||
873 | /* | ||
874 | * VESA Generalized Timing Formula (GTF) | ||
875 | */ | ||
876 | |||
877 | #define FLYBACK 550 | ||
878 | #define V_FRONTPORCH 1 | ||
879 | #define H_OFFSET 40 | ||
880 | #define H_SCALEFACTOR 20 | ||
881 | #define H_BLANKSCALE 128 | ||
882 | #define H_GRADIENT 600 | ||
883 | #define C_VAL 30 | ||
884 | #define M_VAL 300 | ||
885 | |||
886 | struct __fb_timings { | ||
887 | u32 dclk; | ||
888 | u32 hfreq; | ||
889 | u32 vfreq; | ||
890 | u32 hactive; | ||
891 | u32 vactive; | ||
892 | u32 hblank; | ||
893 | u32 vblank; | ||
894 | u32 htotal; | ||
895 | u32 vtotal; | ||
896 | }; | ||
897 | |||
898 | /** | ||
899 | * fb_get_vblank - get vertical blank time | ||
900 | * @hfreq: horizontal freq | ||
901 | * | ||
902 | * DESCRIPTION: | ||
903 | * vblank = right_margin + vsync_len + left_margin | ||
904 | * | ||
905 | * given: right_margin = 1 (V_FRONTPORCH) | ||
906 | * vsync_len = 3 | ||
907 | * flyback = 550 | ||
908 | * | ||
909 | * flyback * hfreq | ||
910 | * left_margin = --------------- - vsync_len | ||
911 | * 1000000 | ||
912 | */ | ||
913 | static u32 fb_get_vblank(u32 hfreq) | ||
914 | { | ||
915 | u32 vblank; | ||
916 | |||
917 | vblank = (hfreq * FLYBACK)/1000; | ||
918 | vblank = (vblank + 500)/1000; | ||
919 | return (vblank + V_FRONTPORCH); | ||
920 | } | ||
921 | |||
922 | /** | ||
923 | * fb_get_hblank_by_freq - get horizontal blank time given hfreq | ||
924 | * @hfreq: horizontal freq | ||
925 | * @xres: horizontal resolution in pixels | ||
926 | * | ||
927 | * DESCRIPTION: | ||
928 | * | ||
929 | * xres * duty_cycle | ||
930 | * hblank = ------------------ | ||
931 | * 100 - duty_cycle | ||
932 | * | ||
933 | * duty cycle = percent of htotal assigned to inactive display | ||
934 | * duty cycle = C - (M/Hfreq) | ||
935 | * | ||
936 | * where: C = ((offset - scale factor) * blank_scale) | ||
937 | * -------------------------------------- + scale factor | ||
938 | * 256 | ||
939 | * M = blank_scale * gradient | ||
940 | * | ||
941 | */ | ||
942 | static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres) | ||
943 | { | ||
944 | u32 c_val, m_val, duty_cycle, hblank; | ||
945 | |||
946 | c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 + | ||
947 | H_SCALEFACTOR) * 1000; | ||
948 | m_val = (H_BLANKSCALE * H_GRADIENT)/256; | ||
949 | m_val = (m_val * 1000000)/hfreq; | ||
950 | duty_cycle = c_val - m_val; | ||
951 | hblank = (xres * duty_cycle)/(100000 - duty_cycle); | ||
952 | return (hblank); | ||
953 | } | ||
954 | |||
955 | /** | ||
956 | * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock | ||
957 | * @dclk: pixelclock in Hz | ||
958 | * @xres: horizontal resolution in pixels | ||
959 | * | ||
960 | * DESCRIPTION: | ||
961 | * | ||
962 | * xres * duty_cycle | ||
963 | * hblank = ------------------ | ||
964 | * 100 - duty_cycle | ||
965 | * | ||
966 | * duty cycle = percent of htotal assigned to inactive display | ||
967 | * duty cycle = C - (M * h_period) | ||
968 | * | ||
969 | * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100 | ||
970 | * ----------------------------------------------- | ||
971 | * 2 * M | ||
972 | * M = 300; | ||
973 | * C = 30; | ||
974 | |||
975 | */ | ||
976 | static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres) | ||
977 | { | ||
978 | u32 duty_cycle, h_period, hblank; | ||
979 | |||
980 | dclk /= 1000; | ||
981 | h_period = 100 - C_VAL; | ||
982 | h_period *= h_period; | ||
983 | h_period += (M_VAL * xres * 2 * 1000)/(5 * dclk); | ||
984 | h_period *=10000; | ||
985 | |||
986 | h_period = int_sqrt(h_period); | ||
987 | h_period -= (100 - C_VAL) * 100; | ||
988 | h_period *= 1000; | ||
989 | h_period /= 2 * M_VAL; | ||
990 | |||
991 | duty_cycle = C_VAL * 1000 - (M_VAL * h_period)/100; | ||
992 | hblank = (xres * duty_cycle)/(100000 - duty_cycle) + 8; | ||
993 | hblank &= ~15; | ||
994 | return (hblank); | ||
995 | } | ||
996 | |||
997 | /** | ||
998 | * fb_get_hfreq - estimate hsync | ||
999 | * @vfreq: vertical refresh rate | ||
1000 | * @yres: vertical resolution | ||
1001 | * | ||
1002 | * DESCRIPTION: | ||
1003 | * | ||
1004 | * (yres + front_port) * vfreq * 1000000 | ||
1005 | * hfreq = ------------------------------------- | ||
1006 | * (1000000 - (vfreq * FLYBACK) | ||
1007 | * | ||
1008 | */ | ||
1009 | |||
1010 | static u32 fb_get_hfreq(u32 vfreq, u32 yres) | ||
1011 | { | ||
1012 | u32 divisor, hfreq; | ||
1013 | |||
1014 | divisor = (1000000 - (vfreq * FLYBACK))/1000; | ||
1015 | hfreq = (yres + V_FRONTPORCH) * vfreq * 1000; | ||
1016 | return (hfreq/divisor); | ||
1017 | } | ||
1018 | |||
1019 | static void fb_timings_vfreq(struct __fb_timings *timings) | ||
1020 | { | ||
1021 | timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive); | ||
1022 | timings->vblank = fb_get_vblank(timings->hfreq); | ||
1023 | timings->vtotal = timings->vactive + timings->vblank; | ||
1024 | timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, | ||
1025 | timings->hactive); | ||
1026 | timings->htotal = timings->hactive + timings->hblank; | ||
1027 | timings->dclk = timings->htotal * timings->hfreq; | ||
1028 | } | ||
1029 | |||
1030 | static void fb_timings_hfreq(struct __fb_timings *timings) | ||
1031 | { | ||
1032 | timings->vblank = fb_get_vblank(timings->hfreq); | ||
1033 | timings->vtotal = timings->vactive + timings->vblank; | ||
1034 | timings->vfreq = timings->hfreq/timings->vtotal; | ||
1035 | timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, | ||
1036 | timings->hactive); | ||
1037 | timings->htotal = timings->hactive + timings->hblank; | ||
1038 | timings->dclk = timings->htotal * timings->hfreq; | ||
1039 | } | ||
1040 | |||
1041 | static void fb_timings_dclk(struct __fb_timings *timings) | ||
1042 | { | ||
1043 | timings->hblank = fb_get_hblank_by_dclk(timings->dclk, | ||
1044 | timings->hactive); | ||
1045 | timings->htotal = timings->hactive + timings->hblank; | ||
1046 | timings->hfreq = timings->dclk/timings->htotal; | ||
1047 | timings->vblank = fb_get_vblank(timings->hfreq); | ||
1048 | timings->vtotal = timings->vactive + timings->vblank; | ||
1049 | timings->vfreq = timings->hfreq/timings->vtotal; | ||
1050 | } | ||
1051 | |||
1052 | /* | ||
1053 | * fb_get_mode - calculates video mode using VESA GTF | ||
1054 | * @flags: if: 0 - maximize vertical refresh rate | ||
1055 | * 1 - vrefresh-driven calculation; | ||
1056 | * 2 - hscan-driven calculation; | ||
1057 | * 3 - pixelclock-driven calculation; | ||
1058 | * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock | ||
1059 | * @var: pointer to fb_var_screeninfo | ||
1060 | * @info: pointer to fb_info | ||
1061 | * | ||
1062 | * DESCRIPTION: | ||
1063 | * Calculates video mode based on monitor specs using VESA GTF. | ||
1064 | * The GTF is best for VESA GTF compliant monitors but is | ||
1065 | * specifically formulated to work for older monitors as well. | ||
1066 | * | ||
1067 | * If @flag==0, the function will attempt to maximize the | ||
1068 | * refresh rate. Otherwise, it will calculate timings based on | ||
1069 | * the flag and accompanying value. | ||
1070 | * | ||
1071 | * If FB_IGNOREMON bit is set in @flags, monitor specs will be | ||
1072 | * ignored and @var will be filled with the calculated timings. | ||
1073 | * | ||
1074 | * All calculations are based on the VESA GTF Spreadsheet | ||
1075 | * available at VESA's public ftp (http://www.vesa.org). | ||
1076 | * | ||
1077 | * NOTES: | ||
1078 | * The timings generated by the GTF will be different from VESA | ||
1079 | * DMT. It might be a good idea to keep a table of standard | ||
1080 | * VESA modes as well. The GTF may also not work for some displays, | ||
1081 | * such as, and especially, analog TV. | ||
1082 | * | ||
1083 | * REQUIRES: | ||
1084 | * A valid info->monspecs, otherwise 'safe numbers' will be used. | ||
1085 | */ | ||
1086 | int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info) | ||
1087 | { | ||
1088 | struct __fb_timings timings; | ||
1089 | u32 interlace = 1, dscan = 1; | ||
1090 | u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax; | ||
1091 | |||
1092 | /* | ||
1093 | * If monspecs are invalid, use values that are enough | ||
1094 | * for 640x480@60 | ||
1095 | */ | ||
1096 | if (!info->monspecs.hfmax || !info->monspecs.vfmax || | ||
1097 | !info->monspecs.dclkmax || | ||
1098 | info->monspecs.hfmax < info->monspecs.hfmin || | ||
1099 | info->monspecs.vfmax < info->monspecs.vfmin || | ||
1100 | info->monspecs.dclkmax < info->monspecs.dclkmin) { | ||
1101 | hfmin = 29000; hfmax = 30000; | ||
1102 | vfmin = 60; vfmax = 60; | ||
1103 | dclkmin = 0; dclkmax = 25000000; | ||
1104 | } else { | ||
1105 | hfmin = info->monspecs.hfmin; | ||
1106 | hfmax = info->monspecs.hfmax; | ||
1107 | vfmin = info->monspecs.vfmin; | ||
1108 | vfmax = info->monspecs.vfmax; | ||
1109 | dclkmin = info->monspecs.dclkmin; | ||
1110 | dclkmax = info->monspecs.dclkmax; | ||
1111 | } | ||
1112 | |||
1113 | memset(&timings, 0, sizeof(struct __fb_timings)); | ||
1114 | timings.hactive = var->xres; | ||
1115 | timings.vactive = var->yres; | ||
1116 | if (var->vmode & FB_VMODE_INTERLACED) { | ||
1117 | timings.vactive /= 2; | ||
1118 | interlace = 2; | ||
1119 | } | ||
1120 | if (var->vmode & FB_VMODE_DOUBLE) { | ||
1121 | timings.vactive *= 2; | ||
1122 | dscan = 2; | ||
1123 | } | ||
1124 | |||
1125 | switch (flags & ~FB_IGNOREMON) { | ||
1126 | case FB_MAXTIMINGS: /* maximize refresh rate */ | ||
1127 | timings.hfreq = hfmax; | ||
1128 | fb_timings_hfreq(&timings); | ||
1129 | if (timings.vfreq > vfmax) { | ||
1130 | timings.vfreq = vfmax; | ||
1131 | fb_timings_vfreq(&timings); | ||
1132 | } | ||
1133 | if (timings.dclk > dclkmax) { | ||
1134 | timings.dclk = dclkmax; | ||
1135 | fb_timings_dclk(&timings); | ||
1136 | } | ||
1137 | break; | ||
1138 | case FB_VSYNCTIMINGS: /* vrefresh driven */ | ||
1139 | timings.vfreq = val; | ||
1140 | fb_timings_vfreq(&timings); | ||
1141 | break; | ||
1142 | case FB_HSYNCTIMINGS: /* hsync driven */ | ||
1143 | timings.hfreq = val; | ||
1144 | fb_timings_hfreq(&timings); | ||
1145 | break; | ||
1146 | case FB_DCLKTIMINGS: /* pixelclock driven */ | ||
1147 | timings.dclk = PICOS2KHZ(val) * 1000; | ||
1148 | fb_timings_dclk(&timings); | ||
1149 | break; | ||
1150 | default: | ||
1151 | return -EINVAL; | ||
1152 | |||
1153 | } | ||
1154 | |||
1155 | if (!(flags & FB_IGNOREMON) && | ||
1156 | (timings.vfreq < vfmin || timings.vfreq > vfmax || | ||
1157 | timings.hfreq < hfmin || timings.hfreq > hfmax || | ||
1158 | timings.dclk < dclkmin || timings.dclk > dclkmax)) | ||
1159 | return -EINVAL; | ||
1160 | |||
1161 | var->pixclock = KHZ2PICOS(timings.dclk/1000); | ||
1162 | var->hsync_len = (timings.htotal * 8)/100; | ||
1163 | var->right_margin = (timings.hblank/2) - var->hsync_len; | ||
1164 | var->left_margin = timings.hblank - var->right_margin - var->hsync_len; | ||
1165 | |||
1166 | var->vsync_len = (3 * interlace)/dscan; | ||
1167 | var->lower_margin = (1 * interlace)/dscan; | ||
1168 | var->upper_margin = (timings.vblank * interlace)/dscan - | ||
1169 | (var->vsync_len + var->lower_margin); | ||
1170 | |||
1171 | return 0; | ||
1172 | } | ||
1173 | #else | ||
1174 | int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) | ||
1175 | { | ||
1176 | return 1; | ||
1177 | } | ||
1178 | void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) | ||
1179 | { | ||
1180 | specs = NULL; | ||
1181 | } | ||
1182 | void fb_destroy_modedb(struct fb_videomode *modedb) | ||
1183 | { | ||
1184 | } | ||
1185 | int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, | ||
1186 | struct fb_info *info) | ||
1187 | { | ||
1188 | return -EINVAL; | ||
1189 | } | ||
1190 | #endif /* CONFIG_FB_MODE_HELPERS */ | ||
1191 | |||
1192 | /* | ||
1193 | * fb_validate_mode - validates var against monitor capabilities | ||
1194 | * @var: pointer to fb_var_screeninfo | ||
1195 | * @info: pointer to fb_info | ||
1196 | * | ||
1197 | * DESCRIPTION: | ||
1198 | * Validates video mode against monitor capabilities specified in | ||
1199 | * info->monspecs. | ||
1200 | * | ||
1201 | * REQUIRES: | ||
1202 | * A valid info->monspecs. | ||
1203 | */ | ||
1204 | int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info) | ||
1205 | { | ||
1206 | u32 hfreq, vfreq, htotal, vtotal, pixclock; | ||
1207 | u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax; | ||
1208 | |||
1209 | /* | ||
1210 | * If monspecs are invalid, use values that are enough | ||
1211 | * for 640x480@60 | ||
1212 | */ | ||
1213 | if (!info->monspecs.hfmax || !info->monspecs.vfmax || | ||
1214 | !info->monspecs.dclkmax || | ||
1215 | info->monspecs.hfmax < info->monspecs.hfmin || | ||
1216 | info->monspecs.vfmax < info->monspecs.vfmin || | ||
1217 | info->monspecs.dclkmax < info->monspecs.dclkmin) { | ||
1218 | hfmin = 29000; hfmax = 30000; | ||
1219 | vfmin = 60; vfmax = 60; | ||
1220 | dclkmin = 0; dclkmax = 25000000; | ||
1221 | } else { | ||
1222 | hfmin = info->monspecs.hfmin; | ||
1223 | hfmax = info->monspecs.hfmax; | ||
1224 | vfmin = info->monspecs.vfmin; | ||
1225 | vfmax = info->monspecs.vfmax; | ||
1226 | dclkmin = info->monspecs.dclkmin; | ||
1227 | dclkmax = info->monspecs.dclkmax; | ||
1228 | } | ||
1229 | |||
1230 | if (!var->pixclock) | ||
1231 | return -EINVAL; | ||
1232 | pixclock = PICOS2KHZ(var->pixclock) * 1000; | ||
1233 | |||
1234 | htotal = var->xres + var->right_margin + var->hsync_len + | ||
1235 | var->left_margin; | ||
1236 | vtotal = var->yres + var->lower_margin + var->vsync_len + | ||
1237 | var->upper_margin; | ||
1238 | |||
1239 | if (var->vmode & FB_VMODE_INTERLACED) | ||
1240 | vtotal /= 2; | ||
1241 | if (var->vmode & FB_VMODE_DOUBLE) | ||
1242 | vtotal *= 2; | ||
1243 | |||
1244 | hfreq = pixclock/htotal; | ||
1245 | vfreq = hfreq/vtotal; | ||
1246 | |||
1247 | return (vfreq < vfmin || vfreq > vfmax || | ||
1248 | hfreq < hfmin || hfreq > hfmax || | ||
1249 | pixclock < dclkmin || pixclock > dclkmax) ? | ||
1250 | -EINVAL : 0; | ||
1251 | } | ||
1252 | |||
1253 | EXPORT_SYMBOL(fb_parse_edid); | ||
1254 | EXPORT_SYMBOL(fb_edid_to_monspecs); | ||
1255 | |||
1256 | EXPORT_SYMBOL(fb_get_mode); | ||
1257 | EXPORT_SYMBOL(fb_validate_mode); | ||
1258 | EXPORT_SYMBOL(fb_destroy_modedb); | ||