fix: enclose rounded preferred timings in EDID ranges

This commit is contained in:
Codex
2026-10-13 04:45:14 +03:00
parent 2ae8cfc564
commit db19722d09
3 changed files with 22 additions and 13 deletions
+3 -1
View File
@@ -77,6 +77,8 @@ with tempfile.TemporaryDirectory(prefix='qemu-edid-probe-') as temporary:
(144000, 60000, 1920, 1080, 144),
(144000, 0, 1600, 900, 144),
(0, 144000, 1920, 1080, 144),
(50000, 0, 1920, 1080, 50),
(255000, 0, 1280, 800, 255),
]:
data = subprocess.check_output([str(directory / 'probe'), *map(str, (configured, frontend, width, height))])
assert all(sum(data[offset:offset + 128]) % 256 == 0 for offset in range(0, len(data), 128)), 'invalid EDID checksum'
@@ -93,6 +95,6 @@ with tempfile.TemporaryDirectory(prefix='qemu-edid-probe-') as temporary:
assert ranges[5] <= actual <= ranges[6], f'vertical range {ranges[5:7]} excludes {actual}'
horizontal_khz = clock / (horizontal + hblank) / 1000
assert ranges[7] <= horizontal_khz <= ranges[8], f'horizontal range {ranges[7:9]} excludes {horizontal_khz}'
if expected <= 75:
if configured == 0 and expected <= 75:
assert ranges[5:9] == bytes((50, 125, 30, 160)), 'legacy monitor range changed'
print(f'PASS configured={configured}, frontend={frontend}: {width}x{height}@{actual:.6f}, ranges={list(ranges[5:9])}')
+5 -1
View File
@@ -112,7 +112,11 @@ The probe applies the tracked patch to four relevant source files in a temporary
directory, compiles the actual EDID and virtio GPU generator functions with a
small dependency harness, and decodes the result. It checks 60/75 Hz fallback,
144 Hz configuration, a conflicting 60 Hz frontend request, resized dimensions,
checksums and monitor ranges. At 1920×1080/144000 mHz the preferred timing is
checksums and monitor ranges. It also checks the supported vertical-refresh
boundaries: 1920×1080/50000 mHz and 1280×800/255000 mHz. Range endpoints
use floor/ceiling of the encoded timing after pixel-clock rounding, so the
49.998895 Hz lower-boundary timing is included by a 49 Hz minimum. Normal
automatic 60/75 Hz cases retain the legacy 50–125 Hz range. At 1920×1080/144000 mHz the preferred timing is
143.997231 Hz after EDID clock rounding. This is an advertised guest mode,
not measured frame delivery or proof of a 144 Hz physical output.
+14 -11
View File
@@ -33,13 +33,13 @@
for (output_idx = 0, node = g->conf.outputs;
--- a/hw/display/edid-generate.c
+++ b/hw/display/edid-generate.c
@@ -220,17 +220,17 @@
@@ -220,17 +220,20 @@
desc[5 + len] = '\n';
}
-static void edid_desc_ranges(uint8_t *desc)
+static void edid_desc_ranges(uint8_t *desc, uint32_t refresh_rate,
+ uint32_t horizontal_rate)
+static void edid_desc_ranges(uint8_t *desc, uint64_t clock,
+ uint32_t htotal, uint32_t vtotal)
{
edid_desc_type(desc, 0xfd);
@@ -50,22 +50,25 @@
- /* horizontal (30 -> 160 kHz) */
- desc[7] = 30;
- desc[8] = 160;
+ /* Preserve the legacy ranges and include the preferred mode. */
+ desc[5] = 50;
+ desc[6] = MIN(255, MAX(125, DIV_ROUND_UP(refresh_rate, 1000)));
+ /* Enclose the encoded timing after its 10 kHz pixel-clock rounding,
+ * while retaining legacy ranges whenever they already contain it. */
+ uint64_t vperiod = (uint64_t)htotal * vtotal;
+ uint64_t hperiod = (uint64_t)htotal * 1000;
+ desc[5] = MIN(50, clock / vperiod);
+ desc[6] = MIN(255, MAX(125, DIV_ROUND_UP(clock, vperiod)));
+
+ desc[7] = 30;
+ desc[8] = MIN(255, MAX(160, DIV_ROUND_UP(horizontal_rate, 1000)));
+ desc[7] = MIN(30, clock / hperiod);
+ desc[8] = MIN(255, MAX(160, DIV_ROUND_UP(clock, hperiod)));
/* max dot clock (2550 MHz) */
desc[9] = 2550 / 10;
@@ -513,7 +513,8 @@
@@ -513,7 +516,8 @@
* so dta descriptor offsets don't move any more.
*/
- edid_desc_ranges(desc);
+ edid_desc_ranges(desc, refresh_rate,
+ timings.clock * 10000 / (info->prefx + timings.xblank));
+ edid_desc_ranges(desc, (uint64_t)timings.clock * 10000,
+ info->prefx + timings.xblank, info->prefy + timings.yblank);
desc = edid_desc_next(edid, dta, desc);
if (desc && info->name) {