├── 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 |
--------------------------------------------------------------------------------