In this post I will explain how to force the AMDGPU driver to use 10-bit when the display does not support YUV 4:4:4 10-bit at 2160p 60Hz.
The AMDGPU driver prefers 8-bit modes instead of 10-bit when the display does not support 10-bit without chroma subsampling at the requested resolution and refresh rate.
For example, at 2160p 60Hz, my TV only supports either YUV 4:4:4 8-bit or YUV 4:2:0 10-bit.
The AMDGPU driver will automatically select YUV 4:4:4 8-bit in this case, which makes sense for desktop PC use because chroma subsampling makes text blurry and less readable (see this rtings.com article for more information).
But for video playback, because practically all media is YUV 4:2:0 (DVD, BD, streaming, etc), it doesn’t matter. YUV 4:2:0 10-bit is even preferable, because players like mpv can deband 8-bit video and output a slightly better looking 10-bit video. Some SDR streaming content also comes as HEVC with YUV 4:2:0 10-bit color.
I haven’t noticed this thus far because I had the refresh rate of my HTPC set to 23.976Hz (and sometimes 24Hz or 29.97Hz, automatically) for movie and series consumption.
Because the TV supports YUV 4:4:4 10-bit at this refresh rate, the 10-bit mode was automatically selected.
Things change at 60Hz, as in the example at the start of the post, the driver selects the 8-bit mode with less chroma subsampling instead of 10-bit.
The AMDGPU driver has a setting to force YUV 4:2:0 and it will then always pick 10-bit over 8-bit if possible.
Interestingly, even if “forced” to use YUV 4:2:0, it seems to still select YUV 4:4:4 10-bit when it can. With my TV, at 50 and 60Hz it will pick YUV 4:2:0 10-bit and at 30Hz and below it picks YUV 4:4:4 10-bit.
You can enable it by writing
1 (or disable it by writing
/sys/kernel/debug/dri/0/HDMI-A-1/force_yuv420_output (change the GPU number, for me
0 because I only have one, and HDMI port, in my case it is
HDMI-A-1 with the Ryzen 3200G iGPU)
$ echo 1 | sudo tee /sys/kernel/debug/dri/0/HDMI-A-1/force_yuv420_output
The change takes effect after the next time the display is re-plugged or the display mode is changed.
You can also turn the display off and on again with xrandr:
$ xrandr --output HDMI-A-0 --off && xrandr --output HDMI-A-0 --auto
I am not aware of a way to make it permanent via configuration files or kernel parameters, but you can, for example, create a systemd service that enables it at boot.
Create the systemd service:
sudo nano /etc/systemd/system/force_yuv420.service
Description=Forces YUV 4:2:0
ExecStart=/bin/bash -c 'echo 1 > /sys/kernel/debug/dri/0/HDMI-A-1/force_yuv420_output'
sudo systemctl enable force_yuv420.service to enable it. It should now be applied automatically during boot.