diff options
| author | Krzysztof Nazarewski <gpg@kdn.im> | 2024-04-18 13:33:19 +0200 |
|---|---|---|
| committer | K900 <me@0upti.me> | 2024-07-18 16:57:18 +0300 |
| commit | 8559b460b1caf1b619ef25ef9bb251cfa8282db9 (patch) | |
| tree | 232072c21158184a99cbe72c41298c00c2437413 | |
| parent | linuxhw-edid-fetcher: init at unstable-2023-05-08 (diff) | |
| download | nixpkgs-8559b460b1caf1b619ef25ef9bb251cfa8282db9.tar.gz | |
edido: init
| -rwxr-xr-x | pkgs/by-name/ed/edido/edido.sh | 176 | ||||
| -rw-r--r-- | pkgs/by-name/ed/edido/package.nix | 19 |
2 files changed, 195 insertions, 0 deletions
diff --git a/pkgs/by-name/ed/edido/edido.sh b/pkgs/by-name/ed/edido/edido.sh new file mode 100755 index 000000000000..136205386ee1 --- /dev/null +++ b/pkgs/by-name/ed/edido/edido.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env bash +set -eEuo pipefail +test -z "${DEBUG:-}" || set -x +set -eEuo pipefail + +FIRMWARE_PATH="${EDID_PATH:-"/run/current-system/firmware"}" +mapfile -t edid_paths <<<"${FIRMWARE_PATH//":"/$'\n'}" + +err() { + LOGGER="ERROR" log "$@" + return 1 +} + +log() { + # shellcheck disable=SC2059 + printf "[${LOGGER:-"INFO"}] $1\n" "${@:2}" >&2 +} + +find_path() { + local filePath="$1" + mapfile -t candidates < <( + set -x + find -L "${@:2}" -path "*/${filePath}" + ) + if test "${#candidates[@]}" -eq 0; then + log "'%s' path not found" "${filePath}" + return 1 + fi + log "'%s' path found at %s" "${filePath}" "${candidates[0]}" + echo -n "${candidates[0]}" +} + +wait_for_file() { + local filePath="$1" + until find_path "${filePath}" "${@:2}"; do + backoff "${filePath}" + done +} + +backoff() { + local what="$1" sleepFor + + backoff_start="${backoff_start:-"5"}" + backoff_current="${backoff_current:-"${backoff_start}"}" + backoff_jitter_multiplier="${backoff_jitter_multiplier:-"0.3"}" + backoff_multiplier="${backoff_multiplier:-1.5}" + + sleepFor="$(bc <<<"${backoff_current} + ${RANDOM} % (${backoff_current} * ${backoff_jitter_multiplier})")" + + log "still waiting for '%s', retry in %s sec..." "${what}" "${sleepFor}" + sleep "${sleepFor}" + backoff_current="$(bc <<<"scale=2; ${backoff_current} * ${backoff_multiplier}")" + +} + +force_mode() { + local connPath="$1" newMode="$2" currentMode + currentMode="$(cat "$connPath/force")" + if test "${currentMode}" == "${newMode}"; then + log "video mode is already '%s'" "${currentMode}" + return + fi + log "changing video mode from '%s' to '%s'" "${currentMode}" "${newMode}" + echo "${newMode}" >"$connPath/force" + CHANGED=1 +} + +force_edid() { + local connPath="$1" edidPath="$2" +} + +apply_mode() { + local connPath="$1" mode="$2" + test -n "$mode" || return + log "setting up fb mode..." + + # see https://github.com/torvalds/linux/blob/8cd26fd90c1ad7acdcfb9f69ca99d13aa7b24561/drivers/gpu/drm/drm_sysfs.c#L202-L207 + # see https://docs.kernel.org/fb/modedb.html + case "${mode}" in + *d) force_mode "$connPath" off ;; + *e) force_mode "$connPath" on ;; + *D) force_mode "$connPath" on-digital ;; + esac +} + +apply_edid() { + local connPath="$1" edidFilename="$2" edidPath + test -n "${edidFilename}" || return + log "loading EDID override..." + edidPath="$(find_path "${edidFilename}" "${edid_paths[@]/%/"/"}" -maxdepth 2)" + + force_edid "${connPath}" "$edidPath" + cat "$edidPath" >"${connPath}/edid_override" + + if cmp "${connPath}/edid_override" "${edidPath}" &>/dev/null; then + log "EDID is already up to date with '%s'" "${edidPath}" + else + log "applying EDID override from ${edidPath}" + cat "$edidPath" >"${connPath}/edid_override" + CHANGED=1 + fi +} + +load() { + local conn="$1" edidFilename="$2" mode="$3" + export LOGGER="$conn:${edidFilename}:$mode" + CHANGED="${CHANGED:-0}" + + log "starting configuration" + local connPath + connPath="$(wait_for_file "$conn" /sys/kernel/debug/dri/ -maxdepth 2 -type d)" + apply_edid "${connPath}" "${edidFilename}" + apply_mode "${connPath}" "$mode" + + if test "${CHANGED}" != 0; then + log "changes detected, triggering hotplug" + echo 1 >"${connPath}/trigger_hotplug" + else + log "no changes detected, skipping hotplug trigger" + fi +} + +main() { + if [[ $EUID -ne 0 ]]; then + err "must be run as root" + fi + + if test "$#" == 0; then + log "loading kernel parameters from /proc/cmdline" + # replace script arguments with kernel parameters + mapfile -t args < <(xargs -n1 </proc/cmdline) + else + log "loading kernel parameters compatible arguments from commandline" + args=("$@") + fi + + local -A edids modes connectors + local -a entries + local key value + + for arg in "${args[@]}"; do + key="${arg%%=*}" + value="" + test "${key}" == "${arg}" || value="${arg#*=}" + + case "${key}" in + video) + # one argument per connector: + # video=DP-4:e video=DP-1:e + connector="${value%:*}" + mode="${value#*:}" + connectors["${connector}"]="" + modes["$connector"]="$mode" + ;; + drm.edid_firmware) + # single argument for all connectors: + # drm.edid_firmware=DP-4:edid/one.bin,DP-1:edid/two.bin + mapfile -t entries <<<"${value//","/$'\n'}" + for entry in "${entries[@]}"; do + connector="${entry%:*}" + edidFilename="${entry#*:}" + connectors["${connector}"]="" + edids["${connector}"]="${edidFilename}" + done + ;; + esac + done + + for connector in "${!connectors[@]}"; do + # spawn in a subshell to easily adjust and runtime modify global variables + (load "${connector}" "${edids["${connector}"]:-""}" "${modes["${connector}"]:-""}") & + done + wait +} + +main "$@" diff --git a/pkgs/by-name/ed/edido/package.nix b/pkgs/by-name/ed/edido/package.nix new file mode 100644 index 000000000000..2d1f69e6a740 --- /dev/null +++ b/pkgs/by-name/ed/edido/package.nix @@ -0,0 +1,19 @@ +{ lib +, writeShellApplication +, bc +, diffutils +, findutils +, coreutils +, firmwarePaths ? [ + "/run/current-system/firmware" + ] +}: +writeShellApplication { + name = "edido"; + meta.description = "A tool to apply display configuration from `boot.kernelParams`."; + runtimeInputs = [ diffutils findutils coreutils bc ]; + text = '' + FIRMWARE_PATH="''${FIRMWARE_PATH:-"${builtins.concatStringsSep ":" firmwarePaths}"}" + ${builtins.readFile ./edido.sh} + ''; +} |
