Linux AMDGPU
EDID Override
The amdgpu
driver does not provide a way to set the Output Colur Format such as Full RGB, Limited RGB, etc.
My monitor, an LG 4K 27 inch, indicates in its EDID that it supports YCbCr444
and the amdgpu driver would automatically pick and use that. The result was an image which had overly-bright greys, even if the black level was set to Low in the monitor OSD (with it set to High, it was even brighter).
The solution below walks you through the steps on Pop!_OS 22.04. It has been compiled from various sources, referenced inline as appropriate.
Note: the following assumes that the monitors are always connected to the same outputs on the graphics card.
Methodology
EDID is stored on the monitor, and tells connected devices (in this case, the graphics card and its driver) which resolutions and output modes are supported.
The process here is to take the EDID from the monitor, make some changes to it to remove support for the YCbCr444 mode that we don't want, and then tell the OS/driver to load the modified version at boot instead of the one provided by the monitor.
Install wxEDID
The EDID are stored in binary, and as such it is easiest to use a tool which can load and parse the EDID, make the changes, and then save.
wxEDID can be installed on Pop!_OS 22.04 via the Pop!_Shop.
Export the EDID
First, identify which monitor you want to override. The EDID from the monitors are available at /sys/devices/
and the easiest way to locate them is using the find
command, like this:
$ find /sys/devices/ -name edid
On my system, this produces the following output:
/sys/devices/pci0000:00/0000:00:03.1/0000:09:00.0/0000:0a:00.0/0000:0b:00.0/drm/card1/card1-HDMI-A-1/edid
/sys/devices/pci0000:00/0000:00:03.1/0000:09:00.0/0000:0a:00.0/0000:0b:00.0/drm/card1/card1-DP-2/edid
/sys/devices/pci0000:00/0000:00:03.1/0000:09:00.0/0000:0a:00.0/0000:0b:00.0/drm/card1/card1-Writeback-1/edid
/sys/devices/pci0000:00/0000:00:03.1/0000:09:00.0/0000:0a:00.0/0000:0b:00.0/drm/card1/card1-HDMI-A-2/edid
/sys/devices/pci0000:00/0000:00:03.1/0000:09:00.0/0000:0a:00.0/0000:0b:00.0/drm/card1/card1-DP-1/edid
Note the names after the /drm/cardn part, e.g. card1-HDMI-A-1
, card1-HDMI-A-2
, card1-DP-1
, card1-DP-2
.
Create a new directory in your home directory:
$ mkdir ~/edid
Copy the relevant EDID image to the ~/edid directory. For example, my 27 inch monitor is connected to card1-HDMI-A-1, so the command is:
$ cp -n "/sys/devices/pci0000:00/0000:00:03.1/0000:09:00.0/0000:0a:00.0/0000:0b:00.0/drm/card1/card1-HDMI-A-1/edid" ~/edid/edid27.bin
The name of the image does not matter, but it might be useful to name it after your monitor for easier reference.
If you're unsure which output is the correct monitor, look at the directories under /sys/kernel/debug/dri/1
. From there, you can interact with the outputs/monitors.
For example, this command will trigger a HDMI hotplug event on HDMI-A-1. This should cause the monitor to briefly blank out if it's the correct one:
$ echo 1 > /sys/kernel/debug/dri/1/HDMI-A-1/trigger_hotplug
Edit the image in wxEDID
credit to TingPing's blog [https://blog.tingping.se/2018/12/01/amdgpu-fullrgb.html] for these steps.
Open the file in wxEDID
and edit it as follows to disable YCbCr support:
SPF: Supported features -> change value of vsig_format to 0b00 CHD: CEA-861 header -> change the value of YCbCr420 and YCbCr444 to 0 VSD: Vendor Specific Data Block -> change the value of DC_Y444 to 0
Use the modified EDID file:
Option -> Recalc Checksum
File -> Save EDID Binary (to ~/modified_edid.bin for example)
Test the new EDID image
You can load the EDID with the system running by setting edid_override on the relevant output, and then calling trigger_hotplug.
$ cat ~/edid/edid27_rgb.bin > /sys/kernel/debug/dri/1/HDMI-A-1/edid_override;echo 1 > /sys/kernel/debug/dri/1/HDMI-A-1/trigger_hotplug
Copy to the firmware directory
$ sudo install -Dm644 ~/edid/edid26.bin /usr/bin/firmware/edid/edid27_rgb.bin
Add to Linux boot command line
In Pop!_OS 22.04 you should use the kernelstub
utility to add command line entries, rather than by the more traditional approach of editing config files and update-grub.
Check the current configuration:
$ sudo kernelstub -p
kernelstub.Config : INFO Looking for configuration...
kernelstub : INFO System information:
OS:..................Pop!_OS 22.04
Root partition:....../dev/sdb3
Root FS UUID:........8959c7c6-6f47-469b-8dba-753dc55b168f
ESP Path:............/boot/efi
ESP Partition:......./dev/sdb1
ESP Partition #:.....1
NVRAM entry #:.......-1
Boot Variable #:.....0000
Kernel Boot Options:.quiet loglevel=0 systemd.show_status=false splash drm.edid_firmware=HDMI-A-1:edid/edid27_rgb.bin
Kernel Image Path:.../boot/vmlinuz-6.8.0-76060800daily20240311-generic
Initrd Image Path:.../boot/initrd.img-6.8.0-76060800daily20240311-generic
Force-overwrite:.....False
kernelstub : INFO Configuration details:
ESP Location:................../boot/efi
Management Mode:...............True
Install Loader configuration:..True
Configuration version:.........3
Set a new Kernel Boot Options entry. There are two options - you can either append a new entry using kernelstub -a
, or alternatively specify the complete line of options. This could be be useful if you have made errors which you wish to correct in one shot.
To specify a new option addition:
$ sudo kernelstub -a "drm.edid_firmware=HDMI-A-1:edid/edid27_rgb.bin"
To specify a complete options line:
$ sudo kernelstub -o "quiet loglevel=0 systemd.show_status=false splash drm.edid_firmware=HDMI-A-1:edid/edid27_rgb.bin"
Note that the structure of the added option is drm.edid_firmware=
followed by the output name (in this case, HDMI-A-1
), followed by a colon :
and then the path to the overriding EDID file relative path.
Add the EDID to initramfs
credit to u/mgedmin on Reddit https://www.reddit.com/r/Ubuntu/comments/1afvgh1/comment/koeugjc/.
The boot process of Pop!_OS uses "early KMS" and so the EDID must be added to the initramfs image.
Add a new file /etc/initramfs-tools/hooks/edid
with the following contents to the file:
Important: this is the file contents for
/etc/initramfs-tools/hooks/edid
- DO NOT execute in a terminal!
mkdir -p ${DESTDIR}/lib/firmware/edid
cp /lib/firmware/edid/*.bin ${DESTDIR}/lib/firmware/edid/
Note that we have used the edid subdirectory consistently both in /lib/firmware/edid and the lib/firmware/edid in initramfs.
Make the hook executable:
$ sudo chmod +x /etc/initramfs-tools/hooks/edid
Update initramfs for the current kernel:
$ sudo update-initramfs -u -k $(uname -r)
Reboot
Reboot and you should see that the new EDID has been applied.
If this is not the case, check that you have used the correct graphics card output (e.g. HDMI-A-1
) and also check the output from sudo dmesg
to check for any errors.
For example, if the following appears, it could indicate that the image has not been correctly copied to initramfs.
[ 10.272584] kernel: amdgpu 0000:0b:00.0: Direct firmware load for edid/edid27_rgb.bin failed with error -2
[ 10.272587] kernel: amdgpu 0000:0b:00.0: [drm] *ERROR* [CONNECTOR:106:HDMI-A-1] Requesting EDID firmware "edid/edid27_rgb.bin" failed (err=-2)