Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/drivers/video/fbmon.c | |
3 | * | |
4 | * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net> | |
5 | * | |
6 | * Credits: | |
8c85fd89 | 7 | * |
1da177e4 LT |
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> | |
8c85fd89 AH |
15 | * |
16 | * 3. John Fremlin <vii@users.sourceforge.net> and | |
1da177e4 | 17 | * Ani Joshi <ajoshi@unixbox.com> |
8c85fd89 | 18 | * |
1da177e4 LT |
19 | * Generalized Timing Formula is derived from: |
20 | * | |
8c85fd89 | 21 | * GTF Spreadsheet by Andy Morrish (1/5/97) |
1da177e4 LT |
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 | */ | |
1da177e4 LT |
29 | #include <linux/fb.h> |
30 | #include <linux/module.h> | |
22f4a00f | 31 | #include <linux/pci.h> |
5a0e3ad6 | 32 | #include <linux/slab.h> |
5e518d76 | 33 | #include <video/edid.h> |
79089044 | 34 | #include <video/of_videomode.h> |
2db54c72 | 35 | #include <video/videomode.h> |
1da177e4 | 36 | #ifdef CONFIG_PPC_OF |
1da177e4 LT |
37 | #include <asm/prom.h> |
38 | #include <asm/pci-bridge.h> | |
39 | #endif | |
1da177e4 LT |
40 | #include "edid.h" |
41 | ||
8c85fd89 | 42 | /* |
1da177e4 LT |
43 | * EDID parser |
44 | */ | |
45 | ||
46 | #undef DEBUG /* define this for verbose EDID parsing output */ | |
47 | ||
48 | #ifdef DEBUG | |
49 | #define DPRINTK(fmt, args...) printk(fmt,## args) | |
50 | #else | |
51 | #define DPRINTK(fmt, args...) | |
52 | #endif | |
53 | ||
33a9f640 AD |
54 | #define FBMON_FIX_HEADER 1 |
55 | #define FBMON_FIX_INPUT 2 | |
56 | #define FBMON_FIX_TIMINGS 3 | |
1da177e4 LT |
57 | |
58 | #ifdef CONFIG_FB_MODE_HELPERS | |
59 | struct broken_edid { | |
60 | u8 manufacturer[4]; | |
61 | u32 model; | |
62 | u32 fix; | |
63 | }; | |
64 | ||
d95159cf | 65 | static const struct broken_edid brokendb[] = { |
1da177e4 LT |
66 | /* DEC FR-PCXAV-YZ */ |
67 | { | |
68 | .manufacturer = "DEC", | |
69 | .model = 0x073a, | |
70 | .fix = FBMON_FIX_HEADER, | |
71 | }, | |
72 | /* ViewSonic PF775a */ | |
73 | { | |
74 | .manufacturer = "VSC", | |
75 | .model = 0x5a44, | |
76 | .fix = FBMON_FIX_INPUT, | |
77 | }, | |
33a9f640 AD |
78 | /* Sharp UXGA? */ |
79 | { | |
80 | .manufacturer = "SHP", | |
81 | .model = 0x138e, | |
82 | .fix = FBMON_FIX_TIMINGS, | |
83 | }, | |
1da177e4 LT |
84 | }; |
85 | ||
86 | static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff, | |
87 | 0xff, 0xff, 0xff, 0x00 | |
88 | }; | |
89 | ||
90 | static void copy_string(unsigned char *c, unsigned char *s) | |
91 | { | |
92 | int i; | |
93 | c = c + 5; | |
94 | for (i = 0; (i < 13 && *c != 0x0A); i++) | |
95 | *(s++) = *(c++); | |
96 | *s = 0; | |
97 | while (i-- && (*--s == 0x20)) *s = 0; | |
98 | } | |
99 | ||
33a9f640 AD |
100 | static int edid_is_serial_block(unsigned char *block) |
101 | { | |
102 | if ((block[0] == 0x00) && (block[1] == 0x00) && | |
103 | (block[2] == 0x00) && (block[3] == 0xff) && | |
104 | (block[4] == 0x00)) | |
105 | return 1; | |
106 | else | |
107 | return 0; | |
108 | } | |
109 | ||
110 | static int edid_is_ascii_block(unsigned char *block) | |
111 | { | |
112 | if ((block[0] == 0x00) && (block[1] == 0x00) && | |
113 | (block[2] == 0x00) && (block[3] == 0xfe) && | |
114 | (block[4] == 0x00)) | |
115 | return 1; | |
116 | else | |
117 | return 0; | |
118 | } | |
119 | ||
120 | static int edid_is_limits_block(unsigned char *block) | |
121 | { | |
122 | if ((block[0] == 0x00) && (block[1] == 0x00) && | |
123 | (block[2] == 0x00) && (block[3] == 0xfd) && | |
124 | (block[4] == 0x00)) | |
125 | return 1; | |
126 | else | |
127 | return 0; | |
128 | } | |
129 | ||
130 | static int edid_is_monitor_block(unsigned char *block) | |
131 | { | |
132 | if ((block[0] == 0x00) && (block[1] == 0x00) && | |
133 | (block[2] == 0x00) && (block[3] == 0xfc) && | |
134 | (block[4] == 0x00)) | |
135 | return 1; | |
136 | else | |
137 | return 0; | |
138 | } | |
139 | ||
140 | static int edid_is_timing_block(unsigned char *block) | |
141 | { | |
142 | if ((block[0] != 0x00) || (block[1] != 0x00) || | |
143 | (block[2] != 0x00) || (block[4] != 0x00)) | |
144 | return 1; | |
145 | else | |
146 | return 0; | |
147 | } | |
148 | ||
1da177e4 LT |
149 | static int check_edid(unsigned char *edid) |
150 | { | |
151 | unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4]; | |
152 | unsigned char *b; | |
153 | u32 model; | |
154 | int i, fix = 0, ret = 0; | |
155 | ||
156 | manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@'; | |
157 | manufacturer[1] = ((block[0] & 0x03) << 3) + | |
158 | ((block[1] & 0xe0) >> 5) + '@'; | |
159 | manufacturer[2] = (block[1] & 0x1f) + '@'; | |
160 | manufacturer[3] = 0; | |
161 | model = block[2] + (block[3] << 8); | |
162 | ||
163 | for (i = 0; i < ARRAY_SIZE(brokendb); i++) { | |
164 | if (!strncmp(manufacturer, brokendb[i].manufacturer, 4) && | |
165 | brokendb[i].model == model) { | |
8c85fd89 AH |
166 | fix = brokendb[i].fix; |
167 | break; | |
1da177e4 LT |
168 | } |
169 | } | |
170 | ||
171 | switch (fix) { | |
172 | case FBMON_FIX_HEADER: | |
173 | for (i = 0; i < 8; i++) { | |
33a9f640 | 174 | if (edid[i] != edid_v1_header[i]) { |
1da177e4 | 175 | ret = fix; |
33a9f640 AD |
176 | break; |
177 | } | |
1da177e4 LT |
178 | } |
179 | break; | |
180 | case FBMON_FIX_INPUT: | |
181 | b = edid + EDID_STRUCT_DISPLAY; | |
182 | /* Only if display is GTF capable will | |
183 | the input type be reset to analog */ | |
184 | if (b[4] & 0x01 && b[0] & 0x80) | |
185 | ret = fix; | |
186 | break; | |
33a9f640 AD |
187 | case FBMON_FIX_TIMINGS: |
188 | b = edid + DETAILED_TIMING_DESCRIPTIONS_START; | |
189 | ret = fix; | |
190 | ||
191 | for (i = 0; i < 4; i++) { | |
192 | if (edid_is_limits_block(b)) { | |
193 | ret = 0; | |
194 | break; | |
195 | } | |
196 | ||
197 | b += DETAILED_TIMING_DESCRIPTION_SIZE; | |
198 | } | |
199 | ||
200 | break; | |
1da177e4 LT |
201 | } |
202 | ||
33a9f640 AD |
203 | if (ret) |
204 | printk("fbmon: The EDID Block of " | |
205 | "Manufacturer: %s Model: 0x%x is known to " | |
206 | "be broken,\n", manufacturer, model); | |
207 | ||
1da177e4 LT |
208 | return ret; |
209 | } | |
210 | ||
211 | static void fix_edid(unsigned char *edid, int fix) | |
212 | { | |
33a9f640 AD |
213 | int i; |
214 | unsigned char *b, csum = 0; | |
1da177e4 LT |
215 | |
216 | switch (fix) { | |
217 | case FBMON_FIX_HEADER: | |
218 | printk("fbmon: trying a header reconstruct\n"); | |
219 | memcpy(edid, edid_v1_header, 8); | |
220 | break; | |
221 | case FBMON_FIX_INPUT: | |
222 | printk("fbmon: trying to fix input type\n"); | |
223 | b = edid + EDID_STRUCT_DISPLAY; | |
224 | b[0] &= ~0x80; | |
225 | edid[127] += 0x80; | |
33a9f640 AD |
226 | break; |
227 | case FBMON_FIX_TIMINGS: | |
228 | printk("fbmon: trying to fix monitor timings\n"); | |
229 | b = edid + DETAILED_TIMING_DESCRIPTIONS_START; | |
230 | for (i = 0; i < 4; i++) { | |
231 | if (!(edid_is_serial_block(b) || | |
232 | edid_is_ascii_block(b) || | |
233 | edid_is_monitor_block(b) || | |
234 | edid_is_timing_block(b))) { | |
235 | b[0] = 0x00; | |
236 | b[1] = 0x00; | |
237 | b[2] = 0x00; | |
238 | b[3] = 0xfd; | |
239 | b[4] = 0x00; | |
240 | b[5] = 60; /* vfmin */ | |
241 | b[6] = 60; /* vfmax */ | |
242 | b[7] = 30; /* hfmin */ | |
243 | b[8] = 75; /* hfmax */ | |
244 | b[9] = 17; /* pixclock - 170 MHz*/ | |
245 | b[10] = 0; /* GTF */ | |
246 | break; | |
247 | } | |
248 | ||
249 | b += DETAILED_TIMING_DESCRIPTION_SIZE; | |
250 | } | |
251 | ||
252 | for (i = 0; i < EDID_LENGTH - 1; i++) | |
253 | csum += edid[i]; | |
254 | ||
255 | edid[127] = 256 - csum; | |
256 | break; | |
1da177e4 LT |
257 | } |
258 | } | |
259 | ||
260 | static int edid_checksum(unsigned char *edid) | |
261 | { | |
3730793d LT |
262 | unsigned char csum = 0, all_null = 0; |
263 | int i, err = 0, fix = check_edid(edid); | |
1da177e4 LT |
264 | |
265 | if (fix) | |
266 | fix_edid(edid, fix); | |
267 | ||
268 | for (i = 0; i < EDID_LENGTH; i++) { | |
269 | csum += edid[i]; | |
270 | all_null |= edid[i]; | |
271 | } | |
272 | ||
273 | if (csum == 0x00 && all_null) { | |
274 | /* checksum passed, everything's good */ | |
275 | err = 1; | |
276 | } | |
277 | ||
278 | return err; | |
279 | } | |
280 | ||
281 | static int edid_check_header(unsigned char *edid) | |
282 | { | |
283 | int i, err = 1, fix = check_edid(edid); | |
284 | ||
285 | if (fix) | |
286 | fix_edid(edid, fix); | |
287 | ||
288 | for (i = 0; i < 8; i++) { | |
289 | if (edid[i] != edid_v1_header[i]) | |
290 | err = 0; | |
291 | } | |
292 | ||
293 | return err; | |
294 | } | |
295 | ||
296 | static void parse_vendor_block(unsigned char *block, struct fb_monspecs *specs) | |
297 | { | |
298 | specs->manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@'; | |
299 | specs->manufacturer[1] = ((block[0] & 0x03) << 3) + | |
300 | ((block[1] & 0xe0) >> 5) + '@'; | |
301 | specs->manufacturer[2] = (block[1] & 0x1f) + '@'; | |
302 | specs->manufacturer[3] = 0; | |
303 | specs->model = block[2] + (block[3] << 8); | |
304 | specs->serial = block[4] + (block[5] << 8) + | |
305 | (block[6] << 16) + (block[7] << 24); | |
306 | specs->year = block[9] + 1990; | |
307 | specs->week = block[8]; | |
308 | DPRINTK(" Manufacturer: %s\n", specs->manufacturer); | |
309 | DPRINTK(" Model: %x\n", specs->model); | |
310 | DPRINTK(" Serial#: %u\n", specs->serial); | |
311 | DPRINTK(" Year: %u Week %u\n", specs->year, specs->week); | |
312 | } | |
313 | ||
3c2a0909 S |
314 | static void parse_vendor_specific_block(struct fb_vendor *vsdb, |
315 | unsigned char *svsdb, u8 length) | |
316 | { | |
317 | unsigned int offset = 0; | |
318 | int i; | |
319 | ||
320 | DPRINTK("Vendor Specific Data Block (VSDB)\n"); | |
321 | vsdb->ieee_reg = svsdb[offset + 1] | |
322 | | (svsdb[offset + 2] << 8) | |
323 | | (svsdb[offset + 3] << 16); | |
324 | vsdb->phy_addr = svsdb[offset + 4] << 8; | |
325 | vsdb->phy_addr |= svsdb[offset + 5]; | |
326 | ||
327 | DPRINTK(" ieee_reg: 0x%06x\n", vsdb->ieee_reg); | |
328 | DPRINTK(" physical address: 0x%08x\n", vsdb->phy_addr); | |
329 | if (length < 8) | |
330 | return; | |
331 | ||
332 | vsdb->video_present = (svsdb[offset + 8] & VSDB_VIDEO_PRESENT_MASK) | |
333 | >> VSDB_VIDEO_PRESENT; | |
334 | vsdb->i_latency_field = (svsdb[offset + 8] & VSDB_I_LATENCY_FIELD_MASK) | |
335 | >> VSDB_I_LATENCY_FIELD; | |
336 | vsdb->latency_field = (svsdb[offset + 8] & VSDB_LATENCY_FIELD_MASK) | |
337 | >> VSDB_LATENCY_FIELD; | |
338 | ||
339 | offset += (vsdb->latency_field * 2 + vsdb->i_latency_field * 2); | |
340 | ||
341 | DPRINTK(" video present: %d\n", vsdb->video_present); | |
342 | if (!vsdb->video_present) | |
343 | return; | |
344 | ||
345 | vsdb->s3d_present = (svsdb[offset + 9] & VSDB_3D_PRESENT_MASK) | |
346 | >> VSDB_3D_PRESENT; | |
347 | vsdb->s3d_multi_present = (svsdb[offset + 9] & VSDB_3D_MT_PRESENT_MASK) | |
348 | >> VSDB_3D_MT_PRESENT; | |
349 | vsdb->vic_len = (svsdb[offset + 10] & VSDB_VIC_LEN_MASK) | |
350 | >> VSDB_VIC_LEN; | |
351 | vsdb->s3d_len = svsdb[offset + 10] & VSDB_3D_LEN_MASK; | |
352 | ||
353 | DPRINTK(" HDMI VIC LEN: %d\n", vsdb->vic_len); | |
354 | DPRINTK(" HDMI 3D LEN: %d\n", vsdb->s3d_len); | |
355 | DPRINTK(" 3D present: %d\n", vsdb->s3d_present); | |
356 | DPRINTK(" 3D multi present: %d\n", vsdb->s3d_multi_present); | |
357 | ||
358 | for (i = 0; i < vsdb->vic_len; i++) { | |
359 | vsdb->vic_data[i] = svsdb[offset + 11 + i]; | |
360 | DPRINTK(" HDMI VIC Data[%d]:0x%x\n", i, vsdb->vic_data[i]); | |
361 | } | |
362 | ||
363 | offset += vsdb->vic_len; | |
364 | if (vsdb->s3d_multi_present) { | |
365 | vsdb->s3d_structure_all = svsdb[offset + 11] << 8; | |
366 | vsdb->s3d_structure_all |= svsdb[offset + 12]; | |
367 | DPRINTK(" 3D structure all: 0x%08x\n", | |
368 | vsdb->s3d_structure_all); | |
369 | offset += 2; | |
370 | vsdb->s3d_field = vsdb->s3d_len - 2; | |
371 | if (vsdb->s3d_multi_present == VSDB_3D_MASK) { | |
372 | vsdb->s3d_structure_mask = svsdb[offset + 11] << 8; | |
373 | vsdb->s3d_structure_mask |= svsdb[offset + 12]; | |
374 | DPRINTK(" 3D structure mask: 0x%08x\n", | |
375 | vsdb->s3d_structure_mask); | |
376 | offset += 2; | |
377 | vsdb->s3d_field = vsdb->s3d_field - 2; | |
378 | } | |
379 | } | |
380 | ||
381 | /* | |
382 | * s3d_field is used to indicate that | |
383 | * additional blocks are present in the VSDB. | |
384 | */ | |
385 | if (vsdb->s3d_field) { | |
386 | for (i = 0; i < vsdb->s3d_field; i++, offset++) { | |
387 | vsdb->vic_order[i] = | |
388 | (svsdb[offset + 11] & VSDB_VIC_ORDER_MASK) | |
389 | >> VSDB_VIC_ORDER; | |
390 | vsdb->s3d_structure[i] = | |
391 | svsdb[offset + 11] & VSDB_3D_STRUCTURE_MASK; | |
392 | DPRINTK(" 2D vic order[%d]: %d\n", | |
393 | i, vsdb->vic_order[i]); | |
394 | DPRINTK(" 3D structure[%d]: %d\n", | |
395 | i, vsdb->s3d_structure[i]); | |
396 | if (vsdb->s3d_structure[i] > 7) | |
397 | offset++; | |
398 | } | |
399 | } | |
400 | } | |
401 | ||
1da177e4 LT |
402 | static void get_dpms_capabilities(unsigned char flags, |
403 | struct fb_monspecs *specs) | |
404 | { | |
405 | specs->dpms = 0; | |
406 | if (flags & DPMS_ACTIVE_OFF) | |
407 | specs->dpms |= FB_DPMS_ACTIVE_OFF; | |
408 | if (flags & DPMS_SUSPEND) | |
409 | specs->dpms |= FB_DPMS_SUSPEND; | |
410 | if (flags & DPMS_STANDBY) | |
411 | specs->dpms |= FB_DPMS_STANDBY; | |
412 | DPRINTK(" DPMS: Active %s, Suspend %s, Standby %s\n", | |
413 | (flags & DPMS_ACTIVE_OFF) ? "yes" : "no", | |
414 | (flags & DPMS_SUSPEND) ? "yes" : "no", | |
415 | (flags & DPMS_STANDBY) ? "yes" : "no"); | |
416 | } | |
8c85fd89 | 417 | |
1da177e4 LT |
418 | static void get_chroma(unsigned char *block, struct fb_monspecs *specs) |
419 | { | |
420 | int tmp; | |
421 | ||
422 | DPRINTK(" Chroma\n"); | |
423 | /* Chromaticity data */ | |
424 | tmp = ((block[5] & (3 << 6)) >> 6) | (block[0x7] << 2); | |
425 | tmp *= 1000; | |
426 | tmp += 512; | |
427 | specs->chroma.redx = tmp/1024; | |
428 | DPRINTK(" RedX: 0.%03d ", specs->chroma.redx); | |
429 | ||
430 | tmp = ((block[5] & (3 << 4)) >> 4) | (block[0x8] << 2); | |
431 | tmp *= 1000; | |
432 | tmp += 512; | |
433 | specs->chroma.redy = tmp/1024; | |
434 | DPRINTK("RedY: 0.%03d\n", specs->chroma.redy); | |
435 | ||
436 | tmp = ((block[5] & (3 << 2)) >> 2) | (block[0x9] << 2); | |
437 | tmp *= 1000; | |
438 | tmp += 512; | |
439 | specs->chroma.greenx = tmp/1024; | |
440 | DPRINTK(" GreenX: 0.%03d ", specs->chroma.greenx); | |
441 | ||
442 | tmp = (block[5] & 3) | (block[0xa] << 2); | |
443 | tmp *= 1000; | |
444 | tmp += 512; | |
445 | specs->chroma.greeny = tmp/1024; | |
446 | DPRINTK("GreenY: 0.%03d\n", specs->chroma.greeny); | |
447 | ||
448 | tmp = ((block[6] & (3 << 6)) >> 6) | (block[0xb] << 2); | |
449 | tmp *= 1000; | |
450 | tmp += 512; | |
451 | specs->chroma.bluex = tmp/1024; | |
452 | DPRINTK(" BlueX: 0.%03d ", specs->chroma.bluex); | |
453 | ||
454 | tmp = ((block[6] & (3 << 4)) >> 4) | (block[0xc] << 2); | |
455 | tmp *= 1000; | |
456 | tmp += 512; | |
457 | specs->chroma.bluey = tmp/1024; | |
458 | DPRINTK("BlueY: 0.%03d\n", specs->chroma.bluey); | |
8c85fd89 | 459 | |
1da177e4 LT |
460 | tmp = ((block[6] & (3 << 2)) >> 2) | (block[0xd] << 2); |
461 | tmp *= 1000; | |
462 | tmp += 512; | |
463 | specs->chroma.whitex = tmp/1024; | |
464 | DPRINTK(" WhiteX: 0.%03d ", specs->chroma.whitex); | |
465 | ||
466 | tmp = (block[6] & 3) | (block[0xe] << 2); | |
467 | tmp *= 1000; | |
468 | tmp += 512; | |
469 | specs->chroma.whitey = tmp/1024; | |
470 | DPRINTK("WhiteY: 0.%03d\n", specs->chroma.whitey); | |
471 | } | |
472 | ||
61ab7903 AD |
473 | static void calc_mode_timings(int xres, int yres, int refresh, |
474 | struct fb_videomode *mode) | |
1da177e4 | 475 | { |
af5d0f7e | 476 | struct fb_var_screeninfo *var; |
8c85fd89 | 477 | |
af5d0f7e AD |
478 | var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL); |
479 | ||
480 | if (var) { | |
481 | var->xres = xres; | |
482 | var->yres = yres; | |
483 | fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, | |
484 | refresh, var, NULL); | |
485 | mode->xres = xres; | |
486 | mode->yres = yres; | |
487 | mode->pixclock = var->pixclock; | |
488 | mode->refresh = refresh; | |
489 | mode->left_margin = var->left_margin; | |
490 | mode->right_margin = var->right_margin; | |
491 | mode->upper_margin = var->upper_margin; | |
492 | mode->lower_margin = var->lower_margin; | |
493 | mode->hsync_len = var->hsync_len; | |
494 | mode->vsync_len = var->vsync_len; | |
495 | mode->vmode = 0; | |
496 | mode->sync = 0; | |
497 | kfree(var); | |
498 | } | |
1da177e4 LT |
499 | } |
500 | ||
501 | static int get_est_timing(unsigned char *block, struct fb_videomode *mode) | |
502 | { | |
503 | int num = 0; | |
504 | unsigned char c; | |
505 | ||
506 | c = block[0]; | |
507 | if (c&0x80) { | |
508 | calc_mode_timings(720, 400, 70, &mode[num]); | |
509 | mode[num++].flag = FB_MODE_IS_CALCULATED; | |
510 | DPRINTK(" 720x400@70Hz\n"); | |
511 | } | |
512 | if (c&0x40) { | |
513 | calc_mode_timings(720, 400, 88, &mode[num]); | |
514 | mode[num++].flag = FB_MODE_IS_CALCULATED; | |
515 | DPRINTK(" 720x400@88Hz\n"); | |
516 | } | |
517 | if (c&0x20) { | |
518 | mode[num++] = vesa_modes[3]; | |
519 | DPRINTK(" 640x480@60Hz\n"); | |
520 | } | |
521 | if (c&0x10) { | |
522 | calc_mode_timings(640, 480, 67, &mode[num]); | |
523 | mode[num++].flag = FB_MODE_IS_CALCULATED; | |
524 | DPRINTK(" 640x480@67Hz\n"); | |
525 | } | |
526 | if (c&0x08) { | |
527 | mode[num++] = vesa_modes[4]; | |
528 | DPRINTK(" 640x480@72Hz\n"); | |
529 | } | |
530 | if (c&0x04) { | |
531 | mode[num++] = vesa_modes[5]; | |
532 | DPRINTK(" 640x480@75Hz\n"); | |
533 | } | |
534 | if (c&0x02) { | |
535 | mode[num++] = vesa_modes[7]; | |
536 | DPRINTK(" 800x600@56Hz\n"); | |
537 | } | |
538 | if (c&0x01) { | |
539 | mode[num++] = vesa_modes[8]; | |
540 | DPRINTK(" 800x600@60Hz\n"); | |
541 | } | |
542 | ||
543 | c = block[1]; | |
544 | if (c&0x80) { | |
8c85fd89 | 545 | mode[num++] = vesa_modes[9]; |
1da177e4 LT |
546 | DPRINTK(" 800x600@72Hz\n"); |
547 | } | |
548 | if (c&0x40) { | |
8c85fd89 | 549 | mode[num++] = vesa_modes[10]; |
1da177e4 LT |
550 | DPRINTK(" 800x600@75Hz\n"); |
551 | } | |
552 | if (c&0x20) { | |
553 | calc_mode_timings(832, 624, 75, &mode[num]); | |
554 | mode[num++].flag = FB_MODE_IS_CALCULATED; | |
555 | DPRINTK(" 832x624@75Hz\n"); | |
556 | } | |
557 | if (c&0x10) { | |
558 | mode[num++] = vesa_modes[12]; | |
559 | DPRINTK(" 1024x768@87Hz Interlaced\n"); | |
560 | } | |
561 | if (c&0x08) { | |
562 | mode[num++] = vesa_modes[13]; | |
563 | DPRINTK(" 1024x768@60Hz\n"); | |
564 | } | |
565 | if (c&0x04) { | |
566 | mode[num++] = vesa_modes[14]; | |
567 | DPRINTK(" 1024x768@70Hz\n"); | |
568 | } | |
569 | if (c&0x02) { | |
570 | mode[num++] = vesa_modes[15]; | |
571 | DPRINTK(" 1024x768@75Hz\n"); | |
572 | } | |
573 | if (c&0x01) { | |
574 | mode[num++] = vesa_modes[21]; | |
575 | DPRINTK(" 1280x1024@75Hz\n"); | |
576 | } | |
577 | c = block[2]; | |
578 | if (c&0x80) { | |
579 | mode[num++] = vesa_modes[17]; | |
580 | DPRINTK(" 1152x870@75Hz\n"); | |
581 | } | |
582 | DPRINTK(" Manufacturer's mask: %x\n",c&0x7F); | |
583 | return num; | |
584 | } | |
585 | ||
43dcd13b TV |
586 | static int get_std_timing(unsigned char *block, struct fb_videomode *mode, |
587 | int ver, int rev) | |
1da177e4 LT |
588 | { |
589 | int xres, yres = 0, refresh, ratio, i; | |
8c85fd89 | 590 | |
1da177e4 LT |
591 | xres = (block[0] + 31) * 8; |
592 | if (xres <= 256) | |
593 | return 0; | |
594 | ||
595 | ratio = (block[1] & 0xc0) >> 6; | |
596 | switch (ratio) { | |
597 | case 0: | |
43dcd13b TV |
598 | /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */ |
599 | if (ver < 1 || (ver == 1 && rev < 3)) | |
600 | yres = xres; | |
601 | else | |
602 | yres = (xres * 10)/16; | |
1da177e4 LT |
603 | break; |
604 | case 1: | |
605 | yres = (xres * 3)/4; | |
606 | break; | |
607 | case 2: | |
608 | yres = (xres * 4)/5; | |
609 | break; | |
610 | case 3: | |
611 | yres = (xres * 9)/16; | |
612 | break; | |
613 | } | |
614 | refresh = (block[1] & 0x3f) + 60; | |
615 | ||
616 | DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh); | |
617 | for (i = 0; i < VESA_MODEDB_SIZE; i++) { | |
8c85fd89 | 618 | if (vesa_modes[i].xres == xres && |
1da177e4 LT |
619 | vesa_modes[i].yres == yres && |
620 | vesa_modes[i].refresh == refresh) { | |
621 | *mode = vesa_modes[i]; | |
622 | mode->flag |= FB_MODE_IS_STANDARD; | |
623 | return 1; | |
624 | } | |
625 | } | |
626 | calc_mode_timings(xres, yres, refresh, mode); | |
627 | return 1; | |
628 | } | |
629 | ||
630 | static int get_dst_timing(unsigned char *block, | |
43dcd13b | 631 | struct fb_videomode *mode, int ver, int rev) |
1da177e4 LT |
632 | { |
633 | int j, num = 0; | |
634 | ||
8c85fd89 | 635 | for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE) |
43dcd13b | 636 | num += get_std_timing(block, &mode[num], ver, rev); |
1da177e4 LT |
637 | |
638 | return num; | |
639 | } | |
640 | ||
8c85fd89 | 641 | static void get_detailed_timing(unsigned char *block, |
1da177e4 LT |
642 | struct fb_videomode *mode) |
643 | { | |
3c2a0909 S |
644 | int v_size = V_SIZE; |
645 | int h_size = H_SIZE; | |
646 | ||
1da177e4 LT |
647 | mode->xres = H_ACTIVE; |
648 | mode->yres = V_ACTIVE; | |
649 | mode->pixclock = PIXEL_CLOCK; | |
650 | mode->pixclock /= 1000; | |
651 | mode->pixclock = KHZ2PICOS(mode->pixclock); | |
652 | mode->right_margin = H_SYNC_OFFSET; | |
653 | mode->left_margin = (H_ACTIVE + H_BLANKING) - | |
654 | (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); | |
8c85fd89 | 655 | mode->upper_margin = V_BLANKING - V_SYNC_OFFSET - |
1da177e4 LT |
656 | V_SYNC_WIDTH; |
657 | mode->lower_margin = V_SYNC_OFFSET; | |
658 | mode->hsync_len = H_SYNC_WIDTH; | |
659 | mode->vsync_len = V_SYNC_WIDTH; | |
660 | if (HSYNC_POSITIVE) | |
661 | mode->sync |= FB_SYNC_HOR_HIGH_ACT; | |
662 | if (VSYNC_POSITIVE) | |
663 | mode->sync |= FB_SYNC_VERT_HIGH_ACT; | |
664 | mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) * | |
665 | (V_ACTIVE + V_BLANKING)); | |
1a3b09dc JD |
666 | if (INTERLACED) { |
667 | mode->yres *= 2; | |
668 | mode->upper_margin *= 2; | |
669 | mode->lower_margin *= 2; | |
670 | mode->vsync_len *= 2; | |
671 | mode->vmode |= FB_VMODE_INTERLACED; | |
672 | } | |
1da177e4 LT |
673 | mode->flag = FB_MODE_IS_DETAILED; |
674 | ||
3c2a0909 S |
675 | /* get aspect ratio */ |
676 | if (h_size * 18 > v_size * 31 && h_size * 18 < v_size * 33) | |
677 | mode->flag |= FB_FLAG_RATIO_16_9; | |
678 | if (h_size * 18 > v_size * 23 && h_size * 18 < v_size * 25) | |
679 | mode->flag |= FB_FLAG_RATIO_4_3; | |
680 | ||
1da177e4 LT |
681 | DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000); |
682 | DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET, | |
683 | H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING); | |
684 | DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET, | |
685 | V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING); | |
3c2a0909 | 686 | DPRINTK("%dmm %dmm ", H_SIZE, V_SIZE); |
1da177e4 LT |
687 | DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-", |
688 | (VSYNC_POSITIVE) ? "+" : "-"); | |
689 | } | |
690 | ||
691 | /** | |
692 | * fb_create_modedb - create video mode database | |
693 | * @edid: EDID data | |
694 | * @dbsize: database size | |
695 | * | |
696 | * RETURNS: struct fb_videomode, @dbsize contains length of database | |
697 | * | |
698 | * DESCRIPTION: | |
699 | * This function builds a mode database using the contents of the EDID | |
700 | * data | |
701 | */ | |
702 | static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) | |
703 | { | |
704 | struct fb_videomode *mode, *m; | |
705 | unsigned char *block; | |
5a258d03 | 706 | int num = 0, i, first = 1; |
43dcd13b TV |
707 | int ver, rev; |
708 | ||
709 | ver = edid[EDID_STRUCT_VERSION]; | |
710 | rev = edid[EDID_STRUCT_REVISION]; | |
1da177e4 | 711 | |
def1eded | 712 | mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL); |
1da177e4 LT |
713 | if (mode == NULL) |
714 | return NULL; | |
1da177e4 | 715 | |
8c85fd89 | 716 | if (edid == NULL || !edid_checksum(edid) || |
1da177e4 LT |
717 | !edid_check_header(edid)) { |
718 | kfree(mode); | |
719 | return NULL; | |
720 | } | |
721 | ||
722 | *dbsize = 0; | |
723 | ||
1da177e4 LT |
724 | DPRINTK(" Detailed Timings\n"); |
725 | block = edid + DETAILED_TIMING_DESCRIPTIONS_START; | |
726 | for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) { | |
14c8102f | 727 | if (!(block[0] == 0x00 && block[1] == 0x00)) { |
1da177e4 LT |
728 | get_detailed_timing(block, &mode[num]); |
729 | if (first) { | |
730 | mode[num].flag |= FB_MODE_IS_FIRST; | |
731 | first = 0; | |
732 | } | |
733 | num++; | |
734 | } | |
735 | } | |
14c8102f AD |
736 | |
737 | DPRINTK(" Supported VESA Modes\n"); | |
738 | block = edid + ESTABLISHED_TIMING_1; | |
739 | num += get_est_timing(block, &mode[num]); | |
740 | ||
741 | DPRINTK(" Standard Timings\n"); | |
742 | block = edid + STD_TIMING_DESCRIPTIONS_START; | |
743 | for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE) | |
43dcd13b | 744 | num += get_std_timing(block, &mode[num], ver, rev); |
14c8102f AD |
745 | |
746 | block = edid + DETAILED_TIMING_DESCRIPTIONS_START; | |
747 | for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) { | |
748 | if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa) | |
43dcd13b | 749 | num += get_dst_timing(block + 5, &mode[num], ver, rev); |
14c8102f | 750 | } |
8c85fd89 | 751 | |
1da177e4 LT |
752 | /* Yikes, EDID data is totally useless */ |
753 | if (!num) { | |
754 | kfree(mode); | |
755 | return NULL; | |
756 | } | |
757 | ||
758 | *dbsize = num; | |
759 | m = kmalloc(num * sizeof(struct fb_videomode), GFP_KERNEL); | |
760 | if (!m) | |
761 | return mode; | |
762 | memmove(m, mode, num * sizeof(struct fb_videomode)); | |
763 | kfree(mode); | |
764 | return m; | |
765 | } | |
766 | ||
767 | /** | |
3c2a0909 | 768 | * fb_destroy_xxdb - destroys databases |
1da177e4 | 769 | * @modedb: mode database to destroy |
3c2a0909 S |
770 | * @audiodb: audio database to destroy |
771 | * @videodb: video database to destroy | |
772 | * @vsdb: vendor specific data blcok database to destroy | |
1da177e4 LT |
773 | * |
774 | * DESCRIPTION: | |
3c2a0909 | 775 | * Destroy databases created by fb_create_modedb or fb_edid_add_monspecs |
1da177e4 LT |
776 | */ |
777 | void fb_destroy_modedb(struct fb_videomode *modedb) | |
778 | { | |
779 | kfree(modedb); | |
780 | } | |
781 | ||
3c2a0909 S |
782 | void fb_destroy_audiodb(struct fb_audio *audiodb) |
783 | { | |
784 | kfree(audiodb); | |
785 | } | |
786 | ||
787 | void fb_destroy_videodb(struct fb_video *videodb) | |
788 | { | |
789 | kfree(videodb); | |
790 | } | |
791 | ||
792 | void fb_destroy_vsdb(struct fb_vendor *vsdb) | |
793 | { | |
794 | kfree(vsdb); | |
795 | } | |
796 | ||
1da177e4 LT |
797 | static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) |
798 | { | |
799 | int i, retval = 1; | |
800 | unsigned char *block; | |
801 | ||
802 | block = edid + DETAILED_TIMING_DESCRIPTIONS_START; | |
803 | ||
804 | DPRINTK(" Monitor Operating Limits: "); | |
30076831 | 805 | |
1da177e4 LT |
806 | for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { |
807 | if (edid_is_limits_block(block)) { | |
808 | specs->hfmin = H_MIN_RATE * 1000; | |
809 | specs->hfmax = H_MAX_RATE * 1000; | |
810 | specs->vfmin = V_MIN_RATE; | |
811 | specs->vfmax = V_MAX_RATE; | |
812 | specs->dclkmax = MAX_PIXEL_CLOCK * 1000000; | |
813 | specs->gtf = (GTF_SUPPORT) ? 1 : 0; | |
814 | retval = 0; | |
815 | DPRINTK("From EDID\n"); | |
816 | break; | |
817 | } | |
818 | } | |
30076831 | 819 | |
1da177e4 LT |
820 | /* estimate monitor limits based on modes supported */ |
821 | if (retval) { | |
30076831 | 822 | struct fb_videomode *modes, *mode; |
cb85063a | 823 | int num_modes, hz, hscan, pixclock; |
30076831 | 824 | int vtotal, htotal; |
1da177e4 LT |
825 | |
826 | modes = fb_create_modedb(edid, &num_modes); | |
827 | if (!modes) { | |
828 | DPRINTK("None Available\n"); | |
829 | return 1; | |
830 | } | |
831 | ||
832 | retval = 0; | |
833 | for (i = 0; i < num_modes; i++) { | |
30076831 | 834 | mode = &modes[i]; |
1da177e4 | 835 | pixclock = PICOS2KHZ(modes[i].pixclock) * 1000; |
30076831 AD |
836 | htotal = mode->xres + mode->right_margin + mode->hsync_len |
837 | + mode->left_margin; | |
838 | vtotal = mode->yres + mode->lower_margin + mode->vsync_len | |
839 | + mode->upper_margin; | |
840 | ||
841 | if (mode->vmode & FB_VMODE_INTERLACED) | |
842 | vtotal /= 2; | |
843 | ||
844 | if (mode->vmode & FB_VMODE_DOUBLE) | |
845 | vtotal *= 2; | |
846 | ||
847 | hscan = (pixclock + htotal / 2) / htotal; | |
848 | hscan = (hscan + 500) / 1000 * 1000; | |
849 | hz = (hscan + vtotal / 2) / vtotal; | |
8c85fd89 | 850 | |
1da177e4 LT |
851 | if (specs->dclkmax == 0 || specs->dclkmax < pixclock) |
852 | specs->dclkmax = pixclock; | |
30076831 | 853 | |
1da177e4 LT |
854 | if (specs->dclkmin == 0 || specs->dclkmin > pixclock) |
855 | specs->dclkmin = pixclock; | |
30076831 | 856 | |
1da177e4 LT |
857 | if (specs->hfmax == 0 || specs->hfmax < hscan) |
858 | specs->hfmax = hscan; | |
30076831 | 859 | |
1da177e4 LT |
860 | if (specs->hfmin == 0 || specs->hfmin > hscan) |
861 | specs->hfmin = hscan; | |
30076831 | 862 | |
1da177e4 LT |
863 | if (specs->vfmax == 0 || specs->vfmax < hz) |
864 | specs->vfmax = hz; | |
30076831 | 865 | |
1da177e4 LT |
866 | if (specs->vfmin == 0 || specs->vfmin > hz) |
867 | specs->vfmin = hz; | |
868 | } | |
869 | DPRINTK("Extrapolated\n"); | |
870 | fb_destroy_modedb(modes); | |
871 | } | |
872 | DPRINTK(" H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n", | |
873 | specs->hfmin/1000, specs->hfmax/1000, specs->vfmin, | |
874 | specs->vfmax, specs->dclkmax/1000000); | |
875 | return retval; | |
876 | } | |
877 | ||
878 | static void get_monspecs(unsigned char *edid, struct fb_monspecs *specs) | |
879 | { | |
880 | unsigned char c, *block; | |
881 | ||
882 | block = edid + EDID_STRUCT_DISPLAY; | |
883 | ||
884 | fb_get_monitor_limits(edid, specs); | |
885 | ||
886 | c = block[0] & 0x80; | |
887 | specs->input = 0; | |
888 | if (c) { | |
889 | specs->input |= FB_DISP_DDI; | |
890 | DPRINTK(" Digital Display Input"); | |
891 | } else { | |
892 | DPRINTK(" Analog Display Input: Input Voltage - "); | |
893 | switch ((block[0] & 0x60) >> 5) { | |
894 | case 0: | |
895 | DPRINTK("0.700V/0.300V"); | |
896 | specs->input |= FB_DISP_ANA_700_300; | |
897 | break; | |
898 | case 1: | |
899 | DPRINTK("0.714V/0.286V"); | |
900 | specs->input |= FB_DISP_ANA_714_286; | |
901 | break; | |
902 | case 2: | |
903 | DPRINTK("1.000V/0.400V"); | |
904 | specs->input |= FB_DISP_ANA_1000_400; | |
905 | break; | |
906 | case 3: | |
907 | DPRINTK("0.700V/0.000V"); | |
908 | specs->input |= FB_DISP_ANA_700_000; | |
909 | break; | |
910 | } | |
911 | } | |
912 | DPRINTK("\n Sync: "); | |
913 | c = block[0] & 0x10; | |
914 | if (c) | |
915 | DPRINTK(" Configurable signal level\n"); | |
916 | c = block[0] & 0x0f; | |
917 | specs->signal = 0; | |
918 | if (c & 0x10) { | |
919 | DPRINTK("Blank to Blank "); | |
920 | specs->signal |= FB_SIGNAL_BLANK_BLANK; | |
921 | } | |
922 | if (c & 0x08) { | |
923 | DPRINTK("Separate "); | |
924 | specs->signal |= FB_SIGNAL_SEPARATE; | |
925 | } | |
926 | if (c & 0x04) { | |
927 | DPRINTK("Composite "); | |
928 | specs->signal |= FB_SIGNAL_COMPOSITE; | |
929 | } | |
930 | if (c & 0x02) { | |
931 | DPRINTK("Sync on Green "); | |
932 | specs->signal |= FB_SIGNAL_SYNC_ON_GREEN; | |
933 | } | |
934 | if (c & 0x01) { | |
935 | DPRINTK("Serration on "); | |
936 | specs->signal |= FB_SIGNAL_SERRATION_ON; | |
937 | } | |
938 | DPRINTK("\n"); | |
939 | specs->max_x = block[1]; | |
940 | specs->max_y = block[2]; | |
941 | DPRINTK(" Max H-size in cm: "); | |
942 | if (specs->max_x) | |
943 | DPRINTK("%d\n", specs->max_x); | |
944 | else | |
945 | DPRINTK("variable\n"); | |
946 | DPRINTK(" Max V-size in cm: "); | |
947 | if (specs->max_y) | |
948 | DPRINTK("%d\n", specs->max_y); | |
949 | else | |
950 | DPRINTK("variable\n"); | |
951 | ||
952 | c = block[3]; | |
953 | specs->gamma = c+100; | |
954 | DPRINTK(" Gamma: "); | |
955 | DPRINTK("%d.%d\n", specs->gamma/100, specs->gamma % 100); | |
956 | ||
957 | get_dpms_capabilities(block[4], specs); | |
958 | ||
959 | switch ((block[4] & 0x18) >> 3) { | |
960 | case 0: | |
961 | DPRINTK(" Monochrome/Grayscale\n"); | |
962 | specs->input |= FB_DISP_MONO; | |
963 | break; | |
964 | case 1: | |
965 | DPRINTK(" RGB Color Display\n"); | |
966 | specs->input |= FB_DISP_RGB; | |
967 | break; | |
968 | case 2: | |
969 | DPRINTK(" Non-RGB Multicolor Display\n"); | |
970 | specs->input |= FB_DISP_MULTI; | |
971 | break; | |
972 | default: | |
973 | DPRINTK(" Unknown\n"); | |
974 | specs->input |= FB_DISP_UNKNOWN; | |
975 | break; | |
976 | } | |
977 | ||
978 | get_chroma(block, specs); | |
979 | ||
980 | specs->misc = 0; | |
981 | c = block[4] & 0x7; | |
982 | if (c & 0x04) { | |
983 | DPRINTK(" Default color format is primary\n"); | |
984 | specs->misc |= FB_MISC_PRIM_COLOR; | |
985 | } | |
986 | if (c & 0x02) { | |
987 | DPRINTK(" First DETAILED Timing is preferred\n"); | |
988 | specs->misc |= FB_MISC_1ST_DETAIL; | |
989 | } | |
990 | if (c & 0x01) { | |
991 | printk(" Display is GTF capable\n"); | |
992 | specs->gtf = 1; | |
993 | } | |
994 | } | |
995 | ||
1da177e4 LT |
996 | int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) |
997 | { | |
998 | int i; | |
999 | unsigned char *block; | |
1000 | ||
1001 | if (edid == NULL || var == NULL) | |
1002 | return 1; | |
1003 | ||
1004 | if (!(edid_checksum(edid))) | |
1005 | return 1; | |
1006 | ||
1007 | if (!(edid_check_header(edid))) | |
1008 | return 1; | |
1009 | ||
1010 | block = edid + DETAILED_TIMING_DESCRIPTIONS_START; | |
1011 | ||
1012 | for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { | |
1013 | if (edid_is_timing_block(block)) { | |
1014 | var->xres = var->xres_virtual = H_ACTIVE; | |
1015 | var->yres = var->yres_virtual = V_ACTIVE; | |
43a3abc6 | 1016 | var->height = var->width = 0; |
1da177e4 LT |
1017 | var->right_margin = H_SYNC_OFFSET; |
1018 | var->left_margin = (H_ACTIVE + H_BLANKING) - | |
1019 | (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); | |
1020 | var->upper_margin = V_BLANKING - V_SYNC_OFFSET - | |
1021 | V_SYNC_WIDTH; | |
1022 | var->lower_margin = V_SYNC_OFFSET; | |
1023 | var->hsync_len = H_SYNC_WIDTH; | |
1024 | var->vsync_len = V_SYNC_WIDTH; | |
1025 | var->pixclock = PIXEL_CLOCK; | |
1026 | var->pixclock /= 1000; | |
1027 | var->pixclock = KHZ2PICOS(var->pixclock); | |
1028 | ||
1029 | if (HSYNC_POSITIVE) | |
1030 | var->sync |= FB_SYNC_HOR_HIGH_ACT; | |
1031 | if (VSYNC_POSITIVE) | |
1032 | var->sync |= FB_SYNC_VERT_HIGH_ACT; | |
1033 | return 0; | |
1034 | } | |
1035 | } | |
1036 | return 1; | |
1037 | } | |
1038 | ||
3c2a0909 | 1039 | int fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) |
1da177e4 LT |
1040 | { |
1041 | unsigned char *block; | |
475666d4 | 1042 | int i, found = 0; |
1da177e4 LT |
1043 | |
1044 | if (edid == NULL) | |
3c2a0909 | 1045 | return -EINVAL; |
1da177e4 LT |
1046 | |
1047 | if (!(edid_checksum(edid))) | |
3c2a0909 | 1048 | return -EINVAL; |
1da177e4 LT |
1049 | |
1050 | if (!(edid_check_header(edid))) | |
3c2a0909 | 1051 | return -EINVAL; |
1da177e4 LT |
1052 | |
1053 | memset(specs, 0, sizeof(struct fb_monspecs)); | |
1054 | ||
1055 | specs->version = edid[EDID_STRUCT_VERSION]; | |
1056 | specs->revision = edid[EDID_STRUCT_REVISION]; | |
1057 | ||
1058 | DPRINTK("========================================\n"); | |
1059 | DPRINTK("Display Information (EDID)\n"); | |
1060 | DPRINTK("========================================\n"); | |
1061 | DPRINTK(" EDID Version %d.%d\n", (int) specs->version, | |
1062 | (int) specs->revision); | |
1063 | ||
1064 | parse_vendor_block(edid + ID_MANUFACTURER_NAME, specs); | |
1065 | ||
1066 | block = edid + DETAILED_TIMING_DESCRIPTIONS_START; | |
1067 | for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { | |
1068 | if (edid_is_serial_block(block)) { | |
1069 | copy_string(block, specs->serial_no); | |
1070 | DPRINTK(" Serial Number: %s\n", specs->serial_no); | |
1071 | } else if (edid_is_ascii_block(block)) { | |
1072 | copy_string(block, specs->ascii); | |
1073 | DPRINTK(" ASCII Block: %s\n", specs->ascii); | |
1074 | } else if (edid_is_monitor_block(block)) { | |
1075 | copy_string(block, specs->monitor); | |
1076 | DPRINTK(" Monitor Name: %s\n", specs->monitor); | |
1077 | } | |
1078 | } | |
1079 | ||
1080 | DPRINTK(" Display Characteristics:\n"); | |
1081 | get_monspecs(edid, specs); | |
1082 | ||
1083 | specs->modedb = fb_create_modedb(edid, &specs->modedb_len); | |
475666d4 AD |
1084 | |
1085 | /* | |
1086 | * Workaround for buggy EDIDs that sets that the first | |
1087 | * detailed timing is preferred but has not detailed | |
1088 | * timing specified | |
1089 | */ | |
1090 | for (i = 0; i < specs->modedb_len; i++) { | |
1091 | if (specs->modedb[i].flag & FB_MODE_IS_DETAILED) { | |
1092 | found = 1; | |
1093 | break; | |
1094 | } | |
1095 | } | |
1096 | ||
1097 | if (!found) | |
1098 | specs->misc &= ~FB_MISC_1ST_DETAIL; | |
1099 | ||
1da177e4 | 1100 | DPRINTK("========================================\n"); |
3c2a0909 S |
1101 | return 1; |
1102 | } | |
1103 | ||
1104 | /** | |
1105 | * fb_edid_get_cea_sample_rates() - convert from CEA sample rate format | |
1106 | * @cea_sample_rates: sample rate bitfield | |
1107 | * | |
1108 | * DESCRIPTION: | |
1109 | * | |
1110 | * This function converts from the sample rates bitfield given in | |
1111 | * SAD byte 2 of the Audio Data Block from the CEA EDID Timing | |
1112 | * Extension v3 to something that can be understood here. | |
1113 | */ | |
1114 | static u8 fb_edid_get_cea_sample_rates(u8 cea_sample_rates) | |
1115 | { | |
1116 | u8 rates = 0; | |
1117 | ||
1118 | if (cea_sample_rates & (1 << 0)) | |
1119 | rates |= FB_AUDIO_32KHZ; | |
1120 | if (cea_sample_rates & (1 << 1)) | |
1121 | rates |= FB_AUDIO_44KHZ; | |
1122 | if (cea_sample_rates & (1 << 2)) | |
1123 | rates |= FB_AUDIO_48KHZ; | |
1124 | if (cea_sample_rates & (1 << 3)) | |
1125 | rates |= FB_AUDIO_88KHZ; | |
1126 | if (cea_sample_rates & (1 << 4)) | |
1127 | rates |= FB_AUDIO_96KHZ; | |
1128 | if (cea_sample_rates & (1 << 5)) | |
1129 | rates |= FB_AUDIO_176KHZ; | |
1130 | if (cea_sample_rates & (1 << 6)) | |
1131 | rates |= FB_AUDIO_192KHZ; | |
1132 | ||
1133 | return rates; | |
1134 | } | |
1135 | ||
1136 | /** | |
1137 | * fb_edid_get_cea_bit_rates() - convert from CEA bit rate format | |
1138 | * @cea_bit_rates: bit rate bitfield | |
1139 | * | |
1140 | * DESCRIPTION: | |
1141 | * | |
1142 | * This function converts from the bit rate bitfield given in | |
1143 | * SAD byte 3 of the Audio Data Block from the CEA EDID Timing | |
1144 | * Extension v3 to something that can be understood here. | |
1145 | */ | |
1146 | static u8 fb_edid_get_cea_bit_rates(u8 cea_bit_rates) | |
1147 | { | |
1148 | u8 rates = 0; | |
1149 | ||
1150 | if (cea_bit_rates & (1 << 0)) | |
1151 | rates |= FB_AUDIO_16BIT; | |
1152 | if (cea_bit_rates & (1 << 1)) | |
1153 | rates |= FB_AUDIO_20BIT; | |
1154 | if (cea_bit_rates & (1 << 2)) | |
1155 | rates |= FB_AUDIO_24BIT; | |
1156 | ||
1157 | return rates; | |
1da177e4 LT |
1158 | } |
1159 | ||
e4105119 GL |
1160 | /** |
1161 | * fb_edid_add_monspecs() - add monitor video modes from E-EDID data | |
1162 | * @edid: 128 byte array with an E-EDID block | |
3c2a0909 | 1163 | * @specs: monitor specs to be extended |
e4105119 | 1164 | */ |
3c2a0909 | 1165 | int fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) |
9fbbdde9 EG |
1166 | { |
1167 | unsigned char *block; | |
3c2a0909 S |
1168 | struct fb_videomode *m = NULL; |
1169 | struct fb_video *videodb = NULL; | |
1170 | struct fb_audio *audiodb = NULL; | |
1171 | struct fb_vendor *vsdb = NULL; | |
e4105119 | 1172 | int num = 0, i; |
3c2a0909 S |
1173 | u8 sad[128 - 5], svd[64], svsdb[128 - 5]; |
1174 | u8 edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE]; | |
1175 | u8 pos = 4, sad_n = 0, svd_n = 0, svsdb_n = 0; | |
9fbbdde9 | 1176 | |
e4105119 | 1177 | if (!edid) |
3c2a0909 | 1178 | return -EINVAL; |
9fbbdde9 EG |
1179 | |
1180 | if (!edid_checksum(edid)) | |
3c2a0909 | 1181 | return -EINVAL; |
9fbbdde9 | 1182 | |
3c2a0909 | 1183 | if (edid[0] != 0x2 || edid[1] != 0x3 || |
e4105119 | 1184 | edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE) |
3c2a0909 | 1185 | return -EINVAL; |
9fbbdde9 | 1186 | |
3c2a0909 | 1187 | DPRINTK(" Data Block Collection\n"); |
0ad83f68 GL |
1188 | |
1189 | while (pos < edid[2]) { | |
1190 | u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7; | |
1191 | pr_debug("Data block %u of %u bytes\n", type, len); | |
3c2a0909 S |
1192 | |
1193 | if (len == 0) | |
1194 | break; | |
1195 | ||
1196 | pos++; | |
1197 | if (type == 1) { | |
1198 | /* Short Audio Descriptors */ | |
1199 | for (i = pos; i < pos + len; i += 3) { | |
1200 | if (((edid[i] >> 3) & 0xf) != 1) | |
1201 | continue; /* skip non-lpcm */ | |
1202 | ||
1203 | pr_debug("LPCM ch=%d\n", (edid[i] & 7) + 1); | |
1204 | ||
1205 | sad[sad_n++] = (edid[i] & 7) + 1; | |
1206 | sad[sad_n++] = edid[i + 1]; | |
1207 | sad[sad_n++] = edid[i + 2]; | |
1208 | } | |
1209 | } else if (type == 2) { | |
1210 | /* Short Video Descriptors */ | |
0ad83f68 | 1211 | for (i = pos; i < pos + len; i++) { |
3c2a0909 | 1212 | u8 idx = edid[i] & 0x7f; |
0ad83f68 GL |
1213 | svd[svd_n++] = idx; |
1214 | pr_debug("N%sative mode #%d\n", | |
3c2a0909 | 1215 | edid[i] & 0x80 ? "" : "on-n", idx); |
0ad83f68 | 1216 | } |
3c2a0909 S |
1217 | } else if (type == 3 && len >= 3) { |
1218 | /* Vendor Specific Data Block */ | |
1219 | for (i = pos; i < pos + len; i++) | |
1220 | svsdb[++svsdb_n] = edid[i]; | |
1221 | } | |
1222 | ||
1223 | pos += len; | |
1224 | } | |
1225 | ||
1226 | if (sad_n > 0) { | |
1227 | /* Short audio descriptors are in blocks of 3 bytes */ | |
1228 | sad_n /= 3; | |
1229 | pr_debug("Found %d lpcm audio blocks\n", sad_n); | |
1230 | audiodb = kzalloc(sad_n * sizeof(struct fb_audio), GFP_KERNEL); | |
1231 | if (!audiodb) | |
1232 | goto fail_audiodb; | |
1233 | ||
1234 | for (i = 0; i < sad_n; i++) { | |
1235 | audiodb[i].format = FB_AUDIO_LPCM; | |
1236 | audiodb[i].channel_count = sad[i * 3]; | |
1237 | audiodb[i].sample_rates = | |
1238 | fb_edid_get_cea_sample_rates(sad[i * 3 + 1]); | |
1239 | audiodb[i].bit_rates = | |
1240 | fb_edid_get_cea_bit_rates(sad[i * 3 + 2]); | |
1241 | } | |
1242 | ||
1243 | kfree(specs->audiodb); | |
1244 | specs->audiodb = audiodb; | |
1245 | specs->audiodb_len = sad_n; | |
1246 | } else { | |
1247 | kfree(specs->audiodb); | |
1248 | specs->audiodb = NULL; | |
1249 | specs->audiodb_len = 0; | |
1250 | } | |
1251 | ||
1252 | if (svd_n > 0) { | |
1253 | videodb = kzalloc(svd_n * sizeof(struct fb_video), GFP_KERNEL); | |
1254 | if (!videodb) | |
1255 | goto fail_videodb; | |
1256 | ||
1257 | for (i = 0; i < svd_n; i++) { | |
1258 | videodb[i].vic_idx = svd[i]; | |
1259 | videodb[i].xres = cea_modes[svd[i]].xres; | |
1260 | videodb[i].yres = cea_modes[svd[i]].yres; | |
1261 | videodb[i].refresh = cea_modes[svd[i]].refresh; | |
1262 | videodb[i].vmode = cea_modes[svd[i]].vmode; | |
1263 | } | |
1264 | ||
1265 | kfree(specs->videodb); | |
1266 | specs->videodb = videodb; | |
1267 | specs->videodb_len = svd_n; | |
1268 | } else { | |
1269 | kfree(specs->videodb); | |
1270 | specs->videodb = NULL; | |
1271 | specs->videodb_len = 0; | |
1272 | } | |
1273 | ||
1274 | if (svsdb_n > 0) { | |
1275 | vsdb = kzalloc(sizeof(struct fb_vendor), GFP_KERNEL); | |
1276 | if (!vsdb) | |
1277 | goto fail_vsdb; | |
1278 | ||
1279 | parse_vendor_specific_block(vsdb, svsdb, svsdb_n); | |
1280 | if (vsdb->ieee_reg == 0x000c03) | |
1281 | specs->misc |= FB_MISC_HDMI; | |
1282 | ||
1283 | kfree(specs->vsdb); | |
1284 | specs->vsdb = vsdb; | |
1285 | } else { | |
1286 | kfree(specs->vsdb); | |
1287 | specs->vsdb = NULL; | |
0ad83f68 GL |
1288 | } |
1289 | ||
e4105119 | 1290 | block = edid + edid[2]; |
9fbbdde9 EG |
1291 | |
1292 | DPRINTK(" Extended Detailed Timings\n"); | |
1293 | ||
e4105119 GL |
1294 | for (i = 0; i < (128 - edid[2]) / DETAILED_TIMING_DESCRIPTION_SIZE; |
1295 | i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) | |
1296 | if (PIXEL_CLOCK) | |
1297 | edt[num++] = block - edid; | |
9fbbdde9 | 1298 | |
3c2a0909 | 1299 | /* No video descriptors, so nothing more to do */ |
0ad83f68 | 1300 | if (!(num + svd_n)) |
3c2a0909 | 1301 | return 1; |
9fbbdde9 | 1302 | |
0ad83f68 | 1303 | m = kzalloc((specs->modedb_len + num + svd_n) * |
9fbbdde9 EG |
1304 | sizeof(struct fb_videomode), GFP_KERNEL); |
1305 | ||
e4105119 | 1306 | if (!m) |
3c2a0909 | 1307 | goto fail_modedb; |
e4105119 GL |
1308 | |
1309 | memcpy(m, specs->modedb, specs->modedb_len * sizeof(struct fb_videomode)); | |
1310 | ||
1311 | for (i = specs->modedb_len; i < specs->modedb_len + num; i++) { | |
1312 | get_detailed_timing(edid + edt[i - specs->modedb_len], &m[i]); | |
1313 | if (i == specs->modedb_len) | |
1314 | m[i].flag |= FB_MODE_IS_FIRST; | |
1315 | pr_debug("Adding %ux%u@%u\n", m[i].xres, m[i].yres, m[i].refresh); | |
9fbbdde9 EG |
1316 | } |
1317 | ||
0ad83f68 GL |
1318 | for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) { |
1319 | int idx = svd[i - specs->modedb_len - num]; | |
3c2a0909 | 1320 | if (!idx || idx > (CEA_MODEDB_SIZE - 1)) { |
0ad83f68 | 1321 | pr_warning("Reserved SVD code %d\n", idx); |
0ad83f68 GL |
1322 | } else { |
1323 | memcpy(&m[i], cea_modes + idx, sizeof(m[i])); | |
1324 | pr_debug("Adding SVD #%d: %ux%u@%u\n", idx, | |
1325 | m[i].xres, m[i].yres, m[i].refresh); | |
1326 | } | |
1327 | } | |
1328 | ||
9fbbdde9 EG |
1329 | kfree(specs->modedb); |
1330 | specs->modedb = m; | |
0ad83f68 | 1331 | specs->modedb_len = specs->modedb_len + num + svd_n; |
3c2a0909 S |
1332 | |
1333 | return 1; | |
1334 | ||
1335 | fail_modedb: | |
1336 | kfree(vsdb); | |
1337 | ||
1338 | fail_vsdb: | |
1339 | kfree(videodb); | |
1340 | ||
1341 | fail_videodb: | |
1342 | kfree(audiodb); | |
1343 | ||
1344 | fail_audiodb: | |
1345 | return -ENOMEM; | |
9fbbdde9 EG |
1346 | } |
1347 | ||
8c85fd89 AH |
1348 | /* |
1349 | * VESA Generalized Timing Formula (GTF) | |
1da177e4 LT |
1350 | */ |
1351 | ||
1352 | #define FLYBACK 550 | |
1353 | #define V_FRONTPORCH 1 | |
1354 | #define H_OFFSET 40 | |
1355 | #define H_SCALEFACTOR 20 | |
1356 | #define H_BLANKSCALE 128 | |
1357 | #define H_GRADIENT 600 | |
1358 | #define C_VAL 30 | |
1359 | #define M_VAL 300 | |
1360 | ||
1361 | struct __fb_timings { | |
1362 | u32 dclk; | |
1363 | u32 hfreq; | |
1364 | u32 vfreq; | |
1365 | u32 hactive; | |
1366 | u32 vactive; | |
1367 | u32 hblank; | |
1368 | u32 vblank; | |
1369 | u32 htotal; | |
1370 | u32 vtotal; | |
1371 | }; | |
1372 | ||
1373 | /** | |
1374 | * fb_get_vblank - get vertical blank time | |
1375 | * @hfreq: horizontal freq | |
1376 | * | |
1377 | * DESCRIPTION: | |
8c85fd89 | 1378 | * vblank = right_margin + vsync_len + left_margin |
1da177e4 LT |
1379 | * |
1380 | * given: right_margin = 1 (V_FRONTPORCH) | |
1381 | * vsync_len = 3 | |
1382 | * flyback = 550 | |
1383 | * | |
1384 | * flyback * hfreq | |
1385 | * left_margin = --------------- - vsync_len | |
1386 | * 1000000 | |
1387 | */ | |
1388 | static u32 fb_get_vblank(u32 hfreq) | |
1389 | { | |
1390 | u32 vblank; | |
1391 | ||
8c85fd89 | 1392 | vblank = (hfreq * FLYBACK)/1000; |
1da177e4 LT |
1393 | vblank = (vblank + 500)/1000; |
1394 | return (vblank + V_FRONTPORCH); | |
1395 | } | |
1396 | ||
8c85fd89 | 1397 | /** |
1da177e4 LT |
1398 | * fb_get_hblank_by_freq - get horizontal blank time given hfreq |
1399 | * @hfreq: horizontal freq | |
1400 | * @xres: horizontal resolution in pixels | |
1401 | * | |
1402 | * DESCRIPTION: | |
1403 | * | |
1404 | * xres * duty_cycle | |
1405 | * hblank = ------------------ | |
1406 | * 100 - duty_cycle | |
1407 | * | |
1408 | * duty cycle = percent of htotal assigned to inactive display | |
1409 | * duty cycle = C - (M/Hfreq) | |
1410 | * | |
1411 | * where: C = ((offset - scale factor) * blank_scale) | |
1412 | * -------------------------------------- + scale factor | |
8c85fd89 | 1413 | * 256 |
1da177e4 LT |
1414 | * M = blank_scale * gradient |
1415 | * | |
1416 | */ | |
1417 | static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres) | |
1418 | { | |
1419 | u32 c_val, m_val, duty_cycle, hblank; | |
1420 | ||
8c85fd89 | 1421 | c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 + |
1da177e4 LT |
1422 | H_SCALEFACTOR) * 1000; |
1423 | m_val = (H_BLANKSCALE * H_GRADIENT)/256; | |
1424 | m_val = (m_val * 1000000)/hfreq; | |
1425 | duty_cycle = c_val - m_val; | |
1426 | hblank = (xres * duty_cycle)/(100000 - duty_cycle); | |
1427 | return (hblank); | |
1428 | } | |
1429 | ||
8c85fd89 | 1430 | /** |
1da177e4 LT |
1431 | * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock |
1432 | * @dclk: pixelclock in Hz | |
1433 | * @xres: horizontal resolution in pixels | |
1434 | * | |
1435 | * DESCRIPTION: | |
1436 | * | |
1437 | * xres * duty_cycle | |
1438 | * hblank = ------------------ | |
1439 | * 100 - duty_cycle | |
1440 | * | |
1441 | * duty cycle = percent of htotal assigned to inactive display | |
1442 | * duty cycle = C - (M * h_period) | |
8c85fd89 | 1443 | * |
1da177e4 LT |
1444 | * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100 |
1445 | * ----------------------------------------------- | |
1446 | * 2 * M | |
1447 | * M = 300; | |
1448 | * C = 30; | |
1449 | ||
1450 | */ | |
1451 | static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres) | |
1452 | { | |
1453 | u32 duty_cycle, h_period, hblank; | |
1454 | ||
1455 | dclk /= 1000; | |
1456 | h_period = 100 - C_VAL; | |
1457 | h_period *= h_period; | |
1458 | h_period += (M_VAL * xres * 2 * 1000)/(5 * dclk); | |
8c85fd89 | 1459 | h_period *= 10000; |
1da177e4 LT |
1460 | |
1461 | h_period = int_sqrt(h_period); | |
1462 | h_period -= (100 - C_VAL) * 100; | |
8c85fd89 | 1463 | h_period *= 1000; |
1da177e4 LT |
1464 | h_period /= 2 * M_VAL; |
1465 | ||
1466 | duty_cycle = C_VAL * 1000 - (M_VAL * h_period)/100; | |
1467 | hblank = (xres * duty_cycle)/(100000 - duty_cycle) + 8; | |
1468 | hblank &= ~15; | |
1469 | return (hblank); | |
1470 | } | |
8c85fd89 | 1471 | |
1da177e4 LT |
1472 | /** |
1473 | * fb_get_hfreq - estimate hsync | |
1474 | * @vfreq: vertical refresh rate | |
1475 | * @yres: vertical resolution | |
1476 | * | |
1477 | * DESCRIPTION: | |
1478 | * | |
1479 | * (yres + front_port) * vfreq * 1000000 | |
1480 | * hfreq = ------------------------------------- | |
1481 | * (1000000 - (vfreq * FLYBACK) | |
8c85fd89 | 1482 | * |
1da177e4 LT |
1483 | */ |
1484 | ||
1485 | static u32 fb_get_hfreq(u32 vfreq, u32 yres) | |
1486 | { | |
1487 | u32 divisor, hfreq; | |
8c85fd89 | 1488 | |
1da177e4 LT |
1489 | divisor = (1000000 - (vfreq * FLYBACK))/1000; |
1490 | hfreq = (yres + V_FRONTPORCH) * vfreq * 1000; | |
1491 | return (hfreq/divisor); | |
1492 | } | |
1493 | ||
1494 | static void fb_timings_vfreq(struct __fb_timings *timings) | |
1495 | { | |
1496 | timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive); | |
1497 | timings->vblank = fb_get_vblank(timings->hfreq); | |
1498 | timings->vtotal = timings->vactive + timings->vblank; | |
8c85fd89 | 1499 | timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, |
1da177e4 LT |
1500 | timings->hactive); |
1501 | timings->htotal = timings->hactive + timings->hblank; | |
1502 | timings->dclk = timings->htotal * timings->hfreq; | |
1503 | } | |
1504 | ||
1505 | static void fb_timings_hfreq(struct __fb_timings *timings) | |
1506 | { | |
1507 | timings->vblank = fb_get_vblank(timings->hfreq); | |
1508 | timings->vtotal = timings->vactive + timings->vblank; | |
1509 | timings->vfreq = timings->hfreq/timings->vtotal; | |
8c85fd89 | 1510 | timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, |
1da177e4 LT |
1511 | timings->hactive); |
1512 | timings->htotal = timings->hactive + timings->hblank; | |
1513 | timings->dclk = timings->htotal * timings->hfreq; | |
1514 | } | |
1515 | ||
1516 | static void fb_timings_dclk(struct __fb_timings *timings) | |
1517 | { | |
8c85fd89 | 1518 | timings->hblank = fb_get_hblank_by_dclk(timings->dclk, |
1da177e4 LT |
1519 | timings->hactive); |
1520 | timings->htotal = timings->hactive + timings->hblank; | |
1521 | timings->hfreq = timings->dclk/timings->htotal; | |
1522 | timings->vblank = fb_get_vblank(timings->hfreq); | |
1523 | timings->vtotal = timings->vactive + timings->vblank; | |
1524 | timings->vfreq = timings->hfreq/timings->vtotal; | |
1525 | } | |
1526 | ||
1527 | /* | |
1528 | * fb_get_mode - calculates video mode using VESA GTF | |
1529 | * @flags: if: 0 - maximize vertical refresh rate | |
1530 | * 1 - vrefresh-driven calculation; | |
1531 | * 2 - hscan-driven calculation; | |
1532 | * 3 - pixelclock-driven calculation; | |
1533 | * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock | |
1534 | * @var: pointer to fb_var_screeninfo | |
1535 | * @info: pointer to fb_info | |
1536 | * | |
1537 | * DESCRIPTION: | |
8c85fd89 AH |
1538 | * Calculates video mode based on monitor specs using VESA GTF. |
1539 | * The GTF is best for VESA GTF compliant monitors but is | |
1da177e4 LT |
1540 | * specifically formulated to work for older monitors as well. |
1541 | * | |
8c85fd89 | 1542 | * If @flag==0, the function will attempt to maximize the |
1da177e4 | 1543 | * refresh rate. Otherwise, it will calculate timings based on |
8c85fd89 | 1544 | * the flag and accompanying value. |
1da177e4 | 1545 | * |
8c85fd89 | 1546 | * If FB_IGNOREMON bit is set in @flags, monitor specs will be |
1da177e4 LT |
1547 | * ignored and @var will be filled with the calculated timings. |
1548 | * | |
1549 | * All calculations are based on the VESA GTF Spreadsheet | |
1550 | * available at VESA's public ftp (http://www.vesa.org). | |
8c85fd89 | 1551 | * |
1da177e4 LT |
1552 | * NOTES: |
1553 | * The timings generated by the GTF will be different from VESA | |
1554 | * DMT. It might be a good idea to keep a table of standard | |
1555 | * VESA modes as well. The GTF may also not work for some displays, | |
1556 | * such as, and especially, analog TV. | |
8c85fd89 | 1557 | * |
1da177e4 LT |
1558 | * REQUIRES: |
1559 | * A valid info->monspecs, otherwise 'safe numbers' will be used. | |
8c85fd89 | 1560 | */ |
1da177e4 LT |
1561 | int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info) |
1562 | { | |
af5d0f7e | 1563 | struct __fb_timings *timings; |
1da177e4 | 1564 | u32 interlace = 1, dscan = 1; |
af5d0f7e AD |
1565 | u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0; |
1566 | ||
1567 | ||
1568 | timings = kzalloc(sizeof(struct __fb_timings), GFP_KERNEL); | |
1569 | ||
1570 | if (!timings) | |
1571 | return -ENOMEM; | |
1da177e4 | 1572 | |
8c85fd89 | 1573 | /* |
1da177e4 LT |
1574 | * If monspecs are invalid, use values that are enough |
1575 | * for 640x480@60 | |
1576 | */ | |
af5d0f7e | 1577 | if (!info || !info->monspecs.hfmax || !info->monspecs.vfmax || |
1da177e4 LT |
1578 | !info->monspecs.dclkmax || |
1579 | info->monspecs.hfmax < info->monspecs.hfmin || | |
1580 | info->monspecs.vfmax < info->monspecs.vfmin || | |
1581 | info->monspecs.dclkmax < info->monspecs.dclkmin) { | |
1582 | hfmin = 29000; hfmax = 30000; | |
1583 | vfmin = 60; vfmax = 60; | |
1584 | dclkmin = 0; dclkmax = 25000000; | |
1585 | } else { | |
1586 | hfmin = info->monspecs.hfmin; | |
1587 | hfmax = info->monspecs.hfmax; | |
1588 | vfmin = info->monspecs.vfmin; | |
1589 | vfmax = info->monspecs.vfmax; | |
1590 | dclkmin = info->monspecs.dclkmin; | |
1591 | dclkmax = info->monspecs.dclkmax; | |
1592 | } | |
1593 | ||
af5d0f7e AD |
1594 | timings->hactive = var->xres; |
1595 | timings->vactive = var->yres; | |
8c85fd89 | 1596 | if (var->vmode & FB_VMODE_INTERLACED) { |
af5d0f7e | 1597 | timings->vactive /= 2; |
1da177e4 LT |
1598 | interlace = 2; |
1599 | } | |
1600 | if (var->vmode & FB_VMODE_DOUBLE) { | |
af5d0f7e | 1601 | timings->vactive *= 2; |
1da177e4 LT |
1602 | dscan = 2; |
1603 | } | |
1604 | ||
1605 | switch (flags & ~FB_IGNOREMON) { | |
1606 | case FB_MAXTIMINGS: /* maximize refresh rate */ | |
af5d0f7e AD |
1607 | timings->hfreq = hfmax; |
1608 | fb_timings_hfreq(timings); | |
1609 | if (timings->vfreq > vfmax) { | |
1610 | timings->vfreq = vfmax; | |
1611 | fb_timings_vfreq(timings); | |
1da177e4 | 1612 | } |
af5d0f7e AD |
1613 | if (timings->dclk > dclkmax) { |
1614 | timings->dclk = dclkmax; | |
1615 | fb_timings_dclk(timings); | |
1da177e4 LT |
1616 | } |
1617 | break; | |
1618 | case FB_VSYNCTIMINGS: /* vrefresh driven */ | |
af5d0f7e AD |
1619 | timings->vfreq = val; |
1620 | fb_timings_vfreq(timings); | |
1da177e4 LT |
1621 | break; |
1622 | case FB_HSYNCTIMINGS: /* hsync driven */ | |
af5d0f7e AD |
1623 | timings->hfreq = val; |
1624 | fb_timings_hfreq(timings); | |
1da177e4 LT |
1625 | break; |
1626 | case FB_DCLKTIMINGS: /* pixelclock driven */ | |
af5d0f7e AD |
1627 | timings->dclk = PICOS2KHZ(val) * 1000; |
1628 | fb_timings_dclk(timings); | |
1da177e4 LT |
1629 | break; |
1630 | default: | |
af5d0f7e | 1631 | err = -EINVAL; |
8c85fd89 AH |
1632 | |
1633 | } | |
1634 | ||
af5d0f7e AD |
1635 | if (err || (!(flags & FB_IGNOREMON) && |
1636 | (timings->vfreq < vfmin || timings->vfreq > vfmax || | |
1637 | timings->hfreq < hfmin || timings->hfreq > hfmax || | |
1638 | timings->dclk < dclkmin || timings->dclk > dclkmax))) { | |
1639 | err = -EINVAL; | |
1640 | } else { | |
1641 | var->pixclock = KHZ2PICOS(timings->dclk/1000); | |
1642 | var->hsync_len = (timings->htotal * 8)/100; | |
1643 | var->right_margin = (timings->hblank/2) - var->hsync_len; | |
1644 | var->left_margin = timings->hblank - var->right_margin - | |
1645 | var->hsync_len; | |
1646 | var->vsync_len = (3 * interlace)/dscan; | |
1647 | var->lower_margin = (1 * interlace)/dscan; | |
1648 | var->upper_margin = (timings->vblank * interlace)/dscan - | |
1649 | (var->vsync_len + var->lower_margin); | |
1650 | } | |
8c85fd89 | 1651 | |
af5d0f7e AD |
1652 | kfree(timings); |
1653 | return err; | |
1da177e4 | 1654 | } |
2db54c72 | 1655 | |
a38884f6 | 1656 | #ifdef CONFIG_VIDEOMODE_HELPERS |
2db54c72 ST |
1657 | int fb_videomode_from_videomode(const struct videomode *vm, |
1658 | struct fb_videomode *fbmode) | |
1659 | { | |
1660 | unsigned int htotal, vtotal; | |
1661 | ||
1662 | fbmode->xres = vm->hactive; | |
1663 | fbmode->left_margin = vm->hback_porch; | |
1664 | fbmode->right_margin = vm->hfront_porch; | |
1665 | fbmode->hsync_len = vm->hsync_len; | |
1666 | ||
1667 | fbmode->yres = vm->vactive; | |
1668 | fbmode->upper_margin = vm->vback_porch; | |
1669 | fbmode->lower_margin = vm->vfront_porch; | |
1670 | fbmode->vsync_len = vm->vsync_len; | |
1671 | ||
1672 | /* prevent division by zero in KHZ2PICOS macro */ | |
1673 | fbmode->pixclock = vm->pixelclock ? | |
1674 | KHZ2PICOS(vm->pixelclock / 1000) : 0; | |
1675 | ||
1676 | fbmode->sync = 0; | |
1677 | fbmode->vmode = 0; | |
06a33079 | 1678 | if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) |
2db54c72 | 1679 | fbmode->sync |= FB_SYNC_HOR_HIGH_ACT; |
9bf9d47a | 1680 | if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) |
2db54c72 | 1681 | fbmode->sync |= FB_SYNC_VERT_HIGH_ACT; |
06a33079 | 1682 | if (vm->flags & DISPLAY_FLAGS_INTERLACED) |
2db54c72 | 1683 | fbmode->vmode |= FB_VMODE_INTERLACED; |
06a33079 | 1684 | if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN) |
2db54c72 ST |
1685 | fbmode->vmode |= FB_VMODE_DOUBLE; |
1686 | fbmode->flag = 0; | |
1687 | ||
1688 | htotal = vm->hactive + vm->hfront_porch + vm->hback_porch + | |
1689 | vm->hsync_len; | |
1690 | vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch + | |
1691 | vm->vsync_len; | |
1692 | /* prevent division by zero */ | |
1693 | if (htotal && vtotal) { | |
1694 | fbmode->refresh = vm->pixelclock / (htotal * vtotal); | |
1695 | /* a mode must have htotal and vtotal != 0 or it is invalid */ | |
1696 | } else { | |
1697 | fbmode->refresh = 0; | |
1698 | return -EINVAL; | |
1699 | } | |
1700 | ||
1701 | return 0; | |
1702 | } | |
1703 | EXPORT_SYMBOL_GPL(fb_videomode_from_videomode); | |
2db54c72 | 1704 | |
a38884f6 | 1705 | #ifdef CONFIG_OF |
79089044 ST |
1706 | static inline void dump_fb_videomode(const struct fb_videomode *m) |
1707 | { | |
1708 | pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n", | |
1709 | m->xres, m->yres, m->refresh, m->pixclock, m->left_margin, | |
1710 | m->right_margin, m->upper_margin, m->lower_margin, | |
1711 | m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag); | |
1712 | } | |
1713 | ||
1714 | /** | |
1715 | * of_get_fb_videomode - get a fb_videomode from devicetree | |
1716 | * @np: device_node with the timing specification | |
1717 | * @fb: will be set to the return value | |
1718 | * @index: index into the list of display timings in devicetree | |
1719 | * | |
1720 | * DESCRIPTION: | |
1721 | * This function is expensive and should only be used, if only one mode is to be | |
1722 | * read from DT. To get multiple modes start with of_get_display_timings ond | |
1723 | * work with that instead. | |
1724 | */ | |
1725 | int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb, | |
1726 | int index) | |
1727 | { | |
1728 | struct videomode vm; | |
1729 | int ret; | |
1730 | ||
1731 | ret = of_get_videomode(np, &vm, index); | |
1732 | if (ret) | |
1733 | return ret; | |
1734 | ||
1735 | fb_videomode_from_videomode(&vm, fb); | |
1736 | ||
1737 | pr_debug("%s: got %dx%d display mode from %s\n", | |
1738 | of_node_full_name(np), vm.hactive, vm.vactive, np->name); | |
1739 | dump_fb_videomode(fb); | |
1740 | ||
1741 | return 0; | |
1742 | } | |
1743 | EXPORT_SYMBOL_GPL(of_get_fb_videomode); | |
a38884f6 TV |
1744 | #endif /* CONFIG_OF */ |
1745 | #endif /* CONFIG_VIDEOMODE_HELPERS */ | |
79089044 | 1746 | |
1da177e4 LT |
1747 | #else |
1748 | int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) | |
1749 | { | |
1750 | return 1; | |
1751 | } | |
3c2a0909 | 1752 | int fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) |
1da177e4 LT |
1753 | { |
1754 | specs = NULL; | |
3c2a0909 | 1755 | return 1; |
1da177e4 | 1756 | } |
3c2a0909 | 1757 | int fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) |
e4105119 | 1758 | { |
3c2a0909 | 1759 | return 1; |
e4105119 | 1760 | } |
1da177e4 LT |
1761 | void fb_destroy_modedb(struct fb_videomode *modedb) |
1762 | { | |
1763 | } | |
3c2a0909 S |
1764 | void fb_destroy_audiodb(struct fb_audio *audiodb) |
1765 | { | |
1766 | } | |
1767 | void fb_destroy_videodb(struct fb_video *videodb) | |
1768 | { | |
1769 | } | |
1770 | void fb_destroy_vsdb(struct fb_vendor *vsdb) | |
1771 | { | |
1772 | } | |
1da177e4 LT |
1773 | int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, |
1774 | struct fb_info *info) | |
1775 | { | |
1776 | return -EINVAL; | |
1777 | } | |
1778 | #endif /* CONFIG_FB_MODE_HELPERS */ | |
8c85fd89 | 1779 | |
1da177e4 LT |
1780 | /* |
1781 | * fb_validate_mode - validates var against monitor capabilities | |
1782 | * @var: pointer to fb_var_screeninfo | |
1783 | * @info: pointer to fb_info | |
1784 | * | |
1785 | * DESCRIPTION: | |
1786 | * Validates video mode against monitor capabilities specified in | |
1787 | * info->monspecs. | |
1788 | * | |
1789 | * REQUIRES: | |
1790 | * A valid info->monspecs. | |
1791 | */ | |
1792 | int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info) | |
1793 | { | |
1794 | u32 hfreq, vfreq, htotal, vtotal, pixclock; | |
1795 | u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax; | |
1796 | ||
8c85fd89 | 1797 | /* |
1da177e4 LT |
1798 | * If monspecs are invalid, use values that are enough |
1799 | * for 640x480@60 | |
1800 | */ | |
1801 | if (!info->monspecs.hfmax || !info->monspecs.vfmax || | |
1802 | !info->monspecs.dclkmax || | |
1803 | info->monspecs.hfmax < info->monspecs.hfmin || | |
1804 | info->monspecs.vfmax < info->monspecs.vfmin || | |
1805 | info->monspecs.dclkmax < info->monspecs.dclkmin) { | |
1806 | hfmin = 29000; hfmax = 30000; | |
1807 | vfmin = 60; vfmax = 60; | |
1808 | dclkmin = 0; dclkmax = 25000000; | |
1809 | } else { | |
1810 | hfmin = info->monspecs.hfmin; | |
1811 | hfmax = info->monspecs.hfmax; | |
1812 | vfmin = info->monspecs.vfmin; | |
1813 | vfmax = info->monspecs.vfmax; | |
1814 | dclkmin = info->monspecs.dclkmin; | |
1815 | dclkmax = info->monspecs.dclkmax; | |
1816 | } | |
1817 | ||
1818 | if (!var->pixclock) | |
1819 | return -EINVAL; | |
1820 | pixclock = PICOS2KHZ(var->pixclock) * 1000; | |
8c85fd89 AH |
1821 | |
1822 | htotal = var->xres + var->right_margin + var->hsync_len + | |
1da177e4 | 1823 | var->left_margin; |
8c85fd89 | 1824 | vtotal = var->yres + var->lower_margin + var->vsync_len + |
1da177e4 LT |
1825 | var->upper_margin; |
1826 | ||
1827 | if (var->vmode & FB_VMODE_INTERLACED) | |
1828 | vtotal /= 2; | |
1829 | if (var->vmode & FB_VMODE_DOUBLE) | |
1830 | vtotal *= 2; | |
1831 | ||
1832 | hfreq = pixclock/htotal; | |
0a793b77 JS |
1833 | hfreq = (hfreq + 500) / 1000 * 1000; |
1834 | ||
1da177e4 LT |
1835 | vfreq = hfreq/vtotal; |
1836 | ||
8c85fd89 | 1837 | return (vfreq < vfmin || vfreq > vfmax || |
1da177e4 LT |
1838 | hfreq < hfmin || hfreq > hfmax || |
1839 | pixclock < dclkmin || pixclock > dclkmax) ? | |
1840 | -EINVAL : 0; | |
1841 | } | |
1842 | ||
ba70710e | 1843 | #if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86) |
5e518d76 AD |
1844 | |
1845 | /* | |
1846 | * We need to ensure that the EDID block is only returned for | |
1847 | * the primary graphics adapter. | |
1848 | */ | |
1849 | ||
1850 | const unsigned char *fb_firmware_edid(struct device *device) | |
1851 | { | |
1852 | struct pci_dev *dev = NULL; | |
1853 | struct resource *res = NULL; | |
1854 | unsigned char *edid = NULL; | |
1855 | ||
1856 | if (device) | |
1857 | dev = to_pci_dev(device); | |
1858 | ||
1859 | if (dev) | |
1860 | res = &dev->resource[PCI_ROM_RESOURCE]; | |
1861 | ||
1862 | if (res && res->flags & IORESOURCE_ROM_SHADOW) | |
1863 | edid = edid_info.dummy; | |
1864 | ||
1865 | return edid; | |
1866 | } | |
1867 | #else | |
1868 | const unsigned char *fb_firmware_edid(struct device *device) | |
1869 | { | |
1870 | return NULL; | |
1871 | } | |
59153f7d AD |
1872 | #endif |
1873 | EXPORT_SYMBOL(fb_firmware_edid); | |
5e518d76 | 1874 | |
1da177e4 LT |
1875 | EXPORT_SYMBOL(fb_parse_edid); |
1876 | EXPORT_SYMBOL(fb_edid_to_monspecs); | |
e4105119 | 1877 | EXPORT_SYMBOL(fb_edid_add_monspecs); |
1da177e4 LT |
1878 | EXPORT_SYMBOL(fb_get_mode); |
1879 | EXPORT_SYMBOL(fb_validate_mode); | |
1880 | EXPORT_SYMBOL(fb_destroy_modedb); | |
3c2a0909 S |
1881 | EXPORT_SYMBOL(fb_destroy_audiodb); |
1882 | EXPORT_SYMBOL(fb_destroy_videodb); | |
1883 | EXPORT_SYMBOL(fb_destroy_vsdb); |