]>
Commit | Line | Data |
---|---|---|
72d277a7 GH |
1 | /* |
2 | * QEMU EDID generator. | |
3 | * | |
4 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
5 | * See the COPYING file in the top-level directory. | |
6 | */ | |
7 | #include "qemu/osdep.h" | |
8 | #include "qemu-common.h" | |
9 | #include "qemu/bswap.h" | |
10 | #include "hw/display/edid.h" | |
11 | ||
12 | static const struct edid_mode { | |
13 | uint32_t xres; | |
14 | uint32_t yres; | |
15 | uint32_t byte; | |
16 | uint32_t xtra3; | |
17 | uint32_t bit; | |
18 | uint32_t dta; | |
19 | } modes[] = { | |
20 | /* dea/dta extension timings (all @ 50 Hz) */ | |
21 | { .xres = 5120, .yres = 2160, .dta = 125 }, | |
22 | { .xres = 4096, .yres = 2160, .dta = 101 }, | |
23 | { .xres = 3840, .yres = 2160, .dta = 96 }, | |
24 | { .xres = 2560, .yres = 1080, .dta = 89 }, | |
25 | { .xres = 2048, .yres = 1152 }, | |
26 | { .xres = 1920, .yres = 1080, .dta = 31 }, | |
27 | ||
28 | /* additional standard timings 3 (all @ 60Hz) */ | |
29 | { .xres = 1920, .yres = 1440, .xtra3 = 11, .bit = 5 }, | |
30 | { .xres = 1920, .yres = 1200, .xtra3 = 10, .bit = 0 }, | |
31 | { .xres = 1856, .yres = 1392, .xtra3 = 10, .bit = 3 }, | |
32 | { .xres = 1792, .yres = 1344, .xtra3 = 10, .bit = 5 }, | |
33 | { .xres = 1600, .yres = 1200, .xtra3 = 9, .bit = 2 }, | |
34 | { .xres = 1680, .yres = 1050, .xtra3 = 9, .bit = 5 }, | |
35 | { .xres = 1440, .yres = 1050, .xtra3 = 8, .bit = 1 }, | |
36 | { .xres = 1440, .yres = 900, .xtra3 = 8, .bit = 5 }, | |
37 | { .xres = 1360, .yres = 768, .xtra3 = 8, .bit = 7 }, | |
38 | { .xres = 1280, .yres = 1024, .xtra3 = 7, .bit = 1 }, | |
39 | { .xres = 1280, .yres = 960, .xtra3 = 7, .bit = 3 }, | |
40 | { .xres = 1280, .yres = 768, .xtra3 = 7, .bit = 6 }, | |
41 | ||
42 | /* established timings (all @ 60Hz) */ | |
43 | { .xres = 1024, .yres = 768, .byte = 36, .bit = 3 }, | |
44 | { .xres = 800, .yres = 600, .byte = 35, .bit = 0 }, | |
45 | { .xres = 640, .yres = 480, .byte = 35, .bit = 5 }, | |
46 | }; | |
47 | ||
48 | static void edid_ext_dta(uint8_t *dta) | |
49 | { | |
50 | dta[0] = 0x02; | |
51 | dta[1] = 0x03; | |
52 | dta[2] = 0x05; | |
53 | dta[3] = 0x00; | |
54 | ||
55 | /* video data block */ | |
56 | dta[4] = 0x40; | |
57 | } | |
58 | ||
59 | static void edid_ext_dta_mode(uint8_t *dta, uint8_t nr) | |
60 | { | |
61 | dta[dta[2]] = nr; | |
62 | dta[2]++; | |
63 | dta[4]++; | |
64 | } | |
65 | ||
66 | static int edid_std_mode(uint8_t *mode, uint32_t xres, uint32_t yres) | |
67 | { | |
68 | uint32_t aspect; | |
69 | ||
70 | if (xres == 0 || yres == 0) { | |
71 | mode[0] = 0x01; | |
72 | mode[1] = 0x01; | |
73 | return 0; | |
74 | ||
75 | } else if (xres * 10 == yres * 16) { | |
76 | aspect = 0; | |
77 | } else if (xres * 3 == yres * 4) { | |
78 | aspect = 1; | |
79 | } else if (xres * 4 == yres * 5) { | |
80 | aspect = 2; | |
81 | } else if (xres * 9 == yres * 16) { | |
82 | aspect = 3; | |
83 | } else { | |
84 | return -1; | |
85 | } | |
86 | ||
87 | if ((xres / 8) - 31 > 255) { | |
88 | return -1; | |
89 | } | |
90 | ||
91 | mode[0] = (xres / 8) - 31; | |
92 | mode[1] = ((aspect << 6) | (60 - 60)); | |
93 | return 0; | |
94 | } | |
95 | ||
96 | static void edid_fill_modes(uint8_t *edid, uint8_t *xtra3, uint8_t *dta, | |
97 | uint32_t maxx, uint32_t maxy) | |
98 | { | |
99 | const struct edid_mode *mode; | |
100 | int std = 38; | |
101 | int rc, i; | |
102 | ||
103 | for (i = 0; i < ARRAY_SIZE(modes); i++) { | |
104 | mode = modes + i; | |
105 | ||
106 | if ((maxx && mode->xres > maxx) || | |
107 | (maxy && mode->yres > maxy)) { | |
108 | continue; | |
109 | } | |
110 | ||
111 | if (mode->byte) { | |
112 | edid[mode->byte] |= (1 << mode->bit); | |
113 | } else if (mode->xtra3 && xtra3) { | |
114 | xtra3[mode->xtra3] |= (1 << mode->bit); | |
115 | } else if (std < 54) { | |
116 | rc = edid_std_mode(edid + std, mode->xres, mode->yres); | |
117 | if (rc == 0) { | |
118 | std += 2; | |
119 | } | |
120 | } | |
121 | ||
122 | if (dta && mode->dta) { | |
123 | edid_ext_dta_mode(dta, mode->dta); | |
124 | } | |
125 | } | |
126 | ||
127 | while (std < 54) { | |
128 | edid_std_mode(edid + std, 0, 0); | |
129 | std += 2; | |
130 | } | |
131 | } | |
132 | ||
133 | static void edid_checksum(uint8_t *edid) | |
134 | { | |
135 | uint32_t sum = 0; | |
136 | int i; | |
137 | ||
138 | for (i = 0; i < 127; i++) { | |
139 | sum += edid[i]; | |
140 | } | |
141 | sum &= 0xff; | |
142 | if (sum) { | |
143 | edid[127] = 0x100 - sum; | |
144 | } | |
145 | } | |
146 | ||
147 | static void edid_desc_type(uint8_t *desc, uint8_t type) | |
148 | { | |
149 | desc[0] = 0; | |
150 | desc[1] = 0; | |
151 | desc[2] = 0; | |
152 | desc[3] = type; | |
153 | desc[4] = 0; | |
154 | } | |
155 | ||
156 | static void edid_desc_text(uint8_t *desc, uint8_t type, | |
157 | const char *text) | |
158 | { | |
159 | size_t len; | |
160 | ||
161 | edid_desc_type(desc, type); | |
162 | memset(desc + 5, ' ', 13); | |
163 | ||
164 | len = strlen(text); | |
165 | if (len > 12) { | |
166 | len = 12; | |
167 | } | |
168 | strncpy((char *)(desc + 5), text, len); | |
169 | desc[5 + len] = '\n'; | |
170 | } | |
171 | ||
172 | static void edid_desc_ranges(uint8_t *desc) | |
173 | { | |
174 | edid_desc_type(desc, 0xfd); | |
175 | ||
176 | /* vertical (50 -> 125 Hz) */ | |
177 | desc[5] = 50; | |
178 | desc[6] = 125; | |
179 | ||
180 | /* horizontal (30 -> 160 kHz) */ | |
181 | desc[7] = 30; | |
182 | desc[8] = 160; | |
183 | ||
184 | /* max dot clock (1200 MHz) */ | |
185 | desc[9] = 1200 / 10; | |
186 | ||
187 | /* no extended timing information */ | |
188 | desc[10] = 0x01; | |
189 | ||
190 | /* padding */ | |
191 | desc[11] = '\n'; | |
192 | memset(desc + 12, ' ', 6); | |
193 | } | |
194 | ||
195 | /* additional standard timings 3 */ | |
196 | static void edid_desc_xtra3_std(uint8_t *desc) | |
197 | { | |
198 | edid_desc_type(desc, 0xf7); | |
199 | desc[5] = 10; | |
200 | } | |
201 | ||
202 | static void edid_desc_dummy(uint8_t *desc) | |
203 | { | |
204 | edid_desc_type(desc, 0x10); | |
205 | } | |
206 | ||
207 | static void edid_desc_timing(uint8_t *desc, | |
208 | uint32_t xres, uint32_t yres, | |
209 | uint32_t dpi) | |
210 | { | |
211 | /* physical display size */ | |
212 | uint32_t xmm = xres * dpi / 254; | |
213 | uint32_t ymm = yres * dpi / 254; | |
214 | ||
215 | /* pull some realistic looking timings out of thin air */ | |
216 | uint32_t xfront = xres * 25 / 100; | |
217 | uint32_t xsync = xres * 3 / 100; | |
218 | uint32_t xblank = xres * 35 / 100; | |
219 | ||
220 | uint32_t yfront = yres * 5 / 1000; | |
221 | uint32_t ysync = yres * 5 / 1000; | |
222 | uint32_t yblank = yres * 35 / 1000; | |
223 | ||
224 | uint32_t clock = 75 * (xres + xblank) * (yres + yblank); | |
225 | ||
2e4a0b17 | 226 | stl_le_p(desc, clock / 10000); |
72d277a7 GH |
227 | |
228 | desc[2] = xres & 0xff; | |
229 | desc[3] = xblank & 0xff; | |
230 | desc[4] = (((xres & 0xf00) >> 4) | | |
231 | ((xblank & 0xf00) >> 8)); | |
232 | ||
233 | desc[5] = yres & 0xff; | |
234 | desc[6] = yblank & 0xff; | |
235 | desc[7] = (((yres & 0xf00) >> 4) | | |
236 | ((yblank & 0xf00) >> 8)); | |
237 | ||
238 | desc[8] = xfront & 0xff; | |
239 | desc[9] = xsync & 0xff; | |
240 | ||
241 | desc[10] = (((yfront & 0x00f) << 4) | | |
242 | ((ysync & 0x00f) << 0)); | |
243 | desc[11] = (((xfront & 0x300) >> 2) | | |
244 | ((xsync & 0x300) >> 4) | | |
245 | ((yfront & 0x030) >> 2) | | |
246 | ((ysync & 0x030) >> 4)); | |
247 | ||
248 | desc[12] = xmm & 0xff; | |
249 | desc[13] = ymm & 0xff; | |
250 | desc[14] = (((xmm & 0xf00) >> 4) | | |
251 | ((ymm & 0xf00) >> 8)); | |
252 | ||
253 | desc[17] = 0x18; | |
254 | } | |
255 | ||
256 | static uint32_t edid_to_10bit(float value) | |
257 | { | |
258 | return (uint32_t)(value * 1024 + 0.5); | |
259 | } | |
260 | ||
261 | static void edid_colorspace(uint8_t *edid, | |
262 | float rx, float ry, | |
263 | float gx, float gy, | |
264 | float bx, float by, | |
265 | float wx, float wy) | |
266 | { | |
267 | uint32_t red_x = edid_to_10bit(rx); | |
268 | uint32_t red_y = edid_to_10bit(ry); | |
269 | uint32_t green_x = edid_to_10bit(gx); | |
270 | uint32_t green_y = edid_to_10bit(gy); | |
271 | uint32_t blue_x = edid_to_10bit(bx); | |
272 | uint32_t blue_y = edid_to_10bit(by); | |
273 | uint32_t white_x = edid_to_10bit(wx); | |
274 | uint32_t white_y = edid_to_10bit(wy); | |
275 | ||
276 | edid[25] = (((red_x & 0x03) << 6) | | |
277 | ((red_y & 0x03) << 4) | | |
278 | ((green_x & 0x03) << 2) | | |
279 | ((green_y & 0x03) << 0)); | |
280 | edid[26] = (((blue_x & 0x03) << 6) | | |
281 | ((blue_y & 0x03) << 4) | | |
282 | ((white_x & 0x03) << 2) | | |
283 | ((white_y & 0x03) << 0)); | |
284 | edid[27] = red_x >> 2; | |
285 | edid[28] = red_y >> 2; | |
286 | edid[29] = green_x >> 2; | |
287 | edid[30] = green_y >> 2; | |
288 | edid[31] = blue_x >> 2; | |
289 | edid[32] = blue_y >> 2; | |
290 | edid[33] = white_x >> 2; | |
291 | edid[34] = white_y >> 2; | |
292 | } | |
293 | ||
294 | void qemu_edid_generate(uint8_t *edid, size_t size, | |
295 | qemu_edid_info *info) | |
296 | { | |
297 | uint32_t desc = 54; | |
298 | uint8_t *xtra3 = NULL; | |
299 | uint8_t *dta = NULL; | |
300 | ||
301 | /* =============== set defaults =============== */ | |
302 | ||
303 | if (!info->vendor || strlen(info->vendor) != 3) { | |
edbc4b24 | 304 | info->vendor = "RHT"; |
72d277a7 GH |
305 | } |
306 | if (!info->name) { | |
307 | info->name = "QEMU Monitor"; | |
308 | } | |
309 | if (!info->dpi) { | |
310 | info->dpi = 100; | |
311 | } | |
312 | if (!info->prefx) { | |
313 | info->prefx = 1024; | |
314 | } | |
315 | if (!info->prefy) { | |
316 | info->prefy = 768; | |
317 | } | |
318 | ||
319 | /* =============== extensions =============== */ | |
320 | ||
321 | if (size >= 256) { | |
322 | dta = edid + 128; | |
323 | edid[126]++; | |
324 | edid_ext_dta(dta); | |
325 | } | |
326 | ||
327 | /* =============== header information =============== */ | |
328 | ||
329 | /* fixed */ | |
330 | edid[0] = 0x00; | |
331 | edid[1] = 0xff; | |
332 | edid[2] = 0xff; | |
333 | edid[3] = 0xff; | |
334 | edid[4] = 0xff; | |
335 | edid[5] = 0xff; | |
336 | edid[6] = 0xff; | |
337 | edid[7] = 0x00; | |
338 | ||
339 | /* manufacturer id, product code, serial number */ | |
340 | uint16_t vendor_id = ((((info->vendor[0] - '@') & 0x1f) << 10) | | |
341 | (((info->vendor[1] - '@') & 0x1f) << 5) | | |
342 | (((info->vendor[2] - '@') & 0x1f) << 0)); | |
343 | uint16_t model_nr = 0x1234; | |
344 | uint32_t serial_nr = info->serial ? atoi(info->serial) : 0; | |
2e4a0b17 GH |
345 | stw_be_p(edid + 8, vendor_id); |
346 | stw_le_p(edid + 10, model_nr); | |
347 | stl_le_p(edid + 12, serial_nr); | |
72d277a7 GH |
348 | |
349 | /* manufacture week and year */ | |
350 | edid[16] = 42; | |
351 | edid[17] = 2014 - 1990; | |
352 | ||
353 | /* edid version */ | |
354 | edid[18] = 1; | |
355 | edid[19] = 4; | |
356 | ||
357 | ||
358 | /* =============== basic display parameters =============== */ | |
359 | ||
360 | /* video input: digital, 8bpc, displayport */ | |
361 | edid[20] = 0xa5; | |
362 | ||
363 | /* screen size: undefined */ | |
364 | edid[21] = info->prefx * info->dpi / 2540; | |
365 | edid[22] = info->prefy * info->dpi / 2540; | |
366 | ||
367 | /* display gamma: 2.2 */ | |
368 | edid[23] = 220 - 100; | |
369 | ||
370 | /* supported features bitmap: std sRGB, preferred timing */ | |
371 | edid[24] = 0x06; | |
372 | ||
373 | ||
374 | /* =============== chromaticity coordinates =============== */ | |
375 | ||
376 | /* standard sRGB colorspace */ | |
377 | edid_colorspace(edid, | |
378 | 0.6400, 0.3300, /* red */ | |
379 | 0.3000, 0.6000, /* green */ | |
380 | 0.1500, 0.0600, /* blue */ | |
381 | 0.3127, 0.3290); /* white point */ | |
382 | ||
383 | /* =============== established timing bitmap =============== */ | |
384 | /* =============== standard timing information =============== */ | |
385 | ||
386 | /* both filled by edid_fill_modes() */ | |
387 | ||
388 | ||
389 | /* =============== descriptor blocks =============== */ | |
390 | ||
391 | edid_desc_timing(edid + desc, info->prefx, info->prefy, info->dpi); | |
392 | desc += 18; | |
393 | ||
394 | edid_desc_ranges(edid + desc); | |
395 | desc += 18; | |
396 | ||
397 | if (info->name) { | |
398 | edid_desc_text(edid + desc, 0xfc, info->name); | |
399 | desc += 18; | |
400 | } | |
401 | ||
402 | if (info->serial) { | |
403 | edid_desc_text(edid + desc, 0xff, info->serial); | |
404 | desc += 18; | |
405 | } | |
406 | ||
407 | if (desc < 126) { | |
408 | xtra3 = edid + desc; | |
409 | edid_desc_xtra3_std(xtra3); | |
410 | desc += 18; | |
411 | } | |
412 | ||
413 | while (desc < 126) { | |
414 | edid_desc_dummy(edid + desc); | |
415 | desc += 18; | |
416 | } | |
417 | ||
418 | /* =============== finish up =============== */ | |
419 | ||
420 | edid_fill_modes(edid, xtra3, dta, info->maxx, info->maxy); | |
421 | edid_checksum(edid); | |
422 | if (dta) { | |
423 | edid_checksum(dta); | |
424 | } | |
425 | } | |
e7992fc5 GH |
426 | |
427 | size_t qemu_edid_size(uint8_t *edid) | |
428 | { | |
429 | uint32_t exts; | |
430 | ||
431 | if (edid[0] != 0x00 || | |
432 | edid[1] != 0xff) { | |
433 | /* doesn't look like a valid edid block */ | |
434 | return 0; | |
435 | } | |
436 | ||
437 | exts = edid[126]; | |
438 | return 128 * (exts + 1); | |
439 | } |