A small, dependency-free Python tool to cap the advertised maximum refresh rate in a monitor's EDID so that Apple Silicon Macs (M-series) reserve less display bandwidth and can drive two external displays that would otherwise exceed the budget.
Apple Silicon Macs reserve display-controller bandwidth based on the maximum capability advertised in the EDID, not the refresh rate you actually select in System Settings. An M4 / M4 Pro can drive roughly 2× 4K@144 Hz worth of external displays. A monitor that advertises 4K@160 Hz (e.g. the INNOCN GA32V1M) consumes more than its share of that budget, so connecting a second identical display fails — only one external display works at a time.
Crucially, lowering the active refresh rate in macOS does not help — the reservation is driven by the EDID's advertised maximum, not the live mode. The only reliable lever is to edit the EDID so its advertised maximum is lower (≤120 Hz), then apply it as a per-display override (e.g. with BetterDisplay).
Given an EDID dump, edid_cap.py can:
- Drop trailing extension blocks — e.g. a DisplayID block that carries 4K@144/160 Hz detailed timings (these can't live in the base/CTA blocks because their >655 MHz pixel clock exceeds the 2-byte DTD field, so vendors stash them in a DisplayID extension).
- Cap the base-block "Display Range Limits" descriptor — max vertical rate and max pixel clock (a continuous-frequency display advertises a range, not just discrete modes).
- Cap the AMD FreeSync VSDB max refresh rate (both the legacy and the FreeSync-v3 two-byte encodings).
- Recompute every affected block checksum.
The Range-Limits descriptor and the AMD VSDB are located dynamically, so the tool is not hard-coded to one monitor's byte layout.
# Cap to 120 Hz (default) and drop the trailing DisplayID block:
python3 edid_cap.py edid.bin edid_capped.bin
# Keep all extension blocks, just cap the range limits / FreeSync to 144 Hz:
python3 edid_cap.py --drop-ext 0 --cap-hz 144 edid.bin edid_capped.bin
# Rename two identical units so macOS can tell them apart:
python3 edid_cap.py --name "GA32V1M Left" left.bin left_capped.bin
python3 edid_cap.py --name "GA32V1M Right" right.bin right_capped.bin
# Don't touch the pixel-clock limit:
python3 edid_cap.py --no-dotclock-cap edid.bin edid_capped.binOptions:
| flag | default | meaning |
|---|---|---|
--cap-hz N |
120 |
cap advertised max refresh to N Hz |
--cap-dotclock-mhz M |
1190 |
cap range-limits max pixel clock (1190 MHz ≈ 4K@120, blocks 4K@144+) |
--drop-ext N |
1 |
drop the last N extension blocks (0 = keep all) |
--no-dotclock-cap |
off | leave the range-limits pixel clock untouched |
--name STR |
— | set the Display Product Name (≤13 chars), e.g. to label identical units |
Always confirm the result with edid-decode (the authoritative VESA decoder),
which explicitly flags bad checksums and out-of-range timings:
edid-decode edid_capped.binedid-decode is part of v4l-utils. On macOS it isn't in Homebrew; build it
with the helper script:
./build-edid-decode.sh # clones v4l-utils, builds ./edid-decode
./edid-decode edid_capped.binA clean run shows Checksum: 0x.. with no (should be 0x..) suffix, and no
timing/range above your cap.
- Settings → Displays → select the display → Custom display EDID → Upload
EDID Binary → choose
edid_capped.bin→ Apply EDID Now. - For two identical monitors, apply per-display using BetterDisplay's per-port (Port ID) identification, and make sure each unit's override has a distinct serial number so macOS can tell them apart. (Many units already ship with distinct serials — check the base-block serial and the Display Product Serial Number descriptor before assuming they collide.)
- This edits the EDID bytes correctly and validly; it cannot guarantee macOS will honour the override or free the second display. Pre-reservation on Apple Silicon is a documented limitation, and some setups simply won't run dual 4K@high-refresh regardless of EDID. If a validated cap + per-port overrides + distinct serials still won't bring up the second display, treat it as a hard macOS limit.
- No sample EDID dumps are bundled in this repo (manufacturer EDIDs may be
proprietary). Use your own dump exported from BetterDisplay or
edid-decode.
MIT — see LICENSE.