Fixing sound through HDMI on NVIDIA Optimus

There is a common problem with those NVIDIA Optimus laptops having an HDMI port connected to the dedicated GPU: NVIDIA audio device is not detected thus no sound goes over HDMI port.

For more details see:

There is a useful workaround for that - separate module controlling power state of NVIDIA audio device called nvhda. One can get more details on this in project README.

However when using tools like bbswitchd / bbswitch-gui it turns out that to make it work correctly it’s necessary to detect whether external screen is connected over HDMI and nvidia kernel module is loaded.

Then it will be possible to keep audio device turned off when NVIDIA GPU is not in use and no external screen connected and enable audio device only when it is necessary thus extending laptop battery life.

I’ve created simple script for that purpose (gist):



# Check all HDMI connections
while IFS= read -r; do
    DRIVER=$(realpath "$(dirname "$REPLY")"/../device/driver)
    if [ "$(basename "$DRIVER")" = "nvidia" ]; then
done < <(grep -l ^connected /sys/class/drm/*HDMI*/status)

if [ -n "$HDMI_CONNECTED" ] && [ "$NVHDA_STATUS" == "OFF" ]; then
    echo "ON" > "$NVHDA_FILE"
    echo "HDA NVidia: turned on"
elif [ -z "$HDMI_CONNECTED" ] && [ "$NVHDA_STATUS" == "ON" ]; then
    echo "OFF" > "$NVHDA_FILE"
    echo "HDA NVidia: turned off"

It simply checks if something is connected to an HDMI port while nvidia driver is in use and then turns NVIDIA audio device on / off respectively.

To automate this fixup we can write a simple udev rule (assuming the script above is located in /usr/local/bin/nvhda-fixup):

# Reload HDA NVidia device on HDMI connect
ACTION=="change", SUBSYSTEM=="drm", RUN+="/usr/local/bin/nvhda-fixup"

Save this script as 10-nvhda-fixup.rules and put inside /etc/udev/rules.d/.

Then it will be automatically called when something is attached or detached from HDMI port.