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