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...) no_printk(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
55struct broken_edid {
56 u8 manufacturer[4];
57 u32 model;
58 u32 fix;
59};
60
61static 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
82static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
83 0xff, 0xff, 0xff, 0x00
84};
85
86static 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
96static 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
106static 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
116static 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
126static 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
136static 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
145static 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
207static 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
256static 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
277static 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
292static 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
310static 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
326static 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
381static 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
409static 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
494static 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
553static 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
564static 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
616static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize,
617 const struct fb_monspecs *specs)
618{
619 struct fb_videomode *mode, *m;
620 unsigned char *block;
621 int num = 0, i, first = 1;
622 int ver, rev;
623
624 mode = kcalloc(50, sizeof(struct fb_videomode), GFP_KERNEL);
625 if (mode == NULL)
626 return NULL;
627
628 if (edid == NULL || !edid_checksum(edid) ||
629 !edid_check_header(edid)) {
630 kfree(mode);
631 return NULL;
632 }
633
634 ver = edid[EDID_STRUCT_VERSION];
635 rev = edid[EDID_STRUCT_REVISION];
636
637 *dbsize = 0;
638
639 DPRINTK(" Detailed Timings\n");
640 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
641 for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
642 if (!(block[0] == 0x00 && block[1] == 0x00)) {
643 get_detailed_timing(block, &mode[num]);
644 if (first) {
645 mode[num].flag |= FB_MODE_IS_FIRST;
646 first = 0;
647 }
648 num++;
649 }
650 }
651
652 DPRINTK(" Supported VESA Modes\n");
653 block = edid + ESTABLISHED_TIMING_1;
654 num += get_est_timing(block, &mode[num]);
655
656 DPRINTK(" Standard Timings\n");
657 block = edid + STD_TIMING_DESCRIPTIONS_START;
658 for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
659 num += get_std_timing(block, &mode[num], ver, rev, specs);
660
661 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
662 for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
663 if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa)
664 num += get_dst_timing(block + 5, &mode[num],
665 ver, rev, specs);
666 }
667
668
669 if (!num) {
670 kfree(mode);
671 return NULL;
672 }
673
674 *dbsize = num;
675 m = kmalloc_array(num, sizeof(struct fb_videomode), GFP_KERNEL);
676 if (!m)
677 return mode;
678 memmove(m, mode, num * sizeof(struct fb_videomode));
679 kfree(mode);
680 return m;
681}
682
683
684
685
686
687
688
689
690void fb_destroy_modedb(struct fb_videomode *modedb)
691{
692 kfree(modedb);
693}
694
695static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
696{
697 int i, retval = 1;
698 unsigned char *block;
699
700 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
701
702 DPRINTK(" Monitor Operating Limits: ");
703
704 for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
705 if (edid_is_limits_block(block)) {
706 specs->hfmin = H_MIN_RATE * 1000;
707 specs->hfmax = H_MAX_RATE * 1000;
708 specs->vfmin = V_MIN_RATE;
709 specs->vfmax = V_MAX_RATE;
710 specs->dclkmax = MAX_PIXEL_CLOCK * 1000000;
711 specs->gtf = (GTF_SUPPORT) ? 1 : 0;
712 retval = 0;
713 DPRINTK("From EDID\n");
714 break;
715 }
716 }
717
718
719 if (retval) {
720 struct fb_videomode *modes, *mode;
721 int num_modes, hz, hscan, pixclock;
722 int vtotal, htotal;
723
724 modes = fb_create_modedb(edid, &num_modes, specs);
725 if (!modes) {
726 DPRINTK("None Available\n");
727 return 1;
728 }
729
730 retval = 0;
731 for (i = 0; i < num_modes; i++) {
732 mode = &modes[i];
733 pixclock = PICOS2KHZ(modes[i].pixclock) * 1000;
734 htotal = mode->xres + mode->right_margin + mode->hsync_len
735 + mode->left_margin;
736 vtotal = mode->yres + mode->lower_margin + mode->vsync_len
737 + mode->upper_margin;
738
739 if (mode->vmode & FB_VMODE_INTERLACED)
740 vtotal /= 2;
741
742 if (mode->vmode & FB_VMODE_DOUBLE)
743 vtotal *= 2;
744
745 hscan = (pixclock + htotal / 2) / htotal;
746 hscan = (hscan + 500) / 1000 * 1000;
747 hz = (hscan + vtotal / 2) / vtotal;
748
749 if (specs->dclkmax == 0 || specs->dclkmax < pixclock)
750 specs->dclkmax = pixclock;
751
752 if (specs->dclkmin == 0 || specs->dclkmin > pixclock)
753 specs->dclkmin = pixclock;
754
755 if (specs->hfmax == 0 || specs->hfmax < hscan)
756 specs->hfmax = hscan;
757
758 if (specs->hfmin == 0 || specs->hfmin > hscan)
759 specs->hfmin = hscan;
760
761 if (specs->vfmax == 0 || specs->vfmax < hz)
762 specs->vfmax = hz;
763
764 if (specs->vfmin == 0 || specs->vfmin > hz)
765 specs->vfmin = hz;
766 }
767 DPRINTK("Extrapolated\n");
768 fb_destroy_modedb(modes);
769 }
770 DPRINTK(" H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n",
771 specs->hfmin/1000, specs->hfmax/1000, specs->vfmin,
772 specs->vfmax, specs->dclkmax/1000000);
773 return retval;
774}
775
776static void get_monspecs(unsigned char *edid, struct fb_monspecs *specs)
777{
778 unsigned char c, *block;
779
780 block = edid + EDID_STRUCT_DISPLAY;
781
782 fb_get_monitor_limits(edid, specs);
783
784 c = block[0] & 0x80;
785 specs->input = 0;
786 if (c) {
787 specs->input |= FB_DISP_DDI;
788 DPRINTK(" Digital Display Input");
789 } else {
790 DPRINTK(" Analog Display Input: Input Voltage - ");
791 switch ((block[0] & 0x60) >> 5) {
792 case 0:
793 DPRINTK("0.700V/0.300V");
794 specs->input |= FB_DISP_ANA_700_300;
795 break;
796 case 1:
797 DPRINTK("0.714V/0.286V");
798 specs->input |= FB_DISP_ANA_714_286;
799 break;
800 case 2:
801 DPRINTK("1.000V/0.400V");
802 specs->input |= FB_DISP_ANA_1000_400;
803 break;
804 case 3:
805 DPRINTK("0.700V/0.000V");
806 specs->input |= FB_DISP_ANA_700_000;
807 break;
808 }
809 }
810 DPRINTK("\n Sync: ");
811 c = block[0] & 0x10;
812 if (c)
813 DPRINTK(" Configurable signal level\n");
814 c = block[0] & 0x0f;
815 specs->signal = 0;
816 if (c & 0x10) {
817 DPRINTK("Blank to Blank ");
818 specs->signal |= FB_SIGNAL_BLANK_BLANK;
819 }
820 if (c & 0x08) {
821 DPRINTK("Separate ");
822 specs->signal |= FB_SIGNAL_SEPARATE;
823 }
824 if (c & 0x04) {
825 DPRINTK("Composite ");
826 specs->signal |= FB_SIGNAL_COMPOSITE;
827 }
828 if (c & 0x02) {
829 DPRINTK("Sync on Green ");
830 specs->signal |= FB_SIGNAL_SYNC_ON_GREEN;
831 }
832 if (c & 0x01) {
833 DPRINTK("Serration on ");
834 specs->signal |= FB_SIGNAL_SERRATION_ON;
835 }
836 DPRINTK("\n");
837 specs->max_x = block[1];
838 specs->max_y = block[2];
839 DPRINTK(" Max H-size in cm: ");
840 if (specs->max_x)
841 DPRINTK("%d\n", specs->max_x);
842 else
843 DPRINTK("variable\n");
844 DPRINTK(" Max V-size in cm: ");
845 if (specs->max_y)
846 DPRINTK("%d\n", specs->max_y);
847 else
848 DPRINTK("variable\n");
849
850 c = block[3];
851 specs->gamma = c+100;
852 DPRINTK(" Gamma: ");
853 DPRINTK("%d.%d\n", specs->gamma/100, specs->gamma % 100);
854
855 get_dpms_capabilities(block[4], specs);
856
857 switch ((block[4] & 0x18) >> 3) {
858 case 0:
859 DPRINTK(" Monochrome/Grayscale\n");
860 specs->input |= FB_DISP_MONO;
861 break;
862 case 1:
863 DPRINTK(" RGB Color Display\n");
864 specs->input |= FB_DISP_RGB;
865 break;
866 case 2:
867 DPRINTK(" Non-RGB Multicolor Display\n");
868 specs->input |= FB_DISP_MULTI;
869 break;
870 default:
871 DPRINTK(" Unknown\n");
872 specs->input |= FB_DISP_UNKNOWN;
873 break;
874 }
875
876 get_chroma(block, specs);
877
878 specs->misc = 0;
879 c = block[4] & 0x7;
880 if (c & 0x04) {
881 DPRINTK(" Default color format is primary\n");
882 specs->misc |= FB_MISC_PRIM_COLOR;
883 }
884 if (c & 0x02) {
885 DPRINTK(" First DETAILED Timing is preferred\n");
886 specs->misc |= FB_MISC_1ST_DETAIL;
887 }
888 if (c & 0x01) {
889 printk(" Display is GTF capable\n");
890 specs->gtf = 1;
891 }
892}
893
894int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
895{
896 int i;
897 unsigned char *block;
898
899 if (edid == NULL || var == NULL)
900 return 1;
901
902 if (!(edid_checksum(edid)))
903 return 1;
904
905 if (!(edid_check_header(edid)))
906 return 1;
907
908 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
909
910 for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
911 if (edid_is_timing_block(block)) {
912 var->xres = var->xres_virtual = H_ACTIVE;
913 var->yres = var->yres_virtual = V_ACTIVE;
914 var->height = var->width = 0;
915 var->right_margin = H_SYNC_OFFSET;
916 var->left_margin = (H_ACTIVE + H_BLANKING) -
917 (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
918 var->upper_margin = V_BLANKING - V_SYNC_OFFSET -
919 V_SYNC_WIDTH;
920 var->lower_margin = V_SYNC_OFFSET;
921 var->hsync_len = H_SYNC_WIDTH;
922 var->vsync_len = V_SYNC_WIDTH;
923 var->pixclock = PIXEL_CLOCK;
924 var->pixclock /= 1000;
925 var->pixclock = KHZ2PICOS(var->pixclock);
926
927 if (HSYNC_POSITIVE)
928 var->sync |= FB_SYNC_HOR_HIGH_ACT;
929 if (VSYNC_POSITIVE)
930 var->sync |= FB_SYNC_VERT_HIGH_ACT;
931 return 0;
932 }
933 }
934 return 1;
935}
936
937void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
938{
939 unsigned char *block;
940 int i, found = 0;
941
942 if (edid == NULL)
943 return;
944
945 if (!(edid_checksum(edid)))
946 return;
947
948 if (!(edid_check_header(edid)))
949 return;
950
951 memset(specs, 0, sizeof(struct fb_monspecs));
952
953 specs->version = edid[EDID_STRUCT_VERSION];
954 specs->revision = edid[EDID_STRUCT_REVISION];
955
956 DPRINTK("========================================\n");
957 DPRINTK("Display Information (EDID)\n");
958 DPRINTK("========================================\n");
959 DPRINTK(" EDID Version %d.%d\n", (int) specs->version,
960 (int) specs->revision);
961
962 parse_vendor_block(edid + ID_MANUFACTURER_NAME, specs);
963
964 block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
965 for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
966 if (edid_is_serial_block(block)) {
967 copy_string(block, specs->serial_no);
968 DPRINTK(" Serial Number: %s\n", specs->serial_no);
969 } else if (edid_is_ascii_block(block)) {
970 copy_string(block, specs->ascii);
971 DPRINTK(" ASCII Block: %s\n", specs->ascii);
972 } else if (edid_is_monitor_block(block)) {
973 copy_string(block, specs->monitor);
974 DPRINTK(" Monitor Name: %s\n", specs->monitor);
975 }
976 }
977
978 DPRINTK(" Display Characteristics:\n");
979 get_monspecs(edid, specs);
980
981 specs->modedb = fb_create_modedb(edid, &specs->modedb_len, specs);
982 if (!specs->modedb)
983 return;
984
985
986
987
988
989
990 for (i = 0; i < specs->modedb_len; i++) {
991 if (specs->modedb[i].flag & FB_MODE_IS_DETAILED) {
992 found = 1;
993 break;
994 }
995 }
996
997 if (!found)
998 specs->misc &= ~FB_MISC_1ST_DETAIL;
999
1000 DPRINTK("========================================\n");
1001}
1002
1003
1004
1005
1006
1007#define FLYBACK 550
1008#define V_FRONTPORCH 1
1009#define H_OFFSET 40
1010#define H_SCALEFACTOR 20
1011#define H_BLANKSCALE 128
1012#define H_GRADIENT 600
1013#define C_VAL 30
1014#define M_VAL 300
1015
1016struct __fb_timings {
1017 u32 dclk;
1018 u32 hfreq;
1019 u32 vfreq;
1020 u32 hactive;
1021 u32 vactive;
1022 u32 hblank;
1023 u32 vblank;
1024 u32 htotal;
1025 u32 vtotal;
1026};
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043static u32 fb_get_vblank(u32 hfreq)
1044{
1045 u32 vblank;
1046
1047 vblank = (hfreq * FLYBACK)/1000;
1048 vblank = (vblank + 500)/1000;
1049 return (vblank + V_FRONTPORCH);
1050}
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres)
1073{
1074 u32 c_val, m_val, duty_cycle, hblank;
1075
1076 c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 +
1077 H_SCALEFACTOR) * 1000;
1078 m_val = (H_BLANKSCALE * H_GRADIENT)/256;
1079 m_val = (m_val * 1000000)/hfreq;
1080 duty_cycle = c_val - m_val;
1081 hblank = (xres * duty_cycle)/(100000 - duty_cycle);
1082 return (hblank);
1083}
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105static 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
1139static 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
1148static 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
1159static 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
1170static 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
1215int 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
1311int 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}
1357EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
1358
1359#ifdef CONFIG_OF
1360static 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
1379int 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}
1399EXPORT_SYMBOL_GPL(of_get_fb_videomode);
1400#endif
1401#endif
1402
1403#else
1404int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
1405{
1406 return 1;
1407}
1408void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
1409{
1410}
1411void fb_destroy_modedb(struct fb_videomode *modedb)
1412{
1413}
1414int 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
1433int 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
1491const 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
1509const unsigned char *fb_firmware_edid(struct device *device)
1510{
1511 return NULL;
1512}
1513#endif
1514EXPORT_SYMBOL(fb_firmware_edid);
1515
1516EXPORT_SYMBOL(fb_parse_edid);
1517EXPORT_SYMBOL(fb_edid_to_monspecs);
1518EXPORT_SYMBOL(fb_get_mode);
1519EXPORT_SYMBOL(fb_validate_mode);
1520EXPORT_SYMBOL(fb_destroy_modedb);
1521