This commit is contained in:
Trevor Joynson (trevorj)
2016-06-10 02:48:27 -04:00
parent 6a1dc7e89c
commit c378d16125
13 changed files with 142 additions and 190 deletions

2
.gitignore vendored
View File

@@ -35,4 +35,6 @@
*.c
*.bin
*.ihex
*.o.pre
*.o.post

View File

@@ -39,6 +39,5 @@
#define ESTABLISHED_TIMING2_BITS 0x08 /* Bit 3 -> 1024x768 @60 Hz */
#define HSYNC_POL 0
#define VSYNC_POL 0
#define CRC 0x55
#include "edid.S"

View File

@@ -39,6 +39,5 @@
/* No ESTABLISHED_TIMINGx_BITS */
#define HSYNC_POL 1
#define VSYNC_POL 1
#define CRC 0xa0
#include "edid.S"

View File

@@ -39,6 +39,5 @@
/* No ESTABLISHED_TIMINGx_BITS */
#define HSYNC_POL 1
#define VSYNC_POL 1
#define CRC 0x9d
#include "edid.S"

View File

@@ -39,6 +39,5 @@
/* No ESTABLISHED_TIMINGx_BITS */
#define HSYNC_POL 1
#define VSYNC_POL 1
#define CRC 0x26
#include "edid.S"

View File

@@ -39,6 +39,5 @@
/* No ESTABLISHED_TIMINGx_BITS */
#define HSYNC_POL 1
#define VSYNC_POL 1
#define CRC 0x05
#include "edid.S"

View File

@@ -1,6 +1,5 @@
/* 3840x2160: 3840x2160 594.000 3840 4016 4104 4400 2160 2168 2178 2250 +hsync +vsync */
/* 3840x2160: Modeline "3840x2160" 594.000 3840 4016 4104 4400 2160 2168 2178 2250 +hsync +vsync */
#define HSYNC_POL 1
#define CRC 0x9b
#define VSYNC_POL 1
#define YPULSE (63+10)
#define XPULSE 88

View File

@@ -36,6 +36,5 @@
#define ESTABLISHED_TIMING1_BITS 0x01 /* Bit 0: 800x600 @ 60Hz */
#define HSYNC_POL 1
#define VSYNC_POL 1
#define CRC 0xc2
#include "edid.S"

View File

@@ -1,58 +0,0 @@
In the good old days when graphics parameters were configured explicitly
in a file called xorg.conf, even broken hardware could be managed.
Today, with the advent of Kernel Mode Setting, a graphics board is
either correctly working because all components follow the standards -
or the computer is unusable, because the screen remains dark after
booting or it displays the wrong area. Cases when this happens are:
- The graphics board does not recognize the monitor.
- The graphics board is unable to detect any EDID data.
- The graphics board incorrectly forwards EDID data to the driver.
- The monitor sends no or bogus EDID data.
- A KVM sends its own EDID data instead of querying the connected monitor.
Adding the kernel parameter "nomodeset" helps in most cases, but causes
restrictions later on.
As a remedy for such situations, the kernel configuration item
CONFIG_DRM_LOAD_EDID_FIRMWARE was introduced. It allows to provide an
individually prepared or corrected EDID data set in the /lib/firmware
directory from where it is loaded via the firmware interface. The code
(see drivers/gpu/drm/drm_edid_load.c) contains built-in data sets for
commonly used screen resolutions (800x600, 1024x768, 1280x1024, 1600x1200,
1680x1050, 1920x1080) as binary blobs, but the kernel source tree does
not contain code to create these data. In order to elucidate the origin
of the built-in binary EDID blobs and to facilitate the creation of
individual data for a specific misbehaving monitor, commented sources
and a Makefile environment are given here.
To create binary EDID and C source code files from the existing data
material, simply type "make".
If you want to create your own EDID file, copy the file 1024x768.S,
replace the settings with your own data and add a new target to the
Makefile. Please note that the EDID data structure expects the timing
values in a different way as compared to the standard X11 format.
X11:
HTimings: hdisp hsyncstart hsyncend htotal
VTimings: vdisp vsyncstart vsyncend vtotal
EDID:
#define XPIX hdisp
#define XBLANK htotal-hdisp
#define XOFFSET hsyncstart-hdisp
#define XPULSE hsyncend-hsyncstart
#define YPIX vdisp
#define YBLANK vtotal-vdisp
#define YOFFSET (63+(vsyncstart-vdisp))
#define YPULSE (63+(vsyncend-vsyncstart))
The CRC value in the last line
#define CRC 0x55
also is a bit tricky. After a first version of the binary data set is
created, it must be checked with the "edid-decode" utility which will
most probably complain about a wrong CRC. Fortunately, the utility also
displays the correct CRC which must then be inserted into the source
file. After the make procedure is repeated, the EDID data set is ready
to be used.

