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:
- https://devtalk.nvidia.com/default/topic/1024022/linux/gtx-1060-no-audio-over-hdmi-only-hda-intel-detected-azalia/post/5211273/#5211273
- https://bugs.freedesktop.org/show_bug.cgi?id=75985
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):
#!/bin/bash
NVHDA_FILE="/proc/acpi/nvhda"
# Check all HDMI connections
while IFS= read -r; do
DRIVER=$(realpath "$(dirname "$REPLY")"/../device/driver)
if [ "$(basename "$DRIVER")" = "nvidia" ]; then
HDMI_CONNECTED=1
break
fi
done < <(grep -l ^connected /sys/class/drm/*HDMI*/status)
NVHDA_STATUS="$(cat $NVHDA_FILE)"
NVHDA_STATUS="${NVHDA_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"
fi
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.