Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright 2009 Red Hat, Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software") | |
6 | * to deal in the software without restriction, including without limitation | |
7 | * on the rights to use, copy, modify, merge, publish, distribute, sub | |
8 | * license, and/or sell copies of the Software, and to permit persons to whom | |
9 | * them Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice (including the next | |
12 | * paragraph) shall be included in all copies or substantial portions of the | |
13 | * Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTIBILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | |
18 | * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER | |
19 | * IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: | |
23 | * Adam Jackson <ajax@redhat.com> | |
24 | */ | |
25 | ||
26 | #include "xorg-config.h" | |
27 | #include "xf86.h" | |
28 | #include "xf86Modes.h" | |
29 | #include "xf86str.h" | |
30 | #include "edid.h" | |
31 | #include "xf86DDC.h" | |
32 | ||
33 | typedef void (*did_proc) (int scrnIndex, unsigned char *data, void *closure); | |
34 | ||
35 | #define DID_PRODUCT_ID 0x00 | |
36 | #define DID_DISPLAY_PARAMETERS 0x01 | |
37 | #define DID_COLOR_INFO 0x02 | |
38 | #define DID_TIMING_1_DETAILED 0x03 | |
39 | #define DID_TIMING_2_DETAILED 0x04 | |
40 | #define DID_TIMING_3_SHORT 0x05 | |
41 | #define DID_TIMING_4_DMT 0x06 | |
42 | #define DID_TIMING_VESA 0x07 | |
43 | #define DID_TIMING_CEA 0x08 | |
44 | #define DID_TIMING_RANGE_LIMITS 0x09 | |
45 | #define DID_PRODUCT_SERIAL 0x0A | |
46 | #define DID_ASCII_STRING 0x0B | |
47 | #define DID_DISPLAY_DEVICE 0x0C | |
48 | #define DID_POWER_SEQUENCING 0x0D | |
49 | #define DID_TRANSFER_INFO 0x0E | |
50 | #define DID_DISPLAY_INTERFACE 0x0F | |
51 | #define DID_STEREO 0x10 | |
52 | #define DID_VENDOR 0x7F | |
53 | ||
54 | #define extract_le16(x, i) ((x[i+1] << 8) + (x[i])) | |
55 | #define extract_le24(x, i) ((x[i+2] << 16) + (x[i+1] << 8) + (x[i])) | |
56 | ||
57 | static DisplayModePtr | |
58 | modeCalloc(void) | |
59 | { | |
60 | return calloc(1, sizeof(DisplayModeRec)); | |
61 | } | |
62 | ||
63 | /* | |
64 | * How awesome is it to have two detailed timing formats, neither of which | |
65 | * are compatible with the format in EDID? So awesome. | |
66 | */ | |
67 | ||
68 | static void | |
69 | didDetailedTiming1(int i, unsigned char *x, MonPtr mon) | |
70 | { | |
71 | DisplayModePtr m = modeCalloc(); | |
72 | ||
73 | if (!m) | |
74 | return; | |
75 | ||
76 | m->Clock = extract_le24(x, 0); | |
77 | ||
78 | m->HDisplay = extract_le16(x, 4); | |
79 | m->HSyncStart = m->HDisplay + (extract_le16(x, 8) & 0x7f); | |
80 | m->HSyncEnd = m->HSyncStart + extract_le16(x, 10); | |
81 | m->HTotal = m->HDisplay + extract_le16(x, 6); | |
82 | m->Flags |= (x[9] & 0x80) ? V_PHSYNC : V_NHSYNC; | |
83 | ||
84 | m->VDisplay = extract_le16(x, 12); | |
85 | m->VSyncStart = m->VDisplay + (extract_le16(x, 16) & 0x7f); | |
86 | m->VSyncEnd = m->VSyncStart + extract_le16(x, 18); | |
87 | m->VTotal = m->VDisplay + extract_le16(x, 14); | |
88 | m->Flags |= (x[17] & 0x80) ? V_PVSYNC : V_NVSYNC; | |
89 | ||
90 | m->type = M_T_DRIVER; | |
91 | if (x[3] & 0x80) | |
92 | m->type |= M_T_PREFERRED; | |
93 | ||
94 | /* XXX double check handling of this */ | |
95 | if (x[3] & 0x10) | |
96 | m->Flags |= V_INTERLACE; | |
97 | ||
98 | mon->Modes = xf86ModesAdd(mon->Modes, m); | |
99 | } | |
100 | ||
101 | /* XXX no sync bits. what to do? */ | |
102 | static void | |
103 | didDetailedTiming2(int i, unsigned char *x, MonPtr mon) | |
104 | { | |
105 | DisplayModePtr mode = modeCalloc(); | |
106 | ||
107 | if (!mode) | |
108 | return; | |
109 | ||
110 | mode->Clock = extract_le24(x, 0); | |
111 | ||
112 | /* horiz sizes are in character cells, not pixels, hence * 8 */ | |
113 | mode->HDisplay = ((extract_le16(x, 4) & 0x01ff) + 1) * 8; | |
114 | mode->HSyncStart = mode->HDisplay + (((x[6] & 0xf0) >> 4) + 1) * 8; | |
115 | mode->HSyncEnd = mode->HSyncStart + ((x[6] & 0x0f) + 1) * 8; | |
116 | mode->HTotal = mode->HDisplay + ((x[5] >> 1) + 1) * 8; | |
117 | ||
118 | mode->VDisplay = extract_le16(x, 7) & 0x07ff; | |
119 | mode->VSyncStart = mode->VDisplay + (x[10] >> 4) + 1; | |
120 | mode->VSyncEnd = mode->VSyncStart + (x[10] & 0x0f) + 1; | |
121 | mode->VTotal = mode->VDisplay + x[9]; | |
122 | ||
123 | mode->status = M_T_DRIVER; | |
124 | if (x[3] & 0x80) | |
125 | mode->status |= M_T_PREFERRED; | |
126 | ||
127 | /* XXX double check handling of this */ | |
128 | if (x[3] & 0x10) | |
129 | mode->Flags |= V_INTERLACE; | |
130 | ||
131 | mon->Modes = xf86ModesAdd(mon->Modes, mode); | |
132 | } | |
133 | ||
134 | static void | |
135 | didShortTiming(int i, unsigned char *x, MonPtr mon) | |
136 | { | |
137 | DisplayModePtr m; | |
138 | int w, h, r; | |
139 | ||
140 | w = (x[1] + 1) * 8; | |
141 | switch (x[0] & 0x0f) { | |
142 | case 0: | |
143 | h = w; | |
144 | break; | |
145 | case 1: | |
146 | h = (w * 4) / 5; | |
147 | break; | |
148 | case 2: | |
149 | h = (w * 3) / 4; | |
150 | break; | |
151 | case 3: | |
152 | h = (w * 9) / 15; | |
153 | break; | |
154 | case 4: | |
155 | h = (w * 9) / 16; | |
156 | break; | |
157 | case 5: | |
158 | h = (w * 10) / 16; | |
159 | break; | |
160 | default: | |
161 | return; | |
162 | } | |
163 | r = (x[2] & 0x7f) + 1; | |
164 | ||
165 | m = xf86CVTMode(w, h, r, ! !(x[0] & 0x10), ! !(x[2] & 0x80)); | |
166 | ||
167 | m->type = M_T_DRIVER; | |
168 | if (x[0] & 0x80) | |
169 | m->type |= M_T_PREFERRED; | |
170 | ||
171 | mon->Modes = xf86ModesAdd(mon->Modes, m); | |
172 | } | |
173 | ||
174 | static void | |
175 | didDMTTiming(int i, unsigned char *x, void *closure) | |
176 | { | |
177 | MonPtr mon = closure; | |
178 | ||
179 | mon->Modes = xf86ModesAdd(mon->Modes, xf86DuplicateMode(DMTModes + *x)); | |
180 | } | |
181 | ||
182 | #define RB 1 | |
183 | #define INT 2 | |
184 | static const struct did_dmt { | |
185 | short w, h, r, f; | |
186 | } did_dmt[] = { | |
187 | /* byte 3 */ | |
188 | {640, 350, 85, 0}, | |
189 | {640, 400, 85, 0}, | |
190 | {720, 400, 85, 0}, | |
191 | {640, 480, 60, 0}, | |
192 | {640, 480, 72, 0}, | |
193 | {640, 480, 75, 0}, | |
194 | {640, 480, 85, 0}, | |
195 | {800, 600, 56, 0}, | |
196 | /* byte 4 */ | |
197 | {800, 600, 60, 0}, | |
198 | {800, 600, 72, 0}, | |
199 | {800, 600, 75, 0}, | |
200 | {800, 600, 85, 0}, | |
201 | {800, 600, 120, RB}, | |
202 | {848, 480, 60, 0}, | |
203 | {1024, 768, 43, INT}, | |
204 | {1024, 768, 60, 0}, | |
205 | /* byte 5 */ | |
206 | {1024, 768, 70, 0}, | |
207 | {1024, 768, 75, 0}, | |
208 | {1024, 768, 85, 0}, | |
209 | {1024, 768, 120, RB}, | |
210 | {1152, 864, 75, 0}, | |
211 | {1280, 768, 60, RB}, | |
212 | {1280, 768, 60, 0}, | |
213 | {1280, 768, 75, 0}, | |
214 | /* byte 6 */ | |
215 | {1280, 768, 85, 0}, | |
216 | {1280, 768, 120, RB}, | |
217 | {1280, 800, 60, RB}, | |
218 | {1280, 800, 60, 0}, | |
219 | {1280, 800, 75, 0}, | |
220 | {1280, 800, 85, 0}, | |
221 | {1280, 800, 120, RB}, | |
222 | {1280, 960, 60, 0}, | |
223 | /* byte 7 */ | |
224 | {1280, 960, 85, 0}, | |
225 | {1280, 960, 120, RB}, | |
226 | {1280, 1024, 60, 0}, | |
227 | {1280, 1024, 75, 0}, | |
228 | {1280, 1024, 85, 0}, | |
229 | {1280, 1024, 120, RB}, | |
230 | {1360, 768, 60, 0}, | |
231 | {1360, 768, 120, RB}, | |
232 | /* byte 8 */ | |
233 | {1400, 1050, 60, RB}, | |
234 | {1400, 1050, 60, 0}, | |
235 | {1400, 1050, 75, 0}, | |
236 | {1400, 1050, 85, 0}, | |
237 | {1400, 1050, 120, RB}, | |
238 | {1440, 900, 60, RB}, | |
239 | {1440, 900, 60, 0}, | |
240 | {1440, 900, 75, 0}, | |
241 | /* byte 9 */ | |
242 | {1440, 900, 85, 0}, | |
243 | {1440, 900, 120, RB}, | |
244 | {1600, 1200, 60, 0}, | |
245 | {1600, 1200, 65, 0}, | |
246 | {1600, 1200, 70, 0}, | |
247 | {1600, 1200, 75, 0}, | |
248 | {1600, 1200, 85, 0}, | |
249 | {1600, 1200, 120, RB}, | |
250 | /* byte a */ | |
251 | {1680, 1050, 60, RB}, | |
252 | {1680, 1050, 60, 0}, | |
253 | {1680, 1050, 75, 0}, | |
254 | {1680, 1050, 85, 0}, | |
255 | {1680, 1050, 120, RB}, | |
256 | {1792, 1344, 60, 0}, | |
257 | {1792, 1344, 75, 0}, | |
258 | {1792, 1344, 120, RB}, | |
259 | /* byte b */ | |
260 | {1856, 1392, 60, 0}, | |
261 | {1856, 1392, 75, 0}, | |
262 | {1856, 1392, 120, RB}, | |
263 | {1920, 1200, 60, RB}, | |
264 | {1920, 1200, 60, 0}, | |
265 | {1920, 1200, 75, 0}, | |
266 | {1920, 1200, 85, 0}, | |
267 | {1920, 1200, 120, RB}, | |
268 | /* byte c */ | |
269 | {1920, 1440, 60, 0}, | |
270 | {1920, 1440, 75, 0}, | |
271 | {1920, 1440, 120, RB}, | |
272 | {2560, 1600, 60, RB}, | |
273 | {2560, 1600, 60, 0}, | |
274 | {2560, 1600, 75, 0}, | |
275 | {2560, 1600, 85, 0}, | |
276 | {2560, 1600, 120, RB}, | |
277 | }; | |
278 | ||
279 | static void | |
280 | didVesaTiming(int scrn, unsigned char *x, MonPtr mon) | |
281 | { | |
282 | int i, j; | |
283 | ||
284 | x += 3; | |
285 | ||
286 | for (i = 0; i < 10; i++) | |
287 | for (j = 0; j < 8; j++) | |
288 | if (x[i] & (1 << j)) { | |
289 | const struct did_dmt *d = &(did_dmt[i * 8 + j]); | |
290 | ||
291 | if (d->f == INT) | |
292 | continue; | |
293 | mon->Modes = xf86ModesAdd(mon->Modes, | |
294 | FindDMTMode(d->w, d->h, d->r, | |
295 | d->f == RB)); | |
296 | } | |
297 | ||
298 | } | |
299 | ||
300 | static void | |
301 | handleDisplayIDBlock(int scrnIndex, unsigned char *x, void *closure) | |
302 | { | |
303 | MonPtr mon = closure; | |
304 | ||
305 | switch (x[0]) { | |
306 | case DID_DISPLAY_PARAMETERS: | |
307 | /* w/h are in decimillimeters */ | |
308 | mon->widthmm = (extract_le16(x, 3) + 5) / 10; | |
309 | mon->heightmm = (extract_le16(x, 5) + 5) / 10; | |
310 | /* XXX pixel count, feature flags, gamma, aspect, color depth */ | |
311 | break; | |
312 | ||
313 | case DID_TIMING_RANGE_LIMITS: | |
314 | { | |
315 | int n; | |
316 | ||
317 | mon->maxPixClock = max(mon->maxPixClock, extract_le24(x, 6) * 10); | |
318 | ||
319 | n = mon->nHsync++; | |
320 | if (n < MAX_HSYNC) { | |
321 | mon->hsync[n].lo = x[9]; | |
322 | mon->hsync[n].hi = x[10]; | |
323 | } | |
324 | else { | |
325 | n = MAX_HSYNC; | |
326 | } | |
327 | n = mon->nVrefresh++; | |
328 | if (n < MAX_VREFRESH) { | |
329 | mon->vrefresh[n].lo = x[13]; | |
330 | mon->vrefresh[n].hi = x[14]; | |
331 | } | |
332 | else { | |
333 | n = MAX_VREFRESH; | |
334 | } | |
335 | break; | |
336 | } | |
337 | ||
338 | case DID_TIMING_1_DETAILED: | |
339 | { | |
340 | int i; | |
341 | ||
342 | for (i = 0; i < x[2]; i += 20) | |
343 | didDetailedTiming1(scrnIndex, x + i + 3, mon); | |
344 | break; | |
345 | } | |
346 | ||
347 | case DID_TIMING_2_DETAILED: | |
348 | { | |
349 | int i; | |
350 | ||
351 | for (i = 0; i < x[2]; i += 11) | |
352 | didDetailedTiming2(scrnIndex, x + i + 3, mon); | |
353 | break; | |
354 | } | |
355 | ||
356 | case DID_TIMING_3_SHORT: | |
357 | { | |
358 | int i; | |
359 | ||
360 | for (i = 0; i < x[2]; i += 3) | |
361 | didShortTiming(scrnIndex, x + i + 3, mon); | |
362 | break; | |
363 | } | |
364 | ||
365 | case DID_TIMING_4_DMT: | |
366 | { | |
367 | int i; | |
368 | ||
369 | for (i = 0; i < x[2]; i++) | |
370 | didDMTTiming(scrnIndex, x + i + 3, mon); | |
371 | break; | |
372 | } | |
373 | ||
374 | case DID_TIMING_VESA: | |
375 | didVesaTiming(scrnIndex, x, mon); | |
376 | break; | |
377 | ||
378 | /* XXX pixel format, ar, orientation, subpixel, dot pitch, bit depth */ | |
379 | case DID_DISPLAY_DEVICE: | |
380 | ||
381 | /* XXX interface, links, color encoding, ss, drm */ | |
382 | case DID_DISPLAY_INTERFACE: | |
383 | ||
384 | /* XXX stereo */ | |
385 | case DID_STEREO: | |
386 | ||
387 | /* nothing interesting in these */ | |
388 | case DID_COLOR_INFO: | |
389 | case DID_PRODUCT_SERIAL: | |
390 | case DID_ASCII_STRING: | |
391 | case DID_POWER_SEQUENCING: | |
392 | case DID_TRANSFER_INFO: | |
393 | case DID_VENDOR: | |
394 | break; | |
395 | ||
396 | /* warn about anything else */ | |
397 | default: | |
398 | xf86DrvMsg(scrnIndex, X_WARNING, | |
399 | "Unknown DisplayID block type %hx\n", x[0]); | |
400 | break; | |
401 | } | |
402 | } | |
403 | ||
404 | static void | |
405 | forEachDisplayIDBlock(int scrnIndex, unsigned char *did, did_proc proc, | |
406 | void *closure) | |
407 | { | |
408 | int num_extensions = did[3]; | |
409 | int section_size = did[1]; | |
410 | unsigned char *block; | |
411 | ||
412 | do { | |
413 | if ((did[0] & 0xf0) != 0x10) /* not 1.x, abort */ | |
414 | return; | |
415 | /* XXX also, checksum */ | |
416 | ||
417 | block = did + 4; | |
418 | ||
419 | while (section_size > 0) { | |
420 | int block_size = (block[2] + 2); | |
421 | ||
422 | proc(scrnIndex, block, closure); | |
423 | ||
424 | section_size -= block_size; | |
425 | block += block_size; | |
426 | } | |
427 | ||
428 | did += (did[1] + 5); | |
429 | } while (num_extensions--); | |
430 | } | |
431 | ||
432 | /* | |
433 | * Fill out MonPtr with xf86MonPtr information. | |
434 | */ | |
435 | void | |
436 | xf86DisplayIDMonitorSet(int scrnIndex, MonPtr mon, xf86MonPtr DDC) | |
437 | { | |
438 | if (!mon || !DDC) | |
439 | return; | |
440 | ||
441 | mon->DDC = DDC; | |
442 | ||
443 | forEachDisplayIDBlock(scrnIndex, DDC->rawData, handleDisplayIDBlock, mon); | |
444 | } |