#!/bin/bash

set -e

# Use the Vfio to Integrated trick to turn off NVIDIA dgpu when in integrated mode
# without needing to restart the computer. This is needed because computers like the Asus G14
# will wake after suspend in Hybrid mode, even if the system was in Integrated mode before
# suspending.

restore_marker=/run/blob-force-igpu-integrated
sleep_action=${SYSTEMD_SLEEP_ACTION:-$2}
[[ -x /usr/bin/supergfxctl ]] || exit 0

switch_mode() {
  local expected="$1" current

  if ! /usr/bin/timeout --kill-after=1s 3s /usr/bin/supergfxctl -m "$expected"; then
    echo "Could not request the GPU transition to $expected mode" >&2
    return 1
  fi
  for _ in {1..10}; do
    if current=$(/usr/bin/timeout --kill-after=1s 2s /usr/bin/supergfxctl -g 2>/dev/null) &&
      [[ $current == "$expected" ]]; then
      return 0
    fi
    sleep 1
  done

  echo "Could not confirm the GPU transition to $expected mode" >&2
  return 1
}

case "$1" in
  pre)
    # Remember the mode this sleep cycle started in. supergfxctl persists the
    # temporary hibernate switch to Vfio, so post must not consult that mutable
    # value when deciding whether to restore Integrated mode.
    if [[ -L $restore_marker ]]; then
      exit 1
    elif [[ ! -f $restore_marker ]]; then
      /usr/bin/grep -Eq '"mode"[[:space:]]*:[[:space:]]*"Integrated"' /etc/supergfxd.conf 2>/dev/null || exit 0
      /usr/bin/install -m 0600 -o root -g root -T /dev/null "$restore_marker"
    fi

    # Before hibernating, switch to Vfio so the nvidia driver is detached from the dGPU.
    # Without this, hibernate resume fails because the nvidia driver can't freeze a
    # powered-off dGPU (returns -EIO), which aborts the entire resume.
    if [[ $sleep_action == "hibernate" ]]; then
      switch_mode Vfio
    fi
    ;;
  post)
    [[ -f $restore_marker && ! -L $restore_marker ]] || exit 0

    # small delay so the device is fully re-enumerated
    sleep 4

    # force-bind dGPU to vfio (fully detached from nvidia)
    switch_mode Vfio

    # then go back to Integrated, which powers it off again
    switch_mode Integrated
    /usr/bin/rm -f -- "$restore_marker"
    ;;
esac
