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