├── requirements.txt ├── .gitignore ├── Makefile ├── README.md └── edid-rw /requirements.txt: -------------------------------------------------------------------------------- 1 | smbus 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | README.html 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012 Mark Blakeney. This program is distributed under 2 | # the terms of the GNU General Public License. 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or any 7 | # later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, but 10 | # WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # General Public License at for more 13 | # details. 14 | 15 | PROG = edid-rw 16 | SRC = README.md 17 | DOC = $(SRC:.md=.html) 18 | 19 | doc: $(DOC) 20 | 21 | $(DOC): $(SRC) 22 | markdown $< >$@ 23 | 24 | check: 25 | ruff check $(PROG) 26 | vermin --no-tips -i -q $(PROG) 27 | 28 | install:: 29 | install -CD $(PROG) ~/bin 30 | 31 | clean:: 32 | rm -rf $(DOC) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## edid-rw: A utility to read and write a display EDID value 2 | 3 | _Sorry, but I do not support this project anymore. I wrote this utility 4 | for my own one-off personal use many years ago but have no means to test 5 | and support changes nowadays. Feel free to fork and improve it._ 6 | 7 | ### Overview 8 | 9 | This utility will read and/or write a display's EDID data structure. Use 10 | it with the edid-decode utility to view and check an EDID. 11 | You can also write new EDID data to attempt to fix a corrupt EDID. 12 | 13 | *WARNING - THIS UTILITY CAN DESTROY YOUR DISPLAY, MOTHERBOARD, OR OTHER 14 | CONNECTED HARDWARE IF RUN INCORRECTLY. Be very sure you understand what 15 | you are doing. See [this issue](http://github.com/bulletmark/edid-rw/issues/5) 16 | for an example of what can happen.* 17 | 18 | You may have to disable output to the display before you can write the 19 | EDID. 20 | 21 | ### Installation 22 | 23 | Requires python3 smbus module, and edid-decode utility. 24 | 25 | Install these prerequisites on Debian/Ubuntu: 26 | 27 | sudo apt-get install python3-smbus edid-decode 28 | 29 | Or, install these prerequisites on Arch: 30 | 31 | yay -S i2c-tools edid-decode-git 32 | 33 | Get this source code: 34 | 35 | git clone https://github.com/bulletmark/edid-rw 36 | cd edid-rw 37 | 38 | This utility should run using Python version 3.2+. It does not work with 39 | Python 2. 40 | 41 | ### Usage 42 | 43 | Run with `-h` switch to see usage and optional arguments: 44 | 45 | ./edid-rw -h 46 | 47 | Fetch and decode display address 0 EDID data: 48 | 49 | sudo ./edid-rw 0 | edid-decode 50 | 51 | Fetch and decode display address 1 EDID data: 52 | 53 | sudo ./edid-rw 1 | edid-decode 54 | 55 | Capture display address 0 EDID data, edit it, and write it back to 56 | device. Use `!Gxxd [-r]` within vim to read, edit, and write binary 57 | file. See `:h xxd` within vim help. You should set the checksum (last) 58 | byte correctly although edit-rw will calculate and set the checksum 59 | itself if you include the `-f (--fix)` switch. edid-rw will always 60 | validate the checksum and will not write an invalid EDID: 61 | 62 | *WARNING - Be sure to triple check the EDID address you are about to 63 | write!* 64 | 65 | sudo ./edid-rw 0 >edid.bin 66 | vim -b edid.bin # Then use xxd within vim, see ":h xxd" in vim 67 | sudo ./edid-rw -w 0 . 72 | 73 | ### License 74 | 75 | Copyright (C) 2012 Mark Blakeney. This program is distributed under the 76 | terms of the GNU General Public License. 77 | 78 | This program is free software: you can redistribute it and/or modify it 79 | under the terms of the GNU General Public License as published by the 80 | Free Software Foundation, either version 3 of the License, or any later 81 | version. 82 | 83 | This program is distributed in the hope that it will be useful, but 84 | WITHOUT ANY WARRANTY; without even the implied warranty of 85 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 86 | Public License at for more details. 87 | 88 | 89 | -------------------------------------------------------------------------------- /edid-rw: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | ''' 3 | Program to read or write Extended Display Identification Data (EDID) to 4 | a digital display. Default action is to READ binary EDID data from 5 | specified i2c_device and write it to standard output. With "-w", will 6 | instead read EDID binary data from standard input and WRITE it to 7 | specified i2c_device. Always validates the EDID checksum before allowing 8 | a write to device. 9 | ''' 10 | # (C) Mark Blakeney, April 2012. 11 | 12 | import os 13 | import sys 14 | import array 15 | import argparse 16 | import subprocess 17 | import time 18 | from smbus import SMBus 19 | 20 | EDID_HDR = 20 21 | EDID_LEN = (128, 256) 22 | EDID_ADDR = 0x50 23 | 24 | class STDDEV: 25 | 'Standard input/output device for testing' 26 | def __init__(self, args): 27 | 'Create standard input/output device' 28 | pass 29 | 30 | def read(self, n): 31 | 'Read a byte from stdin' 32 | return array.array('B', os.read(sys.stdin.fileno(), 1)).tolist()[0] 33 | 34 | def write(self, n, val): 35 | 'Write a byte to stdout' 36 | os.write(sys.stdout.fileno(), array.array('B', (val,)).tobytes()) 37 | 38 | class SMBDEV: 39 | 'SMB device' 40 | def __init__(self, args): 41 | 'Create SMB device' 42 | # Need ic2 device module installed 43 | if subprocess.call(('modprobe', 'i2c-dev')) != 0: 44 | sys.exit('ERROR: Can not load i2c-dev. Must use sudo or be root.') 45 | 46 | # Access i2c device 47 | self.smb = SMBus(args.i2c_device_num[0]) 48 | 49 | def read(self, n): 50 | 'Read a byte from given SMB device' 51 | return self.smb.read_byte_data(EDID_ADDR, n) 52 | 53 | def write(self, n, val): 54 | 'Write a byte to given SMB device' 55 | self.smb.write_byte_data(EDID_ADDR, n, val) 56 | 57 | def getlen(edidhdr): 58 | 'Return the number of bytes expected for this edid hdr' 59 | if edidhdr[18] <= 0 or edidhdr[18] > len(EDID_LEN): 60 | sys.exit('ERROR: Unknown EDID version %d' % edidhdr[18]) 61 | 62 | return EDID_LEN[edidhdr[18] - 1] 63 | 64 | def blkname(blk): 65 | 'Return a name for given block number' 66 | return 'EDID' if blk == 0 else ('Ext block %d' % blk) 67 | 68 | def checksum(edid): 69 | 'Return EDID checksum of the given buffer' 70 | return (0 - (sum(edid[:-1]) % 256)) % 256 71 | 72 | def main(): 73 | 'Main code' 74 | 75 | # Process command line options 76 | opt = argparse.ArgumentParser(description=__doc__.strip(), 77 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 78 | opt.add_argument('-w', '--write', action='store_true', 79 | help='Write EDID to i2c_device from data read from stdin') 80 | opt.add_argument('-t', '--std', action='store_true', 81 | help='Read stdin or write stdout only, for testing') 82 | opt.add_argument('-f', '--fix', action='store_true', 83 | help='Fix last checksum byte automatically when writing the ' 84 | 'EDID and extensions') 85 | opt.add_argument('-s', '--sleep', default=0.01, type=float, 86 | help='Sleep this many seconds between writes') 87 | opt.add_argument('i2c_device_num', nargs=1, type=int, 88 | help='i2c device number, e.g. 0 or 1 or 2 ..') 89 | args = opt.parse_args() 90 | 91 | # Assign smb device, or stdin/stdout for testing 92 | dev = STDDEV(args) if args.std else SMBDEV(args) 93 | 94 | if args.write: 95 | # Read EDID from stdin 96 | edid = array.array('B', os.read(sys.stdin.fileno(), 97 | max(EDID_LEN))).tolist() 98 | edid_len = getlen(edid) 99 | edid_n = edid[edid_len - 2] 100 | nbytes = edid_len + (edid_len * edid_n) 101 | if len(edid) != nbytes: 102 | sys.exit('ERROR: Input must be %d bytes' % nbytes) 103 | 104 | # Check each EDID block checksum is valid 105 | for b in range(edid_n + 1): 106 | x = (b + 1) * edid_len 107 | actsum = edid[x - 1] 108 | calsum = checksum(edid[x - edid_len:x]) 109 | if actsum != calsum: 110 | if args.fix: 111 | edid[x - 1] = calsum 112 | sys.stderr.write( 113 | '%s checksum 0x%02x was BAD, rewrote it to ' 114 | '0x%02x.\n' % (blkname(b), actsum, calsum)) 115 | else: 116 | sys.exit('ERROR: %s checksum 0x%02x is BAD, should be ' 117 | '0x%02x.' % (blkname(b), actsum, calsum)) 118 | 119 | # Write EDID to device 120 | for i, val in enumerate(edid): 121 | dev.write(i, val) 122 | time.sleep(args.sleep) 123 | else: 124 | # Read EDID from device 125 | edid = [dev.read(i) for i in range(EDID_HDR)] 126 | edid_len = getlen(edid) 127 | edid += [dev.read(i) for i in range(EDID_HDR, edid_len)] 128 | 129 | # Read extensions from device (if any) 130 | edid += [dev.read(i) for i in 131 | range(edid_len, edid_len + edid_len * edid[-2])] 132 | 133 | # Write EDID to stdout 134 | os.write(sys.stdout.fileno(), array.array('B', edid).tobytes()) 135 | 136 | if __name__ == '__main__': 137 | main() 138 | --------------------------------------------------------------------------------