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