View File

@@ -10,22 +10,28 @@ CODE := $(patsubst %.S, %.c, $(SOURCES))
all: $(BIN) $(IHEX) $(CODE)
clean:
rm -f *.o *.bin.ihex *.bin *.c
rm -f *.o *.crc *.bin *.bin.ihex *.c
%.o: %.S
cc -c $^
cc -c -DCRC="0x00" -o $@ $^
%.bin: %.o
%.bin.nocrc: %.o
objcopy -Obinary $^ $@
%.bin.ihex: %.o
%.crc: %.bin.nocrc
cat $^ | edid-decode \
| sed -ne 's/^Checksum: 0x\w\+ (should be \(0x\w\+\))$$/\1/p' >$@
%.p: %.crc %.S
cc -c -DCRC="$$(cat $*.crc)" -o $@ $*.S
%.bin: %.p
objcopy -Obinary $^ $@
%.bin.ihex: %.p
objcopy -Oihex $^ $@
dos2unix $@ 2>/dev/null
%.c: %.bin
@echo "{" >$@; hexdump -f hex $^ >>$@; echo "};" >>$@
%.crc: %.bin
./compute-crc $^ >$@
echo $(MAKE) $^

View File

@@ -1,2 +1,65 @@
# edid-generator
edid-generator
==============
Hackerswork to generate an EDID binary file from given Xorg Modelines
An extension of the awesome work provided in the Linux kernel documentation (in `docs/EDID`).
Simplifies the process greatly by allowing you to use a standard modeline as well as automatically calculating the CRC
and applying it to the resulting image.
Requirements
------------
```
sudo apt install zsh edid-decode automake
```
Usage
-----
If you don't have a `<mode>.S` prepared yet, generate one using a file containing Xorg Modelines. Lines that do not
contain modelines are ignored, so you can just read right from `xorg.conf`.
```s
./modeline2edid /etc/X11/xorg.conf
```
You can also just read from `stdin` the way you'd expect:
```s
./modeline2edid
# or explicitly:
./modeline2edid -
```
After this creates your `<name>.S` files for each modeline it encounters, simply `make`:
```sh
make
```
The end result, providing all goes well, is a glorious EDID bin image for each mode you gave to it. A `<name>.S` file
is templated, and then `make` is invoked to compile it into `<name>.bin`. It's actually compiled twice; once with an
invalid CRC in order to generate said CRC to enter it into the template, after which we recompile, hence glorious bins.
NOTE: If you use a ratio other than 16:9, you'll need to specify it at the end of the modeline.as `ratio=4:3`.
Ratios are hard defined in `edid.S`, so if you are trying to do something non-standard you'll need to add it.
Why?
----
Many monitors and TVs (both high and low end) provide invalid EDID data. After dealing with this for years, I wanted to
automate this process.
The final straw was when I bought a Sceptre 4K tv for ~$225 and ran into a long series of hurdles to get it to operate
as expected at `3840x2160@60`. After doing this enough times, I had to automate it or I was going to go crazy.
I used this to quickly iterate while troubleshhooting, finally it's all working from KMS all the way down to X!
(Via `drm_kms_helper.edid_firmware=DP-1:edid/blah.bin``if you're interested. I'm using radeon + intel, with nvidia you
have to specify it in `xorg.conf`/`xorg.conf.d` as they don't yet support KMS for the fb console yet; their beta
drivers, 367 at the time of writing, only support using KMS for the xorg server.)
Sometimes I hate being such a perfectionist. Keep in mind this project was made in a couple hours, I certainly didn't
attempt to polish it in the least ;)

View File

@@ -1,3 +0,0 @@
#!/bin/sh
# A tad gross, but hey, it works.
cat "$bin_fn" | edid-decode | sed -ne 's/^Checksum: 0x\w\+ (should be \(0x\w\+\))$/\1/p'

View File

@@ -1,148 +1,97 @@
#!/bin/zsh
setopt errexit errreturn
setopt sourcetrace verbose
setopt xtrace
#setopt xtrace
template-S() {
[[ ${(L)1} = mode(|line) ]] || return 1
echo "-- Found naughty unicorn: $@"
shift 1
munge-x11-modeline() {
local args=() arg
for arg in "$@"; do
[[ "$arg" != \"*\" ]] || arg="${arg:1:-1}"
args+=("$arg")
done
set -- ${(@)args}
local name="${1//\"}"
[[ -z "$name" ]] && echo "Could not parse modeline: $@" >&2 && return 1
local fn="${name}.S"
[[ "$1" != "Modeline" ]] || shift 1
name="$1" pixel_clock_mhz="$2"; shift 2
hdisp="$1" hsyncstart="$2" hsyncend="$3" htotal="$4"; shift 4
vdisp="$1" vsyncstart="$2" vsyncend="$3" vtotal="$4"; shift 4
local -F pixel_clock_mhz=$2
local -i pixel_clock_khz=$((pixel_clock_mhz * 1000))
shift 2
fn="${name}.S"
bin_fn="${name}.bin"
local -i hdisp="$1" hsyncstart="$2" hsyncend="$3" htotal="$4"; shift 4
local -i vdisp="$1" vsyncstart="$2" vsyncend="$3" vtotal="$4"; shift 4
hsync_polarity=0
vsync_polarity=0
ratio="16:9" # todo calc
dpi="96"
edid_version="1.3"
vfreq_hz="60"
crc="0x00"
local -i hsync_polarity=0 vsync_polarity=0 dpi=96 vfreq_hz=60
local edid_version="1.3" ratio="16:9" # TODO calc ratio
local arg
for arg in "$@"; do
case "${(L)arg}" in
*hsync) [[ "${arg:1:1}" == "-" ]] || hsync_polarity=1 ;;
*vsync) [[ "${arg:1:1}" == "-" ]] || vsync_polarity=1 ;;
[-+]hsync) [[ "${arg:1:1}" == "-" ]] || hsync_polarity=1 ;;
[-+]vsync) [[ "${arg:1:1}" == "-" ]] || vsync_polarity=1 ;;
ratio=*|xy_ratio=*) ratio="${arg#*=}" ;;
dpi=*) dpi="${arg#*=}" ;;
edid_version=*) edid_version="${arg#*=}" ;;
vfreq=*|vfreq_hz=*) vfreq_hz="${arg#*=}" ;;
crc=*) crc="${arg#*=}" ;;
*) echo "Unknown modeline option passed: $arg" >&2 ;;
*) echo "Ignoring unknown modeline option passed: '$arg'" >&2 ;;
esac
done
}
gen-S-defines() {
[[ $# -eq 0 ]] || munge-x11-modeline "$@"
local -i pixel_clock_khz=$((pixel_clock_mhz * 1000))
local -A defines
defines=(
TIMING_NAME "${(qqq)name}"
CLOCK "$pixel_clock_khz"
XPIX "$hdisp"
XBLANK "$((htotal - hdisp))"
XOFFSET "$((hsyncstart - hdisp))"
XPULSE "$((hsyncend - hsyncstart))"
CLOCK "$pixel_clock_khz"
XPIX "$hdisp"
XBLANK "$((htotal - hdisp))"
XOFFSET "$((hsyncstart - hdisp))"
XPULSE "$((hsyncend - hsyncstart))"
YPIX "$vdisp"
YBLANK "$((vtotal - vdisp))"
YOFFSET "(63+$((vsyncstart - vdisp)))"
YPULSE "(63+$((vsyncend - vsyncstart)))"
YPIX "$vdisp"
YBLANK "$((vtotal - vdisp))"
YOFFSET "(63+$((vsyncstart - vdisp)))"
YPULSE "(63+$((vsyncend - vsyncstart)))"
VERSION "${edid_version%%.*}"
REVISION "${edid_version#*.}"
VERSION "${edid_version%%.*}"
REVISION "${edid_version#*.}"
XY_RATIO "XY_RATIO_${(U)ratio//:/_}"
DPI "$dpi"
VFREQ "$vfreq_hz"
HSYNC_POL "$hsync_polarity"
VSYNC_POL "$vsync_polarity"
CRC "$crc"
XY_RATIO "XY_RATIO_${(U)ratio//:/_}"
DPI "$dpi"
VFREQ "$vfreq_hz"
HSYNC_POL "$hsync_polarity"
VSYNC_POL "$vsync_polarity"
)
}
template-S() {
local -a lines=("/* $name: $* */")
local -a lines=("/* $name: $REPLY */")
local k
for k in ${(k)defines}; do
lines+=("#define $k ${defines[$k]}")
done
lines+=('#include "edid.S"')
echo "${(j:\n:)lines[@]}" | tee "$fn"
echo "Wrote $fn" >&2
echo "${(j:\n:)lines[@]}" > "$fn"
echo "Wrote $fn"
}
compile() {
rm -fv $bin_fn
echo "Compiling $bin_fn" >&2
local i=0
while true; do
let i++ || :
make >/dev/null 2>&1 || :
! test -f "$bin_fn" || break
[[ $i -lt 100 ]] || (echo "Failed to compute checksum for $bin_fn" >&2; exit 1)
done
echo "Compiled $bin_fn in $i iterations" >&2
}
local f=${1:-'-'}
[[ $f != '-' ]] || f="/dev/stdin"
modeline2edid() {
local name
local -A defines
gen-S-defines "$@"
[[ -n "$name" ]] || (echo "Didn't get a name? The hell?" >&2 && exit 1)
# Template and compile
template-S "$@"
compile
# Fix CRC
local crc=$(${0:h}/compute-crc)
# we're done if we don't have a crc to fix
[[ -n "$crc" ]] || return
# Re-template and compile with proper CRC
defines[CRC]=$crc template-S "$@"
compile
}
main() {
zmodload -i zsh/zutil
# support single argument filename
[[ -z $1 || $1 = -* ]] || set -- -f "$@"
local o_file=()
zparseopts -K -D -E -- f:o_file
local f=${o_file[-1]}
case $f in
-|) f="/dev/stdin" ;;
esac
if [ -z "$f" ]; then
echo "Usage:" >&2
echo "$0 [-f] FILENAME Parse modelines out of a file (eg xorg.conf) or '-' for stdin." >&2
exit 1
fi
echo "Searching for unicorns in $f" >&2
while read; do
echo "-- Found a unicorn: $REPLY" >&2
modeline2edid $=REPLY
done < $f
}
if [[ -z "$f" || "$f" == "-h" ]]; then
self=${0:t}
cat >&2 <<-EOF
Modeline2EDID, version forever 0.0.1
Help:
$self -h
Parse modelines from stdin:
$self
$self -
Parse modelines from a file (eg xorg.conf)
$self FILENAME
EOF
exit 1
fi
echo "Searching for runaway unicorns in '$f'"
while read; do
# trim
REPLY=($=REPLY)
[[ -n "$REPLY" ]] || continue
template-S ${(@)REPLY} || :
done < $f