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