Skip to content

Instantly share code, notes, and snippets.

@RLovelett
Last active October 10, 2024 19:32
Show Gist options
  • Save RLovelett/171c374be1ad4f14eb22fe4e271b7eeb to your computer and use it in GitHub Desktop.
Save RLovelett/171c374be1ad4f14eb22fe4e271b7eeb to your computer and use it in GitHub Desktop.
Force RGB instead of YCbCr output for HDMI on Ubuntu

Force RGB pixel format over YCbCr

There are some monitors, in my case Dell U2413, that report having YCbCr support when plugged in over HDMI. My AMD Radeon RX 570 Series video card sees this YCbCr pixel format and then prefers that over the RGB pixel format. The result is that fonts, graphics and other visuals are pixelated and not smooth in Ubuntu.

This actually is not just a Linux problem. A similar problem exists on macOS with the same monitor hooked up over HDMI. In fact an article by John Ruble on the Atomic Object blog called Fixing the External Monitor Color Problem with My 2018 MacBook Pro attempts to fix the exact same thing.

Solution

All of the articles I could find exploring this topic advocate patching the EDID for the monitor. Unfortunately the macOS solution would not work here. Luckily I found a Reddit post that covered how to get it working.

Install Patched EDID

Install the patched EDID (this example uses the pre-patched EDID attached here) and modify GRUB to use the new EDID.

$ sudo mkdir -p /lib/firmware/edid
$ cd /lib/firmware/edid
$ wget https://gist.github.com/RLovelett/171c374be1ad4f14eb22fe4e271b7eeb/raw/edid.bin

Create initramfs hook to copy the new EDID

$ sudo tee "/etc/initramfs-tools/hooks/edid" > /dev/null <<'EOF'
#!/bin/sh
PREREQ=""
prereqs()
{
    echo "$PREREQ"
}

case $1 in
prereqs)
    prereqs
    exit 0
    ;;
esac

. /usr/share/initramfs-tools/hook-functions
# Begin real processing below this line
mkdir -p "${DESTDIR}/lib/firmware/edid"
cp -a /lib/firmware/edid/edid.bin "${DESTDIR}/lib/firmware/edid/edid.bin"
exit 0
EOF
$ chmod +x /etc/initramfs-tools/hooks/edid
$ sudo update-initramfs -u

Modify the GRUB configuration to use the new EDID

Edit /etc/default/grub and add drm_kms_helper.edid_firmware=edid/edid.bin to the end of GRUB_CMDLINE_LINUX_DEFAULT.

For example:

--- /etc/default/grub	2020-03-19 15:27:24.350222700 -0400
+++ /etc/default/grub	2020-03-19 14:22:58.052179120 -0400
@@ -7,7 +7,7 @@
 GRUB_TIMEOUT_STYLE=hidden
 GRUB_TIMEOUT=0
 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
-GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
+GRUB_CMDLINE_LINUX_DEFAULT="quiet splash drm_kms_helper.edid_firmware=edid/edid.bin"
 GRUB_CMDLINE_LINUX=""
 
 # Uncomment to enable BadRAM filtering, modify to suit your needs

After saving the changes to /etc/default/grub run sudo grub-mkconfig -o /boot/grub/grub.cfg and reboot.

Creating a Patched EDID

Get Unpatched EDID

Find your current EDID and copy it to the current working directory.

