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