Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / xfree86 / modes / xf86DisplayIDModes.c
CommitLineData
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
33typedef 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
57static DisplayModePtr
58modeCalloc(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
68static void
69didDetailedTiming1(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? */
102static void
103didDetailedTiming2(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
134static void
135didShortTiming(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
174static void
175didDMTTiming(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
184static 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
279static void
280didVesaTiming(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
300static void
301handleDisplayIDBlock(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
404static void
405forEachDisplayIDBlock(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 */
435void
436xf86DisplayIDMonitorSet(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}