Set full RGB to HDMI monitor on Ubuntu

 Posted by:   Posted on:   Updated on:  2021-08-02T19:34:58Z

Fix washed out colors on Linux with Xorg/Wayland display server and Intel/AMD video cards

Most common monitors nowadays have 1920x1080 resolution, same as HD TVs. When this resolution is available, some video card drivers, on Linux, will output limited RGB pixel ranges, considering the monitor as a TV. Instead of 8-bit per color (0 to 255), the video card outputs a limited range of 16 to 235 which causes washed out colors, with less bright white and less dark black. This may not be obvious at first sight, especially for regular users when switching to a new monitor.

Unfortunately, there is no straight fix for this issue on Ubuntu (or other Linux distributions). There are no options in settings app. More than that, the way of fixing this has changed with the new Ubuntu 21.04 which uses Wayland instead of Xorg display server. In this post I will show you how to address this issue on both display servers and get the expected picture colors on your HDMI monitor with Ubuntu.

Set full RGB to HDMI monitor on Ubuntu

I have experienced this issue on Intel UHD Graphics 630, but there are also reports about AMD cards. Ubuntu users with NVIDIA cards should be able to change this setting from NVIDIA Settings.

Display server

The first thing to do is check whether your distribution uses Xorg (xrandr) or Wayland. You can get information about the running display server with the following command[1]:

ps -e | grep tty

Or, at least on Ubuntu, you may try to use xrandr to detect monitors.

xrandr --listmonitors

If the first command reports gdm-wayland-ses or the second XWAYLAND0, then you're using Wayland. Otherwise, if you see Xorg in the output of first, then you will be using xrandr to configure monitors. Following is the fix for both servers. Follow only the instructions for your server!

For reference, here is my output on Ubuntu 21.04 Wayland:

~$ xrandr --listmonitors
Monitors: 1
 0: +*XWAYLAND0 1920/520x1080/290+0+0  XWAYLAND0

And here is the output when I switch to Xorg:

~$ xrandr --listmonitors
Monitors: 1
 0: +*HDMI-1 1920/521x1080/293+0+0  HDMI-1

Xorg

Identify monitor connections using the above command to list monitors. In my case, the "connector" is XWAYLAND0, however I cannot change display properties using xrandr on Wayland. If using Xorg, connector names can be: HDMI1, HDMI-1, HDMI-1-1, HDMI-A-1, LVDS1, LVDS-1, DP1, DP-1 etc. Open a terminal as regular user and run one of the following commands depending on video card. Make sure you adjust connector name.

  • For Intel:
xrandr --output HDMI-1 --set "Broadcast RGB" "Full"

Picture quality problems appear whenever there is a mismatch between what the video card outputs and what the monitor expects/supports. Just in case, the limited range is selected with:

xrandr --output HDMI-x --set "Broadcast RGB" "Limited 16:235"
  • For AMD:
xrandr --output HDMI-x --set "output_csc" "bypass"

If this doesn't get you proper picture quality, try other settings[2]:

xrandr --output HDMI-x --set "output_csc" "tvrgb"
xrandr --output HDMI-x --set "output_csc" "ycbcr601"
xrandr --output HDMI-x --set "output_csc" "ycbcr709"

The result is instant, but not permanent. After reboot, it reverts to default mode. But we can create a script, write this command in it and run it at every boot[3]. It is easier to add this command to Startup Applications. Launch it (gnome-session-properties) add add your command. Write whatever you want in the other fields.

Add xrandr display setting command to Startup Applications

Add xrandr display setting command to Startup Applications

This should be all for Xorg users.

Wayland

Wayland server makes this setting more difficult to change and dangerous. This part is based on the instructions provided by Brad T. Laue (see reference [4]). Let's install the prerequisites (libdrm-tests):

sudo apt update && sudo apt install libdrm-tests

Run proptest in a regular terminal. Here is a truncated output of my device:

trying to open device 'i915'...done
Connector 74 (HDMI-A-1)
	1 EDID:
		flags: immutable blob
		blobs:

		value:
			00ffffffffffff00410cd1c0668a0000
			...
			000000000000000000000000000000f9
	...
	76 Broadcast RGB:
		flags: enum
		enums: Automatic=0 Full=1 Limited 16:235=2
		value: 1
	...
Connector 84 (HDMI-A-2)
	1 EDID:
		flags: immutable blob
		blobs:

		value:
	...
	76 Broadcast RGB:
		flags: enum
		enums: Automatic=0 Full=1 Limited 16:235=2
		value: 0
...

Let's see what is important here:

  • The device name: i915 (this is the name of the Intel Graphics driver for Linux)
  • The connector IDs: 74 for HDMI-A-1 with non-empty EDID. Where EDID data exists, a monitor is connected. There is nothing connected to HDMI-A-2 (connector 84).
  • The property ID: 76 for Broadcast RGB (property name would have been output_csc on AMD card).
  • The possible values for the property: 0, 1 and 2. Note value 1 already set by me to connector 74.