$ sudo find /sys/devices/pci*/*/*/*/*/*HDMI* -name "*edid*" | head -1 | xargs -I{} cp {} edid.bin

Compile wxEDID Utility

$ sudo apt install -y libwxgtk3.0-dev
$ wget https://sourceforge.net/projects/wxedid/files/wxedid-0.0.19.tar.gz/download
$ tar xvf wxedid-0.0.19.tar.gz
$ cd wxedid-0.0.19
$ ./configure --prefix=$HOME
$ make
$ make install

Run the wxEDID Utility

$HOME/bin/wxEDID

Edit/Patch EDID with wxEDID Utility

  1. Open the edid.bin file with wxEDID
  2. Find SPF: Supported features -> vsig_format -> replace 0b01 wih 0b00
  3. Find CHD: CEA-861 header -> change the value of YCbCr420 and YCbCr444 to 0
  4. Find VSD: Vendor Specific Data Block -> Change the value of DC_Y444 to 0
  5. Click Option on the panel-> Recalc Checksum
  6. Save patched EDID and exit

Backup

EDID edit

@kesor
Copy link

kesor commented Dec 16, 2023

Thank you! This has been bothering me for a year in a 4 identical monitor setup with 3xDP 1xHDMI. Finally decided to dig into the depths of the internet to find the answer, and even tried the EDID trick some days ago, but it was missing some of the flags you specified here. Going through the steps you described worked flawlessly.

I also noted the initramfs hook where someone was having a problem with cryptfs (which I have). And you have it here as well.

Worth noting they have a patch for this, but it never did end up in the mainline. https://www.spinics.net/lists/amd-gfx/msg53281.html

Thank you again.

@ruineka
Copy link

ruineka commented Jun 20, 2024

I'm working on a kernel patch that should make this override no longer necessary.

@nfp0
Copy link

nfp0 commented Aug 12, 2024

@ruineka Awesome! How is it coming along? Where can I follow it's development?

@rabbit83ka
Copy link

After the latest update in Ubuntu 22.04, I had to change drm_kms_helper.edid_firmware=edid/edid.bin to drm.edid_firmware=HDMI-A-1:edid/edid.bin, otherwise it was still in YCbCr-mode.

@ruineka
Copy link

ruineka commented Aug 15, 2024

I wasn't able to fully test with the amount of time I had with the monitor, but with what I saw the image looked clean. Here is a patch for others to test.

index 4f54c91b31b2..a0f1ed124137 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -94,6 +94,10 @@ static int oui(u8 first, u8 second, u8 third)
 #define EDID_QUIRK_NON_DESKTOP			(1 << 12)
 /* Cap the DSC target bitrate to 15bpp */
 #define EDID_QUIRK_CAP_DSC_15BPP		(1 << 13)
+/* Force EDID RGB color display*/
+#define EDID_QUIRK_FORCE_RGB                    (1 << 14)
+/* Force EDID disable YCbCr color formats */
+#define EDID_QUIRK_DISABLE_YCBCR                (1 << 15)
 
 #define MICROSOFT_IEEE_OUI	0xca125c
 
@@ -150,6 +154,9 @@ static const struct edid_quirk {
 	/* BOE model 0x0771 reports 8 bpc, but is a 6 bpc panel */
 	EDID_QUIRK('B', 'O', 'E', 0x0771, EDID_QUIRK_FORCE_6BPC),
 
+    /* Dell U2413 reports support for YCbCr and Monochrome but is a RGB color display */
+    EDID_QUIRK('D', 'E', 'L', 61512, EDID_QUIRK_FORCE_RGB | EDID_QUIRK_DISABLE_YCBCR),
+
 	/* Belinea 10 15 55 */
 	EDID_QUIRK('M', 'A', 'X', 1516, EDID_QUIRK_PREFER_LARGE_60),
 	EDID_QUIRK('M', 'A', 'X', 0x77e, EDID_QUIRK_PREFER_LARGE_60),
@@ -6781,6 +6788,16 @@ static void update_display_info(struct drm_connector *connector,
 	if (info->quirks & EDID_QUIRK_FORCE_12BPC)
 		info->bpc = 12;
 
+    if (info->quirks & EDID_QUIRK_FORCE_RGB) {
+        /* Force RGB by setting appropriate color format */
+        info->color_formats = DRM_COLOR_FORMAT_RGB444;
+    }
+
+    if (info->quirks & EDID_QUIRK_DISABLE_YCBCR) {
+        /* Disable YCbCr support */
+        info->edid_hdmi_ycbcr444_dc_modes = 0;
+    }
+
 	/* Depends on info->cea_rev set by drm_parse_cea_ext() above */
 	drm_edid_to_eld(connector, drm_edid);
 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment