1 | /* |
2 | * linux/drivers/video/fbmon.c |
3 | * |
4 | * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net> |
5 | * |
6 | * Credits: |
7 | * |
8 | * The EDID Parser is a conglomeration from the following sources: |
9 | * |
10 | * 1. SciTech SNAP Graphics Architecture |
11 | * Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved. |
12 | * |
13 | * 2. XFree86 4.3.0, interpret_edid.c |
14 | * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE> |
15 | * |
16 | * 3. John Fremlin <vii@users.sourceforge.net> and |
17 | * Ani Joshi <ajoshi@unixbox.com> |
18 | * |
19 | * Generalized Timing Formula is derived from: |
20 | * |
21 | * GTF Spreadsheet by Andy Morrish (1/5/97) |
22 | * available at https://www.vesa.org |
23 | * |
24 | * This file is subject to the terms and conditions of the GNU General Public |
25 | * License. See the file COPYING in the main directory of this archive |
26 | * for more details. |
27 | * |
28 | */ |
29 | #include <linux/fb.h> |
30 | #include <linux/module.h> |
31 | #include <linux/pci.h> |
32 | #include <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 | * EDID parser |
40 | */ |
41 | |
42 | #undef DEBUG /* define this for verbose EDID parsing output */ |
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 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 | /* DEC FR-PCXAV-YZ */ |
63 | { |
64 | .manufacturer = "DEC" , |
65 | .model = 0x073a, |
66 | .fix = FBMON_FIX_HEADER, |
67 | }, |
68 | /* ViewSonic PF775a */ |
69 | { |
70 | .manufacturer = "VSC" , |
71 | .model = 0x5a44, |
72 | .fix = FBMON_FIX_INPUT, |
73 | }, |
74 | /* Sharp UXGA? */ |
75 | { |
76 | .manufacturer = "SHP" , |
77 | .model = 0x138e, |
78 | .fix = FBMON_FIX_TIMINGS, |
79 | }, |
80 | }; |
81 | |
82 | static const unsigned char [] = { 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 | /* Only if display is GTF capable will |
179 | the input type be reset to analog */ |
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(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(block: b) || |
228 | edid_is_ascii_block(block: b) || |
229 | edid_is_monitor_block(block: b) || |
230 | edid_is_timing_block(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; /* vfmin */ |
237 | b[6] = 60; /* vfmax */ |
238 | b[7] = 30; /* hfmin */ |
239 | b[8] = 75; /* hfmax */ |
240 | b[9] = 17; /* pixclock - 170 MHz*/ |
241 | b[10] = 0; /* GTF */ |
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 | /* checksum passed, everything's good */ |
271 | err = 1; |
272 | } |
273 | |
274 | return err; |
275 | } |
276 | |
277 | static int (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 | /* Chromaticity data */ |
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(size: 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 | val: 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(objp: 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(xres: 720, yres: 400, refresh: 70, mode: &mode[num]); |
417 | mode[num++].flag = FB_MODE_IS_CALCULATED; |
418 | DPRINTK(" 720x400@70Hz\n" ); |
419 | } |
420 | if (c&0x40) { |
421 | calc_mode_timings(xres: 720, yres: 400, refresh: 88, mode: &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(xres: 640, yres: 480, refresh: 67, mode: &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(xres: 832, yres: 624, refresh: 75, mode: &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 | /* DMT mode found */ |
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 | /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */ |
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 | /* Check the mode we got is within valid spec of the monitor */ |
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: &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 | * fb_create_modedb - create video mode database |
606 | * @edid: EDID data |
607 | * @dbsize: database size |
608 | * @specs: monitor specifications, may be NULL |
609 | * |
610 | * RETURNS: struct fb_videomode, @dbsize contains length of database |
611 | * |
612 | * DESCRIPTION: |
613 | * This function builds a mode database using the contents of the EDID |
614 | * data |
615 | */ |
616 | static 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(n: 50, size: 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(objp: 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: &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: &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: &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: block + 5, mode: &mode[num], |
665 | ver, rev, specs); |
666 | } |
667 | |
668 | /* Yikes, EDID data is totally useless */ |
669 | if (!num) { |
670 | kfree(objp: mode); |
671 | return NULL; |
672 | } |
673 | |
674 | *dbsize = num; |
675 | m = kmalloc_array(n: num, size: sizeof(struct fb_videomode), GFP_KERNEL); |
676 | if (!m) |
677 | return mode; |
678 | memmove(m, mode, num * sizeof(struct fb_videomode)); |
679 | kfree(objp: mode); |
680 | return m; |
681 | } |
682 | |
683 | /** |
684 | * fb_destroy_modedb - destroys mode database |
685 | * @modedb: mode database to destroy |
686 | * |
687 | * DESCRIPTION: |
688 | * Destroy mode database created by fb_create_modedb |
689 | */ |
690 | void fb_destroy_modedb(struct fb_videomode *modedb) |
691 | { |
692 | kfree(objp: modedb); |
693 | } |
694 | |
695 | static 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 | /* estimate monitor limits based on modes supported */ |
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, dbsize: &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(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 | |
776 | static 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(flags: 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 | |
894 | int 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 | |
937 | void 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(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(c: block, s: specs->serial_no); |
968 | DPRINTK(" Serial Number: %s\n" , specs->serial_no); |
969 | } else if (edid_is_ascii_block(block)) { |
970 | copy_string(c: block, s: specs->ascii); |
971 | DPRINTK(" ASCII Block: %s\n" , specs->ascii); |
972 | } else if (edid_is_monitor_block(block)) { |
973 | copy_string(c: block, s: 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, dbsize: &specs->modedb_len, specs); |
982 | if (!specs->modedb) |
983 | return; |
984 | |
985 | /* |
986 | * Workaround for buggy EDIDs that sets that the first |
987 | * detailed timing is preferred but has not detailed |
988 | * timing specified |
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 | * VESA Generalized Timing Formula (GTF) |
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 | |
1016 | struct __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 | * fb_get_vblank - get vertical blank time |
1030 | * @hfreq: horizontal freq |
1031 | * |
1032 | * DESCRIPTION: |
1033 | * vblank = right_margin + vsync_len + left_margin |
1034 | * |
1035 | * given: right_margin = 1 (V_FRONTPORCH) |
1036 | * vsync_len = 3 |
1037 | * flyback = 550 |
1038 | * |
1039 | * flyback * hfreq |
1040 | * left_margin = --------------- - vsync_len |
1041 | * 1000000 |
1042 | */ |
1043 | static 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 | * fb_get_hblank_by_hfreq - get horizontal blank time given hfreq |
1054 | * @hfreq: horizontal freq |
1055 | * @xres: horizontal resolution in pixels |
1056 | * |
1057 | * DESCRIPTION: |
1058 | * |
1059 | * xres * duty_cycle |
1060 | * hblank = ------------------ |
1061 | * 100 - duty_cycle |
1062 | * |
1063 | * duty cycle = percent of htotal assigned to inactive display |
1064 | * duty cycle = C - (M/Hfreq) |
1065 | * |
1066 | * where: C = ((offset - scale factor) * blank_scale) |
1067 | * -------------------------------------- + scale factor |
1068 | * 256 |
1069 | * M = blank_scale * gradient |
1070 | * |
1071 | */ |
1072 | static 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 | * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock |
1087 | * @dclk: pixelclock in Hz |
1088 | * @xres: horizontal resolution in pixels |
1089 | * |
1090 | * DESCRIPTION: |
1091 | * |
1092 | * xres * duty_cycle |
1093 | * hblank = ------------------ |
1094 | * 100 - duty_cycle |
1095 | * |
1096 | * duty cycle = percent of htotal assigned to inactive display |
1097 | * duty cycle = C - (M * h_period) |
1098 | * |
1099 | * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100 |
1100 | * ----------------------------------------------- |
1101 | * 2 * M |
1102 | * M = 300; |
1103 | * C = 30; |
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 | * fb_get_hfreq - estimate hsync |
1128 | * @vfreq: vertical refresh rate |
1129 | * @yres: vertical resolution |
1130 | * |
1131 | * DESCRIPTION: |
1132 | * |
1133 | * (yres + front_port) * vfreq * 1000000 |
1134 | * hfreq = ------------------------------------- |
1135 | * (1000000 - (vfreq * FLYBACK) |
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(vfreq: timings->vfreq, yres: timings->vactive); |
1151 | timings->vblank = fb_get_vblank(hfreq: timings->hfreq); |
1152 | timings->vtotal = timings->vactive + timings->vblank; |
1153 | timings->hblank = fb_get_hblank_by_hfreq(hfreq: timings->hfreq, |
1154 | xres: 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(hfreq: timings->hfreq); |
1162 | timings->vtotal = timings->vactive + timings->vblank; |
1163 | timings->vfreq = timings->hfreq/timings->vtotal; |
1164 | timings->hblank = fb_get_hblank_by_hfreq(hfreq: timings->hfreq, |
1165 | xres: 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(dclk: timings->dclk, |
1173 | xres: timings->hactive); |
1174 | timings->htotal = timings->hactive + timings->hblank; |
1175 | timings->hfreq = timings->dclk/timings->htotal; |
1176 | timings->vblank = fb_get_vblank(hfreq: timings->hfreq); |
1177 | timings->vtotal = timings->vactive + timings->vblank; |
1178 | timings->vfreq = timings->hfreq/timings->vtotal; |
1179 | } |
1180 | |
1181 | /* |
1182 | * fb_get_mode - calculates video mode using VESA GTF |
1183 | * @flags: if: 0 - maximize vertical refresh rate |
1184 | * 1 - vrefresh-driven calculation; |
1185 | * 2 - hscan-driven calculation; |
1186 | * 3 - pixelclock-driven calculation; |
1187 | * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock |
1188 | * @var: pointer to fb_var_screeninfo |
1189 | * @info: pointer to fb_info |
1190 | * |
1191 | * DESCRIPTION: |
1192 | * Calculates video mode based on monitor specs using VESA GTF. |
1193 | * The GTF is best for VESA GTF compliant monitors but is |
1194 | * specifically formulated to work for older monitors as well. |
1195 | * |
1196 | * If @flag==0, the function will attempt to maximize the |
1197 | * refresh rate. Otherwise, it will calculate timings based on |
1198 | * the flag and accompanying value. |
1199 | * |
1200 | * If FB_IGNOREMON bit is set in @flags, monitor specs will be |
1201 | * ignored and @var will be filled with the calculated timings. |
1202 | * |
1203 | * All calculations are based on the VESA GTF Spreadsheet |
1204 | * available at VESA's public ftp (https://www.vesa.org). |
1205 | * |
1206 | * NOTES: |
1207 | * The timings generated by the GTF will be different from VESA |
1208 | * DMT. It might be a good idea to keep a table of standard |
1209 | * VESA modes as well. The GTF may also not work for some displays, |
1210 | * such as, and especially, analog TV. |
1211 | * |
1212 | * REQUIRES: |
1213 | * A valid info->monspecs, otherwise 'safe numbers' will be used. |
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(size: sizeof(struct __fb_timings), GFP_KERNEL); |
1223 | |
1224 | if (!timings) |
1225 | return -ENOMEM; |
1226 | |
1227 | /* |
1228 | * If monspecs are invalid, use values that are enough |
1229 | * for 640x480@60 |
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: /* maximize refresh rate */ |
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: /* vrefresh driven */ |
1273 | timings->vfreq = val; |
1274 | fb_timings_vfreq(timings); |
1275 | break; |
1276 | case FB_HSYNCTIMINGS: /* hsync driven */ |
1277 | timings->hfreq = val; |
1278 | fb_timings_hfreq(timings); |
1279 | break; |
1280 | case FB_DCLKTIMINGS: /* pixelclock driven */ |
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(objp: 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, total; |
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 | /* prevent division by zero in KHZ2PICOS macro */ |
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 | /* prevent division by zero */ |
1347 | total = htotal * vtotal; |
1348 | if (total) { |
1349 | fbmode->refresh = vm->pixelclock / total; |
1350 | /* a mode must have htotal and vtotal != 0 or it is invalid */ |
1351 | } else { |
1352 | fbmode->refresh = 0; |
1353 | return -EINVAL; |
1354 | } |
1355 | |
1356 | return 0; |
1357 | } |
1358 | EXPORT_SYMBOL_GPL(fb_videomode_from_videomode); |
1359 | |
1360 | #ifdef CONFIG_OF |
1361 | static inline void dump_fb_videomode(const struct fb_videomode *m) |
1362 | { |
1363 | pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n" , |
1364 | m->xres, m->yres, m->refresh, m->pixclock, m->left_margin, |
1365 | m->right_margin, m->upper_margin, m->lower_margin, |
1366 | m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag); |
1367 | } |
1368 | |
1369 | /** |
1370 | * of_get_fb_videomode - get a fb_videomode from devicetree |
1371 | * @np: device_node with the timing specification |
1372 | * @fb: will be set to the return value |
1373 | * @index: index into the list of display timings in devicetree |
1374 | * |
1375 | * DESCRIPTION: |
1376 | * This function is expensive and should only be used, if only one mode is to be |
1377 | * read from DT. To get multiple modes start with of_get_display_timings ond |
1378 | * work with that instead. |
1379 | */ |
1380 | int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb, |
1381 | int index) |
1382 | { |
1383 | struct videomode vm; |
1384 | int ret; |
1385 | |
1386 | ret = of_get_videomode(np, vm: &vm, index); |
1387 | if (ret) |
1388 | return ret; |
1389 | |
1390 | ret = fb_videomode_from_videomode(&vm, fb); |
1391 | if (ret) |
1392 | return ret; |
1393 | |
1394 | pr_debug("%pOF: got %dx%d display mode\n" , |
1395 | np, vm.hactive, vm.vactive); |
1396 | dump_fb_videomode(m: fb); |
1397 | |
1398 | return 0; |
1399 | } |
1400 | EXPORT_SYMBOL_GPL(of_get_fb_videomode); |
1401 | #endif /* CONFIG_OF */ |
1402 | #endif /* CONFIG_VIDEOMODE_HELPERS */ |
1403 | |
1404 | #else |
1405 | int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) |
1406 | { |
1407 | return 1; |
1408 | } |
1409 | void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) |
1410 | { |
1411 | } |
1412 | void fb_destroy_modedb(struct fb_videomode *modedb) |
1413 | { |
1414 | } |
1415 | int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, |
1416 | struct fb_info *info) |
1417 | { |
1418 | return -EINVAL; |
1419 | } |
1420 | #endif /* CONFIG_FB_MODE_HELPERS */ |
1421 | |
1422 | /* |
1423 | * fb_validate_mode - validates var against monitor capabilities |
1424 | * @var: pointer to fb_var_screeninfo |
1425 | * @info: pointer to fb_info |
1426 | * |
1427 | * DESCRIPTION: |
1428 | * Validates video mode against monitor capabilities specified in |
1429 | * info->monspecs. |
1430 | * |
1431 | * REQUIRES: |
1432 | * A valid info->monspecs. |
1433 | */ |
1434 | int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info) |
1435 | { |
1436 | u32 hfreq, vfreq, htotal, vtotal, pixclock; |
1437 | u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax; |
1438 | |
1439 | /* |
1440 | * If monspecs are invalid, use values that are enough |
1441 | * for 640x480@60 |
1442 | */ |
1443 | if (!info->monspecs.hfmax || !info->monspecs.vfmax || |
1444 | !info->monspecs.dclkmax || |
1445 | info->monspecs.hfmax < info->monspecs.hfmin || |
1446 | info->monspecs.vfmax < info->monspecs.vfmin || |
1447 | info->monspecs.dclkmax < info->monspecs.dclkmin) { |
1448 | hfmin = 29000; hfmax = 30000; |
1449 | vfmin = 60; vfmax = 60; |
1450 | dclkmin = 0; dclkmax = 25000000; |
1451 | } else { |
1452 | hfmin = info->monspecs.hfmin; |
1453 | hfmax = info->monspecs.hfmax; |
1454 | vfmin = info->monspecs.vfmin; |
1455 | vfmax = info->monspecs.vfmax; |
1456 | dclkmin = info->monspecs.dclkmin; |
1457 | dclkmax = info->monspecs.dclkmax; |
1458 | } |
1459 | |
1460 | if (!var->pixclock) |
1461 | return -EINVAL; |
1462 | pixclock = PICOS2KHZ(var->pixclock) * 1000; |
1463 | |
1464 | htotal = var->xres + var->right_margin + var->hsync_len + |
1465 | var->left_margin; |
1466 | vtotal = var->yres + var->lower_margin + var->vsync_len + |
1467 | var->upper_margin; |
1468 | |
1469 | if (var->vmode & FB_VMODE_INTERLACED) |
1470 | vtotal /= 2; |
1471 | if (var->vmode & FB_VMODE_DOUBLE) |
1472 | vtotal *= 2; |
1473 | |
1474 | hfreq = pixclock/htotal; |
1475 | hfreq = (hfreq + 500) / 1000 * 1000; |
1476 | |
1477 | vfreq = hfreq/vtotal; |
1478 | |
1479 | return (vfreq < vfmin || vfreq > vfmax || |
1480 | hfreq < hfmin || hfreq > hfmax || |
1481 | pixclock < dclkmin || pixclock > dclkmax) ? |
1482 | -EINVAL : 0; |
1483 | } |
1484 | |
1485 | #if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86) |
1486 | |
1487 | /* |
1488 | * We need to ensure that the EDID block is only returned for |
1489 | * the primary graphics adapter. |
1490 | */ |
1491 | |
1492 | const unsigned char *fb_firmware_edid(struct device *device) |
1493 | { |
1494 | struct pci_dev *dev = NULL; |
1495 | struct resource *res = NULL; |
1496 | unsigned char *edid = NULL; |
1497 | |
1498 | if (device) |
1499 | dev = to_pci_dev(device); |
1500 | |
1501 | if (dev) |
1502 | res = &dev->resource[PCI_ROM_RESOURCE]; |
1503 | |
1504 | if (res && res->flags & IORESOURCE_ROM_SHADOW) |
1505 | edid = edid_info.dummy; |
1506 | |
1507 | return edid; |
1508 | } |
1509 | #else |
1510 | const unsigned char *fb_firmware_edid(struct device *device) |
1511 | { |
1512 | return NULL; |
1513 | } |
1514 | #endif |
1515 | EXPORT_SYMBOL(fb_firmware_edid); |
1516 | |
1517 | EXPORT_SYMBOL(fb_parse_edid); |
1518 | EXPORT_SYMBOL(fb_edid_to_monspecs); |
1519 | EXPORT_SYMBOL(fb_get_mode); |
1520 | EXPORT_SYMBOL(fb_validate_mode); |
1521 | EXPORT_SYMBOL(fb_destroy_modedb); |
1522 | |