With this output in the terminal, open a text editor and let's make the correct command. The proptest utility gets installed to /usr/bin. The syntax is:

proptest -M <driver_name> -D <device> <ID_of_connector> connector <ID_of_property> <value>

Connector and property numbers vary depending on hardware. So, in my case, for the connected monitor:

/usr/bin/proptest -M i915 -D /dev/dri/card0 74 connector 76 1

Picture quality problems appear whenever there is a mismatch between what the video card outputs and what the monitor expects/supports. Therefore you should try value 2 if you don't get desired results. But, how do you get the results? Running the command in terminal does nothing. That is because it must run before display manager starts. Note that running the command in TTY console will have immediate results.

Warning! Improper edits of the display manager service configuration will prevent it from starting. Therefore, you will have to revert the changes you made from TTY console (or reinstall Ubuntu).

In /etc/systemd/system there is a file display-manager.service. On Ubuntu 21.04, this is a link to /lib/systemd/system/gdm3.service, so this is the file I will edit. First of all, a backup is highly recommended. Then, we can use nano to add this command to be executed before GDM starts.

sudo cp /lib/systemd/system/gdm3.service /lib/systemd/system/gdm3.service.bak
sudo nano /lib/systemd/system/gdm3.service

Find [Service] section and add an extra ExecStartPre entry before any other entries.

ExecStartPre=-/usr/bin/proptest -M i915 -D /dev/dri/card0 74 connector 76 1

Adjust this command as needed, with the correct driver name, connector and property IDs. Here is my modified GDM service configuration:

Modified GDM3 service configuration for proptest

Modified GDM3 service configuration for proptest

Commands from ExecStartPre are executed before the main process starts. Multiple entries are allowed and commands are executed in order, one after the other. The - in front of the executable to run is very important because it tells systemd to continue in case the command returns an error code. Otherwise, the service would not start.

The proptest utility uses DRM_IOCTL to change GPU settings. DRM is the acronym for Direct Rendering Manager.

This should be all. Reboot and see the difference.

Troubleshooting

  • Nothing changed

Run proptest again. What is the value of Broadcast RGB/output_csc parameter? If it is still default 0, you must see what went wrong with the command. In a terminal, run systemctl status gdm3. The output will be something like:

 gdm3.service - GNOME Display Manager
     Loaded: loaded (/lib/systemd/system/gdm3.service; indirect; vendor preset: enabled)
     Active: active (running) since Sun 2021-08-01 23:31:40 EEST; 13min ago
    Process: 2460 ExecStartPre=/usr/bin/proptest -M i915 -D /dev/dri/card0 74 connector 76 1 (code=exited, status=0/SUCCESS)
    Process: 2461 ExecStartPre=/usr/share/gdm/generate-config (code=exited, status=0/SUCCESS)
    Process: 2464 ExecStartPre=/usr/libexec/gdm-wait-for-drm (code=exited, status=0/SUCCESS)
   Main PID: 2465 (gdm3)
      Tasks: 3 (limit: 9306)
     Memory: 2.9M
     CGroup: /system.slice/gdm3.service
             └─2465 /usr/sbin/gdm3

If the error code is not 0, further investigation is needed.

  • Credentials error

Nothing is changed and the when you check the status of GDM, you find the following error, with exit code 243:

    Process: 815 ExecStartPre=/usr/bin/proptest -M i915 -D /dev/dri/card0 74 connector 76 1 (code=exited, status=243/CREDENTIALS)

I have no idea at this time how to fix this issue. All I can tell you is that if you restart GDM it will work for the current session. Use:

sudo service gdm3 restart
  • GDM no longer starts

This is the worst case. Press Ctrl+Alt+Fx, where Fx can be anything from F3 to F6 to get to console. Log in by entering username and password. Then you may use either nano as you did previously and remove the line you added or you may restore the original file. Here are the commands for both of these approaches:

sudo rm /lib/systemd/system/gdm3.service
sudo cp /lib/systemd/system/gdm3.service.bak /lib/systemd/system/gdm3.service

sudo nano /lib/systemd/system/gdm3.service

Reboot with sudo reboot or attempt to (re)start GDM3.

Conclusions

Although it is common nowadays to connect the PC to monitor using HDMI, the method of adjusting display color range on Linux is still difficult. More than that, the methods depend on video card type, display server and sometimes the results are inconsistent.

The method for Wayland worked for me a few times, it now fails without me changing anything. I'm getting the credentials error exit code.

What else to do? Until a permanent fix appears, some of you can try to use a different connection method. I could use a DVI cable since both PC and monitor support it. I could also use a HDMI to DVI cable/adapter.

