#!/bin/sh
# tlp-func-gpu - Intel GPU Functions
#
# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
# This software is licensed under the GPL v2 or later.

# Needs: tlp-func-base

# shellcheck disable=SC2086

# ----------------------------------------------------------------------------
# Constants

readonly BASE_MODD=/sys/module
readonly BASE_DRMD=/sys/class/drm
readonly BASE_DEBUGD=/sys/kernel/debug/dri

readonly IGPU_MIN_FREQ=gt_min_freq_mhz
readonly IGPU_MAX_FREQ=gt_max_freq_mhz
readonly IGPU_BOOST_FREQ=gt_boost_freq_mhz
readonly IGPU_FREQ_TABLE=i915_ring_freq_table

readonly RADD=/sys/module/radeon

readonly DEFAULT_RADEON_POWER_PROFILE_ON_AC=default
readonly DEFAULT_RADEON_POWER_PROFILE_ON_BAT=default
readonly DEFAULT_RADEON_DPM_PERF_LEVEL_ON_AC=auto
readonly DEFAULT_RADEON_DPM_PERF_LEVEL_ON_BAT=auto

# ----------------------------------------------------------------------------
# Functions

# --- Intel GPU

check_intel_gpu () { # detect Intel GPU presence and determine sysdirs
    # rc: 0=present/1=absent
    # retval: $_intel_gpu_parm: card parameter sysdir;
    #         $_intel_gpu_drm:  card drm sysdir;
    #         $_intel_gpu_dbg:  card debug sysdir
    local cardno driver gpu

    _intel_gpu_parm=""
    _intel_gpu_drm=""
    _intel_gpu_dbg=""

    for gpu in ${BASE_DRMD}/card?; do
        driver=$(readlink ${gpu}/device/driver)
        driver=${driver##*/}
        case $driver in
            i915*) # Intel GPU found
                cardno=${gpu##${BASE_DRMD}/card}
                _intel_gpu_parm=${BASE_MODD}/${driver}/parameters
                _intel_gpu_drm=${gpu}
                _intel_gpu_dbg=${BASE_DEBUGD}/${cardno}
                echo_debug "pm" "check_intel_gpu.present: parm=$_intel_gpu_parm; drm=$_intel_gpu_drm; dbg=$_intel_gpu_dbg"
                return 0
                ;;
        esac
    done

    # no Intel GPU found
    echo_debug "pm" "check_intel_gpu.no_card"
    return 1
}

set_intel_gpu_min_max_boost_freq () { # set gpu frequency limits -- $1: 0=ac mode, 1=battery mode
    local minfreq maxfreq boostfreq
    local conf=0

    check_intel_gpu || return 0

    if [ "$1" = "1" ]; then
        minfreq=${INTEL_GPU_MIN_FREQ_ON_BAT:-}
        maxfreq=${INTEL_GPU_MAX_FREQ_ON_BAT:-}
        boostfreq=${INTEL_GPU_BOOST_FREQ_ON_BAT:-}
    else
        minfreq=${INTEL_GPU_MIN_FREQ_ON_AC:-}
        maxfreq=${INTEL_GPU_MAX_FREQ_ON_AC:-}
        boostfreq=${INTEL_GPU_BOOST_FREQ_ON_AC:-}
    fi

    if [ -n "$minfreq" ] && [ "$minfreq" != "0" ]; then
        write_sysf "$minfreq" $_intel_gpu_drm/$IGPU_MIN_FREQ
        echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).min: $minfreq; rc=$?"
        conf=1
    fi

    if [ -n "$maxfreq" ] && [ "$maxfreq" != "0" ]; then
        write_sysf "$maxfreq" $_intel_gpu_drm/$IGPU_MAX_FREQ
        echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).max: $maxfreq; rc=$?"
        conf=1
    fi

    if [ -n "$boostfreq" ] && [ "$boostfreq" != "0" ]; then
        write_sysf "$boostfreq" $_intel_gpu_drm/$IGPU_BOOST_FREQ
        echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).boost: $boostfreq; rc=$?"
        conf=1
    fi

    [ $conf -eq 1 ] ||  echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).not_configured"
    return 0
}

# --- AMD Radeon GPU

set_radeon_profile () { # set radeon power profile
    # $1: 0=ac mode, 1=battery mode

    local card level pwr rc1 rc2
    local sdone=0 # 1=radeon present

    if [ ! -d $RADD ]; then
        # No card present --> exit
        echo_debug "pm" "set_radeon_profile($1).no_card"
        return 0
    fi

    for card in /sys/class/drm/card[0-9]/device ; do
        if [ -f $card/power_dpm_state ] && [ -f $card/power_dpm_force_performance_level ]; then
            # Use new radeon dynamic power management method (dpm)
            if [ "$1" = "1" ]; then
                pwr=${RADEON_DPM_STATE_ON_BAT:-}
                level=${RADEON_DPM_PERF_LEVEL_ON_BAT-${DEFAULT_RADEON_DPM_PERF_LEVEL_ON_BAT}}
            else
                pwr=${RADEON_DPM_STATE_ON_AC:-}
                level=${RADEON_DPM_PERF_LEVEL_ON_AC-${DEFAULT_RADEON_DPM_PERF_LEVEL_ON_AC}}
            fi

            if [ -z "$pwr" ]; then
                # do nothing if unconfigured
                echo_debug "pm" "set_radeon_profile($1).not_configured: $card"
                return 0
            fi

            if [ -n "$pwr" ]; then
                write_sysf "$pwr" $card/power_dpm_state; rc1=$?
                write_sysf "$level" $card/power_dpm_force_performance_level; rc2=$?
                echo_debug "pm" "set_radeon_profile($1): $card: state=${pwr}: rc=$rc1; perf=${level}: rc=$rc2"
            fi

            sdone=1

        elif [ -f $card/power_method ] && [ -f $card/power_profile ]; then
            # Use old radeon power profile method
            if [ "$1" = "1" ]; then
                pwr=${RADEON_POWER_PROFILE_ON_BAT-${DEFAULT_RADEON_POWER_PROFILE_ON_BAT}}
            else
                pwr=${RADEON_POWER_PROFILE_ON_AC-${DEFAULT_RADEON_POWER_PROFILE_ON_AC}}
            fi

            if [ -z "$pwr" ]; then
                # do nothing if unconfigured
                echo_debug "pm" "set_radeon_profile($1).not_configured: $card"
                return 0
            fi

            if [ -n "$pwr" ]; then
                write_sysf "profile" $card/power_method; rc1=$?
                write_sysf "$pwr" $card/power_profile; rc2=$?
                echo_debug "pm" "set_radeon_profile($1): $card; method=profile: rc=$rc1; profile=${pwr}: rc=$rc2"
            fi

            sdone=1
        fi
    done

    if [ $sdone -eq 0 ]; then
        echo_debug "pm" "set_radeon_profile($1).not_available"
    fi

    return 0
}
