I’m a nerd so I have a computer plugged into my TV. I leave the computer on all the time so it can get my linux ISOs whenever. Then when I want to watch something I turn my TV on and it’s all ready to go.

An update to Pop!_OS suddenly meant that the PC’s picture wouldn’t return when the TV turned on. What worked for a decade now doesn’t. I found this difficult to Google

linux screen off when screen on
linux intel graphics HDMI no picture, sometimes
HDMI doesn't wake up no I'm not talking about the computer being asleep
help me please god

So WTF is going on? I discovered physically replugging the HDMI cable would return the picture, so at least it doesn’t totally break. The ol` TTY switch with ctrl-alt-F2 and back to desktop would return the screen, though logged out.

I found an incantation in /sys that would output the display status.

$ cat /sys/class/drm/card0-HDMI-A-1/status
connected

Using SSH I can see what linux thinks is happening, screen-wise.

  • When on, it says connected. 👌
  • When I turn the TV off, it says connected still 🤔
  • When I turn the TV back on, it says disconnected 😡

The only clue of what’s going wrong is a dmesg that seems to happen whenever the TV turns off or on:

i915 0000:00:02.0: [drm] HPD interrupt storm detected on connector HDMI-A-1: switching from hotplug detection to polling

I have no idea what the real problem is and where in the linux graphics stack it is occurring, and I’m not going to figure it out. Instead, I know I can tell there’s a problem via the shell, and can recover the screen with TTY switching, surely I can write a crappy script to work around this issue?

I had a few false starts figuring out the best way to soft reboot the graphics system to notice the TV is actually there and waiting for a picture. I learnt that chvt is a terminal program that can switch TTYs. And on top of X, the graphics driver and DRM, gnome has it’s own display manager (GDM). If you try to restart it your computer will crash.

The best solution was actually back where I started. Remember that status file that was gaslighting me about my TV’s status? Turns out sometimes, just sometimes, the idea that everything is a file in unix is actually implemented, and I can write to that same file. I can’t write connected, because one can’t simply confront an emotional abuser with the truth. Instead I can write detect to softly suggest to the system to please have another look at the HDMI output. This way the system doesn’t suffer a narcissistic injury.

$ echo detect > /sys/class/drm/card0-HDMI-A-1/status

This brings back the screen no problem.

How to know when to run this therapy?

On my quest to discover WTF was going on, I learnt of another linux thingy, udev. It’ll notice when hardware changes and let you trigger scripts. Check out this article for a way better guide. In terms of just fixing my problem, running

$ sudo udevadm monitor -p

will print out events as they happen. When my TV turns on the following event occurs:

UDEV  [7475.511783] change   /devices/pci0000:00/0000:00:02.0/drm/card0 (drm)
ACTION=change
DEVPATH=/devices/pci0000:00/0000:00:02.0/drm/card0
SUBSYSTEM=drm
HOTPLUG=1
DEVNAME=/dev/dri/card0
DEVTYPE=drm_minor
SEQNUM=4068
USEC_INITIALIZED=2913943
ID_PATH=pci-0000:00:02.0
ID_PATH_TAG=pci-0000_00_02_0
ID_FOR_SEAT=drm-pci-0000_00_02_0
MAJOR=226
MINOR=0
DEVLINKS=/dev/dri/by-path/pci-0000:00:02.0-card
TAGS=:seat:mutter-device-disable-kms-modifiers:uaccess:master-of-seat:
CURRENT_TAGS=:seat:master-of-seat:uaccess:mutter-device-disable-kms-modifiers:

So cool that the kernel and DRM detect the TV turning on, then does fuck all about it. To define the script to run, we of course write a configuration file in some rando folder in a rando format because this is unix baby!

$ cat /usr/lib/udev/rules.d/99-displayfix.rules
SUBSYSTEM=="drm", ACTION=="change", RUN+="/home/screenfix.sh"

It has to be a single line, with double =, which my blog’s font squishes into a single ==. See how the first two are from the udev event output? Think you can specify the DEVNAME to narrow down the script execution? lol of course not, the rules file only accepts a random assortment of values, not at all matching what udevadm outputs. You thought this was written by rational people? I’m not even going to get into why RUN has a +.

Final piece of the puzzle is the screenfix.sh script that will make everything better again.

$ cat /home/screenfix.sh
#!/usr/bin/bash
# Wait a second for the 'interupt storm' to pass
sleep 1

# Check if the TV reports disconnected, that's ironicly how we know it's actually connected
state=$(</sys/class/drm/card0-HDMI-A-1/status)
if [ "$state" = "disconnected" ]; then
	# The magic words to wake Jeff I mean the display up.
	echo detect > /sys/class/drm/card0-HDMI-A-1/status
fi

Just gotta tell udevadm to reload it’s rules, and now I can again enjoy the basic functionality that was robbed from me.

sudo udevadm control --reload
Jeff violently awakening