References

  1. loved.by.Jesus. Answer on How to get information about which display server is running? on Unix & Linux StackExchange
  2. George. Answer on xrandr — how to find the correct RGB full spectrum output command for my system on AskUbuntu StackExchange
  3. Tejas Lotlikar. Answer on How to save xrandr options? on AskUbuntu StackExchange
  4. Brad T. Laue. Quick Tip: Setting the Color Space Value in Wayland (2017).

13 comments :

  1. This is super helpful, my eyes can rest now
    I've tried writing script under /etc/X11/Xsession.d but didn't work

    ReplyDelete
  2. The Wayland method seems does not work for AMD GPUs.

    ReplyDelete
  3. The Xorg method also doesn't work for AMD GPUs.

    ReplyDelete
  4. Is there a way to change this setting on Nvidia using the open source Nouveau drivers?
    My machine always outputs Full range, but I need to set it to Limited so it will look correct on my TV.

    Every article and forum thread I've found on this topic, only talk about how to do it with Intel and AMD, but absolutely nothing about Nouveau.

    (I can't use the proprietary Nvidia driver, because I'm using Retroarch in DRM/KMS mode, which only works with Nouveau. Also, the Nvidia drivers are quite buggy in my experience.)

    ReplyDelete
  5. Hi! I think I found a solution to (y)our (code=exited, status=243/CREDENTIALS) error. Bear in mind: I'm on Fedora 35 GNOME, I have no other way to test this solution, even a VM is useless: any feedback is welcome!

    I followed more or less your approach, but instead of editing directly gdm.service I created a new service from scratch only to launch proptest. Oh, BTW, editing services in /usr/lib/ like you did is discouraged, as these files may change with distro updates: you should always create drop-in files with "systemctl edit service_name.service").

    That said, here's the proptest.service content (placed in /etc/systemd/system/):

    [Unit]
    Description=Launch proptest and set fullRGB by default

    # Hard dependency: "Start proptest before starting gdm"
    After=systemd-user-sessions.service
    Before=gdm.service

    [Service]
    ExecStart=-/usr/bin/proptest -M i915 -D /dev/dri/card0 85 connector 80 1

    [Install]
    WantedBy=multi-user.target

    The "magic" is in the line "After=systemd-user-sessions.service".
    Honestly, I cannot understand deeply in detail why my line solves the issue. Like, I get that the problem is somewhat related to having a session already opened before the display manager starts, but other than that I sincerely don't understand why your solution (which seems to do the same) does not work.

    In any case, let me know if this works for you, if it does it would be really awesome having contributed to your blog.

    (And thank you for this post, without it I would have never tried to solve the issue!)

    ReplyDelete
    Replies
    1. Awesome! Thank you very much! I will try this and see if it works for me too.

      Delete
    2. Thanks a lot! Fedora 38 Works great

      Delete
    3. Thank you so much! It changed to 1 now!

      Delete
  6. It seems that (code=exited, status=243/CREDENTIALS) error has nothing to do with credentias. It is just the exit code of /usr/bin/proptest and systemd assuming it follows the convention for exit status codes (https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.21.8)

    ReplyDelete
  7. It seems that somehow proptest at boot conflicts with plymouth-start.service. I edited proptest.service and now it seems to work consistently:

    [Unit]
    Description=Launch proptest and set fullRGB by default
    DefaultDependencies=no
    After=systemd-modules-load.service
    Before=plymouth-start.service

    [Service]
    ExecStart=-/usr/bin/proptest -M i915 125 connector 98 1

    ReplyDelete
  8. I found a solution: use proptest to set full RGB.
    Steps in Ubuntu:
    sudo apt install libdrm-tests
    Create an udev trigger:
    create file "/etc/udev/rules.d/80-local.rules" with content (replace CONNECTOR with connector number, which can be found executing "proptest" command):
    ACTION=="add", SUBSYSTEM=="module", KERNEL=="i915", RUN+="/usr/bin/proptest -M i915 CONNECTOR connector 98 1"
    This Udev rule execute proptest command right after i915 module is loaded.

    ReplyDelete
  9. I found a solution: use proptest to set full RGB.
    Steps in Ubuntu:
    sudo apt install libdrm-tests
    Create an udev trigger:
    create file "/etc/udev/rules.d/80-local.rules" with content (replace CONNECTOR with connector number, which can be found executing "proptest" command):
    ACTION=="add", SUBSYSTEM=="module", KERNEL=="i915", RUN+="/usr/bin/proptest -M i915 CONNECTOR connector 98 1"
    This Udev rule execute proptest command right after i915 module is loaded.

    ReplyDelete
  10. Better way of doing it and works for Arch and also on other distros. https://www.wezm.net/v2/posts/2020/linux-amdgpu-pixel-format/#the-fix

    ReplyDelete

Please read the comments policy before publishing your comment.