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