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