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