Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 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" |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 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 | |
Satyeshwar Singh | f0602b7 | 2021-11-16 14:11:03 -0800 | [diff] [blame] | 27 | /* dea/dta extension timings (all @ 60 Hz) */ |
| 28 | { .xres = 3840, .yres = 2160, .dta = 97 }, |
| 29 | |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 30 | /* additional standard timings 3 (all @ 60Hz) */ |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 31 | { .xres = 1920, .yres = 1200, .xtra3 = 10, .bit = 0 }, |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 32 | { .xres = 1600, .yres = 1200, .xtra3 = 9, .bit = 2 }, |
| 33 | { .xres = 1680, .yres = 1050, .xtra3 = 9, .bit = 5 }, |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 34 | { .xres = 1440, .yres = 900, .xtra3 = 8, .bit = 5 }, |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 35 | { .xres = 1280, .yres = 1024, .xtra3 = 7, .bit = 1 }, |
| 36 | { .xres = 1280, .yres = 960, .xtra3 = 7, .bit = 3 }, |
| 37 | { .xres = 1280, .yres = 768, .xtra3 = 7, .bit = 6 }, |
| 38 | |
Gerd Hoffmann | 40c5030 | 2021-03-16 15:38:08 +0100 | [diff] [blame] | 39 | { .xres = 1920, .yres = 1440, .xtra3 = 11, .bit = 5 }, |
| 40 | { .xres = 1856, .yres = 1392, .xtra3 = 10, .bit = 3 }, |
| 41 | { .xres = 1792, .yres = 1344, .xtra3 = 10, .bit = 5 }, |
| 42 | { .xres = 1440, .yres = 1050, .xtra3 = 8, .bit = 1 }, |
| 43 | { .xres = 1360, .yres = 768, .xtra3 = 8, .bit = 7 }, |
| 44 | |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 45 | /* established timings (all @ 60Hz) */ |
| 46 | { .xres = 1024, .yres = 768, .byte = 36, .bit = 3 }, |
| 47 | { .xres = 800, .yres = 600, .byte = 35, .bit = 0 }, |
| 48 | { .xres = 640, .yres = 480, .byte = 35, .bit = 5 }, |
| 49 | }; |
| 50 | |
Konstantin Nazarov | 850dc61 | 2021-04-27 17:08:22 +0200 | [diff] [blame] | 51 | typedef struct Timings { |
| 52 | uint32_t xfront; |
| 53 | uint32_t xsync; |
| 54 | uint32_t xblank; |
| 55 | |
| 56 | uint32_t yfront; |
| 57 | uint32_t ysync; |
| 58 | uint32_t yblank; |
| 59 | |
| 60 | uint64_t clock; |
| 61 | } Timings; |
| 62 | |
| 63 | static void generate_timings(Timings *timings, uint32_t refresh_rate, |
| 64 | uint32_t xres, uint32_t yres) |
| 65 | { |
| 66 | /* pull some realistic looking timings out of thin air */ |
| 67 | timings->xfront = xres * 25 / 100; |
| 68 | timings->xsync = xres * 3 / 100; |
| 69 | timings->xblank = xres * 35 / 100; |
| 70 | |
| 71 | timings->yfront = yres * 5 / 1000; |
| 72 | timings->ysync = yres * 5 / 1000; |
| 73 | timings->yblank = yres * 35 / 1000; |
| 74 | |
| 75 | timings->clock = ((uint64_t)refresh_rate * |
| 76 | (xres + timings->xblank) * |
| 77 | (yres + timings->yblank)) / 10000000; |
| 78 | } |
| 79 | |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 80 | static void edid_ext_dta(uint8_t *dta) |
| 81 | { |
| 82 | dta[0] = 0x02; |
| 83 | dta[1] = 0x03; |
| 84 | dta[2] = 0x05; |
| 85 | dta[3] = 0x00; |
| 86 | |
| 87 | /* video data block */ |
| 88 | dta[4] = 0x40; |
| 89 | } |
| 90 | |
| 91 | static void edid_ext_dta_mode(uint8_t *dta, uint8_t nr) |
| 92 | { |
| 93 | dta[dta[2]] = nr; |
| 94 | dta[2]++; |
| 95 | dta[4]++; |
| 96 | } |
| 97 | |
| 98 | static int edid_std_mode(uint8_t *mode, uint32_t xres, uint32_t yres) |
| 99 | { |
| 100 | uint32_t aspect; |
| 101 | |
| 102 | if (xres == 0 || yres == 0) { |
| 103 | mode[0] = 0x01; |
| 104 | mode[1] = 0x01; |
| 105 | return 0; |
| 106 | |
| 107 | } else if (xres * 10 == yres * 16) { |
| 108 | aspect = 0; |
| 109 | } else if (xres * 3 == yres * 4) { |
| 110 | aspect = 1; |
| 111 | } else if (xres * 4 == yres * 5) { |
| 112 | aspect = 2; |
| 113 | } else if (xres * 9 == yres * 16) { |
| 114 | aspect = 3; |
| 115 | } else { |
| 116 | return -1; |
| 117 | } |
| 118 | |
| 119 | if ((xres / 8) - 31 > 255) { |
| 120 | return -1; |
| 121 | } |
| 122 | |
| 123 | mode[0] = (xres / 8) - 31; |
| 124 | mode[1] = ((aspect << 6) | (60 - 60)); |
| 125 | return 0; |
| 126 | } |
| 127 | |
| 128 | static void edid_fill_modes(uint8_t *edid, uint8_t *xtra3, uint8_t *dta, |
| 129 | uint32_t maxx, uint32_t maxy) |
| 130 | { |
| 131 | const struct edid_mode *mode; |
| 132 | int std = 38; |
| 133 | int rc, i; |
| 134 | |
| 135 | for (i = 0; i < ARRAY_SIZE(modes); i++) { |
| 136 | mode = modes + i; |
| 137 | |
| 138 | if ((maxx && mode->xres > maxx) || |
| 139 | (maxy && mode->yres > maxy)) { |
| 140 | continue; |
| 141 | } |
| 142 | |
| 143 | if (mode->byte) { |
| 144 | edid[mode->byte] |= (1 << mode->bit); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 145 | } else if (std < 54) { |
| 146 | rc = edid_std_mode(edid + std, mode->xres, mode->yres); |
| 147 | if (rc == 0) { |
| 148 | std += 2; |
| 149 | } |
Gerd Hoffmann | 40c5030 | 2021-03-16 15:38:08 +0100 | [diff] [blame] | 150 | } else if (mode->xtra3 && xtra3) { |
| 151 | xtra3[mode->xtra3] |= (1 << mode->bit); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 152 | } |
| 153 | |
| 154 | if (dta && mode->dta) { |
| 155 | edid_ext_dta_mode(dta, mode->dta); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | while (std < 54) { |
| 160 | edid_std_mode(edid + std, 0, 0); |
| 161 | std += 2; |
| 162 | } |
| 163 | } |
| 164 | |
Konstantin Nazarov | 5a4e88c | 2021-04-27 17:08:23 +0200 | [diff] [blame] | 165 | static void edid_checksum(uint8_t *edid, size_t len) |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 166 | { |
| 167 | uint32_t sum = 0; |
| 168 | int i; |
| 169 | |
Konstantin Nazarov | 5a4e88c | 2021-04-27 17:08:23 +0200 | [diff] [blame] | 170 | for (i = 0; i < len; i++) { |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 171 | sum += edid[i]; |
| 172 | } |
| 173 | sum &= 0xff; |
| 174 | if (sum) { |
Konstantin Nazarov | 5a4e88c | 2021-04-27 17:08:23 +0200 | [diff] [blame] | 175 | edid[len] = 0x100 - sum; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 176 | } |
| 177 | } |
| 178 | |
Gerd Hoffmann | ed7f17a | 2021-04-27 17:08:18 +0200 | [diff] [blame] | 179 | static uint8_t *edid_desc_next(uint8_t *edid, uint8_t *dta, uint8_t *desc) |
| 180 | { |
| 181 | if (desc == NULL) { |
| 182 | return NULL; |
| 183 | } |
| 184 | if (desc + 18 + 18 < edid + 127) { |
| 185 | return desc + 18; |
| 186 | } |
Gerd Hoffmann | 4f9e268 | 2021-04-27 17:08:20 +0200 | [diff] [blame] | 187 | if (dta) { |
| 188 | if (desc < edid + 127) { |
| 189 | return dta + dta[2]; |
| 190 | } |
| 191 | if (desc + 18 + 18 < dta + 127) { |
| 192 | return desc + 18; |
| 193 | } |
| 194 | } |
Gerd Hoffmann | ed7f17a | 2021-04-27 17:08:18 +0200 | [diff] [blame] | 195 | return NULL; |
| 196 | } |
| 197 | |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 198 | static void edid_desc_type(uint8_t *desc, uint8_t type) |
| 199 | { |
| 200 | desc[0] = 0; |
| 201 | desc[1] = 0; |
| 202 | desc[2] = 0; |
| 203 | desc[3] = type; |
| 204 | desc[4] = 0; |
| 205 | } |
| 206 | |
| 207 | static void edid_desc_text(uint8_t *desc, uint8_t type, |
| 208 | const char *text) |
| 209 | { |
| 210 | size_t len; |
| 211 | |
| 212 | edid_desc_type(desc, type); |
| 213 | memset(desc + 5, ' ', 13); |
| 214 | |
| 215 | len = strlen(text); |
| 216 | if (len > 12) { |
| 217 | len = 12; |
| 218 | } |
Marc-André Lureau | 627c865 | 2018-11-10 15:16:23 +0400 | [diff] [blame] | 219 | memcpy(desc + 5, text, len); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 220 | desc[5 + len] = '\n'; |
| 221 | } |
| 222 | |
| 223 | static void edid_desc_ranges(uint8_t *desc) |
| 224 | { |
| 225 | edid_desc_type(desc, 0xfd); |
| 226 | |
| 227 | /* vertical (50 -> 125 Hz) */ |
| 228 | desc[5] = 50; |
| 229 | desc[6] = 125; |
| 230 | |
| 231 | /* horizontal (30 -> 160 kHz) */ |
| 232 | desc[7] = 30; |
| 233 | desc[8] = 160; |
| 234 | |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 235 | /* max dot clock (2550 MHz) */ |
| 236 | desc[9] = 2550 / 10; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 237 | |
| 238 | /* no extended timing information */ |
| 239 | desc[10] = 0x01; |
| 240 | |
| 241 | /* padding */ |
| 242 | desc[11] = '\n'; |
| 243 | memset(desc + 12, ' ', 6); |
| 244 | } |
| 245 | |
| 246 | /* additional standard timings 3 */ |
| 247 | static void edid_desc_xtra3_std(uint8_t *desc) |
| 248 | { |
| 249 | edid_desc_type(desc, 0xf7); |
| 250 | desc[5] = 10; |
| 251 | } |
| 252 | |
| 253 | static void edid_desc_dummy(uint8_t *desc) |
| 254 | { |
| 255 | edid_desc_type(desc, 0x10); |
| 256 | } |
| 257 | |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 258 | static void edid_desc_timing(uint8_t *desc, const Timings *timings, |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 259 | uint32_t xres, uint32_t yres, |
Marc-André Lureau | fd36ead | 2020-09-27 18:57:47 +0400 | [diff] [blame] | 260 | uint32_t xmm, uint32_t ymm) |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 261 | { |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 262 | stw_le_p(desc, timings->clock); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 263 | |
| 264 | desc[2] = xres & 0xff; |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 265 | desc[3] = timings->xblank & 0xff; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 266 | desc[4] = (((xres & 0xf00) >> 4) | |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 267 | ((timings->xblank & 0xf00) >> 8)); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 268 | |
| 269 | desc[5] = yres & 0xff; |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 270 | desc[6] = timings->yblank & 0xff; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 271 | desc[7] = (((yres & 0xf00) >> 4) | |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 272 | ((timings->yblank & 0xf00) >> 8)); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 273 | |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 274 | desc[8] = timings->xfront & 0xff; |
| 275 | desc[9] = timings->xsync & 0xff; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 276 | |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 277 | desc[10] = (((timings->yfront & 0x00f) << 4) | |
| 278 | ((timings->ysync & 0x00f) << 0)); |
| 279 | desc[11] = (((timings->xfront & 0x300) >> 2) | |
| 280 | ((timings->xsync & 0x300) >> 4) | |
| 281 | ((timings->yfront & 0x030) >> 2) | |
| 282 | ((timings->ysync & 0x030) >> 4)); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 283 | |
| 284 | desc[12] = xmm & 0xff; |
| 285 | desc[13] = ymm & 0xff; |
| 286 | desc[14] = (((xmm & 0xf00) >> 4) | |
| 287 | ((ymm & 0xf00) >> 8)); |
| 288 | |
| 289 | desc[17] = 0x18; |
| 290 | } |
| 291 | |
| 292 | static uint32_t edid_to_10bit(float value) |
| 293 | { |
| 294 | return (uint32_t)(value * 1024 + 0.5); |
| 295 | } |
| 296 | |
| 297 | static void edid_colorspace(uint8_t *edid, |
| 298 | float rx, float ry, |
| 299 | float gx, float gy, |
| 300 | float bx, float by, |
| 301 | float wx, float wy) |
| 302 | { |
| 303 | uint32_t red_x = edid_to_10bit(rx); |
| 304 | uint32_t red_y = edid_to_10bit(ry); |
| 305 | uint32_t green_x = edid_to_10bit(gx); |
| 306 | uint32_t green_y = edid_to_10bit(gy); |
| 307 | uint32_t blue_x = edid_to_10bit(bx); |
| 308 | uint32_t blue_y = edid_to_10bit(by); |
| 309 | uint32_t white_x = edid_to_10bit(wx); |
| 310 | uint32_t white_y = edid_to_10bit(wy); |
| 311 | |
| 312 | edid[25] = (((red_x & 0x03) << 6) | |
| 313 | ((red_y & 0x03) << 4) | |
| 314 | ((green_x & 0x03) << 2) | |
| 315 | ((green_y & 0x03) << 0)); |
| 316 | edid[26] = (((blue_x & 0x03) << 6) | |
| 317 | ((blue_y & 0x03) << 4) | |
| 318 | ((white_x & 0x03) << 2) | |
| 319 | ((white_y & 0x03) << 0)); |
| 320 | edid[27] = red_x >> 2; |
| 321 | edid[28] = red_y >> 2; |
| 322 | edid[29] = green_x >> 2; |
| 323 | edid[30] = green_y >> 2; |
| 324 | edid[31] = blue_x >> 2; |
| 325 | edid[32] = blue_y >> 2; |
| 326 | edid[33] = white_x >> 2; |
| 327 | edid[34] = white_y >> 2; |
| 328 | } |
| 329 | |
Marc-André Lureau | fd36ead | 2020-09-27 18:57:47 +0400 | [diff] [blame] | 330 | static uint32_t qemu_edid_dpi_from_mm(uint32_t mm, uint32_t res) |
| 331 | { |
| 332 | return res * 254 / 10 / mm; |
| 333 | } |
| 334 | |
| 335 | uint32_t qemu_edid_dpi_to_mm(uint32_t dpi, uint32_t res) |
| 336 | { |
| 337 | return res * 254 / 10 / dpi; |
| 338 | } |
| 339 | |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 340 | static void init_displayid(uint8_t *did) |
| 341 | { |
| 342 | did[0] = 0x70; /* display id extension */ |
| 343 | did[1] = 0x13; /* version 1.3 */ |
| 344 | did[2] = 4; /* length */ |
| 345 | did[3] = 0x03; /* product type (0x03 == standalone display device) */ |
| 346 | edid_checksum(did + 1, did[2] + 4); |
| 347 | } |
| 348 | |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 349 | static void qemu_displayid_generate(uint8_t *did, const Timings *timings, |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 350 | uint32_t xres, uint32_t yres, |
| 351 | uint32_t xmm, uint32_t ymm) |
| 352 | { |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 353 | did[0] = 0x70; /* display id extension */ |
| 354 | did[1] = 0x13; /* version 1.3 */ |
| 355 | did[2] = 23; /* length */ |
| 356 | did[3] = 0x03; /* product type (0x03 == standalone display device) */ |
| 357 | |
| 358 | did[5] = 0x03; /* Detailed Timings Data Block */ |
| 359 | did[6] = 0x00; /* revision */ |
| 360 | did[7] = 0x14; /* block length */ |
| 361 | |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 362 | did[8] = timings->clock & 0xff; |
| 363 | did[9] = (timings->clock & 0xff00) >> 8; |
| 364 | did[10] = (timings->clock & 0xff0000) >> 16; |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 365 | |
| 366 | did[11] = 0x88; /* leave aspect ratio undefined */ |
| 367 | |
| 368 | stw_le_p(did + 12, 0xffff & (xres - 1)); |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 369 | stw_le_p(did + 14, 0xffff & (timings->xblank - 1)); |
| 370 | stw_le_p(did + 16, 0xffff & (timings->xfront - 1)); |
| 371 | stw_le_p(did + 18, 0xffff & (timings->xsync - 1)); |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 372 | |
| 373 | stw_le_p(did + 20, 0xffff & (yres - 1)); |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 374 | stw_le_p(did + 22, 0xffff & (timings->yblank - 1)); |
| 375 | stw_le_p(did + 24, 0xffff & (timings->yfront - 1)); |
| 376 | stw_le_p(did + 26, 0xffff & (timings->ysync - 1)); |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 377 | |
| 378 | edid_checksum(did + 1, did[2] + 4); |
| 379 | } |
| 380 | |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 381 | void qemu_edid_generate(uint8_t *edid, size_t size, |
| 382 | qemu_edid_info *info) |
| 383 | { |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 384 | Timings timings; |
Gerd Hoffmann | ed7f17a | 2021-04-27 17:08:18 +0200 | [diff] [blame] | 385 | uint8_t *desc = edid + 54; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 386 | uint8_t *xtra3 = NULL; |
| 387 | uint8_t *dta = NULL; |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 388 | uint8_t *did = NULL; |
Marc-André Lureau | fd36ead | 2020-09-27 18:57:47 +0400 | [diff] [blame] | 389 | uint32_t width_mm, height_mm; |
Akihiko Odaki | fce39fa | 2021-04-27 17:08:21 +0200 | [diff] [blame] | 390 | uint32_t refresh_rate = info->refresh_rate ? info->refresh_rate : 75000; |
Marc-André Lureau | fd36ead | 2020-09-27 18:57:47 +0400 | [diff] [blame] | 391 | uint32_t dpi = 100; /* if no width_mm/height_mm */ |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 392 | uint32_t large_screen = 0; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 393 | |
| 394 | /* =============== set defaults =============== */ |
| 395 | |
| 396 | if (!info->vendor || strlen(info->vendor) != 3) { |
Gerd Hoffmann | edbc4b2 | 2018-10-05 11:19:34 +0200 | [diff] [blame] | 397 | info->vendor = "RHT"; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 398 | } |
| 399 | if (!info->name) { |
| 400 | info->name = "QEMU Monitor"; |
| 401 | } |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 402 | if (!info->prefx) { |
Daniel P. Berrangé | de72c4b | 2021-11-29 14:05:08 +0000 | [diff] [blame] | 403 | info->prefx = 1280; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 404 | } |
| 405 | if (!info->prefy) { |
Daniel P. Berrangé | de72c4b | 2021-11-29 14:05:08 +0000 | [diff] [blame] | 406 | info->prefy = 800; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 407 | } |
Marc-André Lureau | fd36ead | 2020-09-27 18:57:47 +0400 | [diff] [blame] | 408 | if (info->width_mm && info->height_mm) { |
| 409 | width_mm = info->width_mm; |
| 410 | height_mm = info->height_mm; |
| 411 | dpi = qemu_edid_dpi_from_mm(width_mm, info->prefx); |
| 412 | } else { |
| 413 | width_mm = qemu_edid_dpi_to_mm(dpi, info->prefx); |
| 414 | height_mm = qemu_edid_dpi_to_mm(dpi, info->prefy); |
| 415 | } |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 416 | |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 417 | generate_timings(&timings, refresh_rate, info->prefx, info->prefy); |
| 418 | if (info->prefx >= 4096 || info->prefy >= 4096 || timings.clock >= 65536) { |
| 419 | large_screen = 1; |
| 420 | } |
| 421 | |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 422 | /* =============== extensions =============== */ |
| 423 | |
| 424 | if (size >= 256) { |
| 425 | dta = edid + 128; |
| 426 | edid[126]++; |
| 427 | edid_ext_dta(dta); |
| 428 | } |
| 429 | |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 430 | if (size >= 384 && large_screen) { |
| 431 | did = edid + 256; |
| 432 | edid[126]++; |
| 433 | init_displayid(did); |
| 434 | } |
| 435 | |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 436 | /* =============== header information =============== */ |
| 437 | |
| 438 | /* fixed */ |
| 439 | edid[0] = 0x00; |
| 440 | edid[1] = 0xff; |
| 441 | edid[2] = 0xff; |
| 442 | edid[3] = 0xff; |
| 443 | edid[4] = 0xff; |
| 444 | edid[5] = 0xff; |
| 445 | edid[6] = 0xff; |
| 446 | edid[7] = 0x00; |
| 447 | |
| 448 | /* manufacturer id, product code, serial number */ |
| 449 | uint16_t vendor_id = ((((info->vendor[0] - '@') & 0x1f) << 10) | |
| 450 | (((info->vendor[1] - '@') & 0x1f) << 5) | |
| 451 | (((info->vendor[2] - '@') & 0x1f) << 0)); |
| 452 | uint16_t model_nr = 0x1234; |
| 453 | uint32_t serial_nr = info->serial ? atoi(info->serial) : 0; |
Gerd Hoffmann | 2e4a0b1 | 2018-10-15 07:53:33 +0200 | [diff] [blame] | 454 | stw_be_p(edid + 8, vendor_id); |
| 455 | stw_le_p(edid + 10, model_nr); |
| 456 | stl_le_p(edid + 12, serial_nr); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 457 | |
| 458 | /* manufacture week and year */ |
| 459 | edid[16] = 42; |
| 460 | edid[17] = 2014 - 1990; |
| 461 | |
| 462 | /* edid version */ |
| 463 | edid[18] = 1; |
| 464 | edid[19] = 4; |
| 465 | |
| 466 | |
| 467 | /* =============== basic display parameters =============== */ |
| 468 | |
| 469 | /* video input: digital, 8bpc, displayport */ |
| 470 | edid[20] = 0xa5; |
| 471 | |
| 472 | /* screen size: undefined */ |
Marc-André Lureau | fd36ead | 2020-09-27 18:57:47 +0400 | [diff] [blame] | 473 | edid[21] = width_mm / 10; |
| 474 | edid[22] = height_mm / 10; |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 475 | |
| 476 | /* display gamma: 2.2 */ |
| 477 | edid[23] = 220 - 100; |
| 478 | |
| 479 | /* supported features bitmap: std sRGB, preferred timing */ |
| 480 | edid[24] = 0x06; |
| 481 | |
| 482 | |
| 483 | /* =============== chromaticity coordinates =============== */ |
| 484 | |
| 485 | /* standard sRGB colorspace */ |
| 486 | edid_colorspace(edid, |
| 487 | 0.6400, 0.3300, /* red */ |
| 488 | 0.3000, 0.6000, /* green */ |
| 489 | 0.1500, 0.0600, /* blue */ |
| 490 | 0.3127, 0.3290); /* white point */ |
| 491 | |
| 492 | /* =============== established timing bitmap =============== */ |
| 493 | /* =============== standard timing information =============== */ |
| 494 | |
| 495 | /* both filled by edid_fill_modes() */ |
| 496 | |
| 497 | |
| 498 | /* =============== descriptor blocks =============== */ |
| 499 | |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 500 | if (!large_screen) { |
| 501 | /* The DTD section has only 12 bits to store the resolution */ |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 502 | edid_desc_timing(desc, &timings, info->prefx, info->prefy, |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 503 | width_mm, height_mm); |
| 504 | desc = edid_desc_next(edid, dta, desc); |
| 505 | } |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 506 | |
Gerd Hoffmann | ec70aec | 2021-04-27 17:08:19 +0200 | [diff] [blame] | 507 | xtra3 = desc; |
| 508 | edid_desc_xtra3_std(xtra3); |
| 509 | desc = edid_desc_next(edid, dta, desc); |
| 510 | edid_fill_modes(edid, xtra3, dta, info->maxx, info->maxy); |
| 511 | /* |
| 512 | * dta video data block is finished at thus point, |
| 513 | * so dta descriptor offsets don't move any more. |
| 514 | */ |
| 515 | |
Gerd Hoffmann | ed7f17a | 2021-04-27 17:08:18 +0200 | [diff] [blame] | 516 | edid_desc_ranges(desc); |
| 517 | desc = edid_desc_next(edid, dta, desc); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 518 | |
Gerd Hoffmann | ec70aec | 2021-04-27 17:08:19 +0200 | [diff] [blame] | 519 | if (desc && info->name) { |
Gerd Hoffmann | ed7f17a | 2021-04-27 17:08:18 +0200 | [diff] [blame] | 520 | edid_desc_text(desc, 0xfc, info->name); |
| 521 | desc = edid_desc_next(edid, dta, desc); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 522 | } |
| 523 | |
Gerd Hoffmann | ec70aec | 2021-04-27 17:08:19 +0200 | [diff] [blame] | 524 | if (desc && info->serial) { |
Gerd Hoffmann | ed7f17a | 2021-04-27 17:08:18 +0200 | [diff] [blame] | 525 | edid_desc_text(desc, 0xff, info->serial); |
| 526 | desc = edid_desc_next(edid, dta, desc); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 527 | } |
| 528 | |
Gerd Hoffmann | ed7f17a | 2021-04-27 17:08:18 +0200 | [diff] [blame] | 529 | while (desc) { |
| 530 | edid_desc_dummy(desc); |
| 531 | desc = edid_desc_next(edid, dta, desc); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 532 | } |
| 533 | |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 534 | /* =============== display id extensions =============== */ |
| 535 | |
| 536 | if (did && large_screen) { |
Akihiko Odaki | 4377683 | 2022-02-13 11:15:29 +0900 | [diff] [blame] | 537 | qemu_displayid_generate(did, &timings, info->prefx, info->prefy, |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 538 | width_mm, height_mm); |
| 539 | } |
| 540 | |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 541 | /* =============== finish up =============== */ |
| 542 | |
Konstantin Nazarov | 5a4e88c | 2021-04-27 17:08:23 +0200 | [diff] [blame] | 543 | edid_checksum(edid, 127); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 544 | if (dta) { |
Konstantin Nazarov | 5a4e88c | 2021-04-27 17:08:23 +0200 | [diff] [blame] | 545 | edid_checksum(dta, 127); |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 546 | } |
Konstantin Nazarov | 35f171a | 2021-04-27 17:08:24 +0200 | [diff] [blame] | 547 | if (did) { |
| 548 | edid_checksum(did, 127); |
| 549 | } |
Gerd Hoffmann | 72d277a | 2018-09-25 09:56:42 +0200 | [diff] [blame] | 550 | } |
Gerd Hoffmann | e7992fc | 2018-09-25 09:56:43 +0200 | [diff] [blame] | 551 | |
| 552 | size_t qemu_edid_size(uint8_t *edid) |
| 553 | { |
| 554 | uint32_t exts; |
| 555 | |
| 556 | if (edid[0] != 0x00 || |
| 557 | edid[1] != 0xff) { |
| 558 | /* doesn't look like a valid edid block */ |
| 559 | return 0; |
| 560 | } |
| 561 | |
| 562 | exts = edid[126]; |
| 563 | return 128 * (exts + 1); |
| 564 | } |